~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/mf_keycaches.c

  • Committer: Brian Aker
  • Date: 2009-02-21 00:18:15 UTC
  • Revision ID: brian@tangent.org-20090221001815-x20e8h71e984lvs1
Completion (?) of uint conversion.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2003 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/*
 
17
  Handling of multiple key caches
 
18
 
 
19
  The idea is to have a thread safe hash on the table name,
 
20
  with a default key cache value that is returned if the table name is not in
 
21
  the cache.
 
22
*/
 
23
 
 
24
#include <drizzled/global.h>
 
25
#include <mysys/mysys_err.h>
 
26
#include <mysys/my_sys.h>
 
27
#include "keycache.h"
 
28
#include <mysys/hash.h>
 
29
#include <mystrings/m_string.h>
 
30
 
 
31
/*****************************************************************************
 
32
  General functions to handle SAFE_HASH objects.
 
33
 
 
34
  A SAFE_HASH object is used to store the hash, the mutex and default value
 
35
  needed by the rest of the key cache code.
 
36
  This is a separate struct to make it easy to later reuse the code for other
 
37
  purposes
 
38
 
 
39
  All entries are linked in a list to allow us to traverse all elements
 
40
  and delete selected ones. (HASH doesn't allow any easy ways to do this).
 
41
*****************************************************************************/
 
42
 
 
43
/*
 
44
  Struct to store a key and pointer to object
 
45
*/
 
46
 
 
47
typedef struct st_safe_hash_entry
 
48
{
 
49
  unsigned char *key;
 
50
  uint32_t length;
 
51
  unsigned char *data;
 
52
  struct st_safe_hash_entry *next, **prev;
 
53
} SAFE_HASH_ENTRY;
 
54
 
 
55
 
 
56
typedef struct st_safe_hash_with_default
 
57
{
 
58
  pthread_rwlock_t mutex;
 
59
  HASH hash;
 
60
  unsigned char *default_value;
 
61
  SAFE_HASH_ENTRY *root;
 
62
} SAFE_HASH;
 
63
 
 
64
 
 
65
/*
 
66
  Free a SAFE_HASH_ENTRY
 
67
 
 
68
  This function is called by the hash object on delete
 
69
*/
 
70
 
 
71
static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
 
72
{
 
73
  free((unsigned char*) entry);
 
74
  return;
 
75
}
 
76
 
 
77
 
 
78
/* Get key and length for a SAFE_HASH_ENTRY */
 
79
 
 
80
static unsigned char *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length,
 
81
                                  bool not_used)
 
82
{
 
83
  (void)not_used;
 
84
  *length=entry->length;
 
85
  return (unsigned char*) entry->key;
 
86
}
 
87
 
 
88
 
 
89
/*
 
90
  Init a SAFE_HASH object
 
91
 
 
92
  SYNOPSIS
 
93
    safe_hash_init()
 
94
    hash                safe_hash handler
 
95
    elements            Expected max number of elements
 
96
    default_value       default value
 
97
 
 
98
  NOTES
 
99
    In case of error we set hash->default_value to 0 to allow one to call
 
100
    safe_hash_free on an object that couldn't be initialized.
 
101
 
 
102
  RETURN
 
103
    0  ok
 
104
    1  error
 
105
*/
 
106
 
 
107
static bool safe_hash_init(SAFE_HASH *hash, uint32_t elements,
 
108
                              unsigned char *default_value)
 
109
{
 
110
  if (hash_init(&hash->hash, &my_charset_bin, elements,
 
111
                0, 0, (hash_get_key) safe_hash_entry_get,
 
112
                (void (*)(void*)) safe_hash_entry_free, 0))
 
113
  {
 
114
    hash->default_value= 0;
 
115
    return(1);
 
116
  }
 
117
  pthread_rwlock_init(&hash->mutex, 0);
 
118
  hash->default_value= default_value;
 
119
  hash->root= 0;
 
120
  return(0);
 
121
}
 
122
 
 
123
 
 
124
/*
 
125
  Free a SAFE_HASH object
 
126
 
 
127
  NOTES
 
128
    This is safe to call on any object that has been sent to safe_hash_init()
 
129
*/
 
130
 
 
131
static void safe_hash_free(SAFE_HASH *hash)
 
132
{
 
133
  /*
 
134
    Test if safe_hash_init succeeded. This will also guard us against multiple
 
135
    free calls.
 
136
  */
 
137
  if (hash->default_value)
 
138
  {
 
139
    hash_free(&hash->hash);
 
140
    pthread_rwlock_destroy(&hash->mutex);
 
141
    hash->default_value=0;
 
142
  }
 
143
}
 
144
 
 
145
/*
 
146
  Return the value stored for a key or default value if no key
 
147
*/
 
148
 
 
149
static unsigned char *safe_hash_search(SAFE_HASH *hash, const unsigned char *key, uint32_t length)
 
150
{
 
151
  unsigned char *result;
 
152
  pthread_rwlock_rdlock(&hash->mutex);
 
153
  result= hash_search(&hash->hash, key, length);
 
154
  pthread_rwlock_unlock(&hash->mutex);
 
155
  if (!result)
 
156
    result= hash->default_value;
 
157
  else
 
158
    result= ((SAFE_HASH_ENTRY*) result)->data;
 
159
  return(result);
 
160
}
 
161
 
 
162
 
 
163
/*
 
164
  Associate a key with some data
 
165
 
 
166
  SYONOPSIS
 
167
    safe_hash_set()
 
168
    hash                        Hash handle
 
169
    key                         key (path to table etc..)
 
170
    length                      Length of key
 
171
    data                        data to to associate with the data
 
172
 
 
173
  NOTES
 
174
    This can be used both to insert a new entry and change an existing
 
175
    entry.
 
176
    If one associates a key with the default key cache, the key is deleted
 
177
 
 
178
  RETURN
 
179
    0  ok
 
180
    1  error (Can only be EOM). In this case my_message() is called.
 
181
*/
 
182
 
 
183
static bool safe_hash_set(SAFE_HASH *hash, const unsigned char *key, uint32_t length,
 
184
                             unsigned char *data)
 
185
{
 
186
  SAFE_HASH_ENTRY *entry;
 
187
  bool error= 0;
 
188
 
 
189
  pthread_rwlock_wrlock(&hash->mutex);
 
190
  entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length);
 
191
 
 
192
  if (data == hash->default_value)
 
193
  {
 
194
    /*
 
195
      The key is to be associated with the default entry. In this case
 
196
      we can just delete the entry (if it existed) from the hash as a
 
197
      search will return the default entry
 
198
    */
 
199
    if (!entry)                                 /* nothing to do */
 
200
      goto end;
 
201
    /* unlink entry from list */
 
202
    if ((*entry->prev= entry->next))
 
203
      entry->next->prev= entry->prev;
 
204
    hash_delete(&hash->hash, (unsigned char*) entry);
 
205
    goto end;
 
206
  }
 
207
  if (entry)
 
208
  {
 
209
    /* Entry existed;  Just change the pointer to point at the new data */
 
210
    entry->data= data;
 
211
  }
 
212
  else
 
213
  {
 
214
    if (!(entry= (SAFE_HASH_ENTRY *) malloc(sizeof(*entry) + length)))
 
215
    {
 
216
      error= 1;
 
217
      goto end;
 
218
    }
 
219
    entry->key= (unsigned char*) (entry +1);
 
220
    memcpy(entry->key, key, length);
 
221
    entry->length= length;
 
222
    entry->data= data;
 
223
    /* Link entry to list */
 
224
    if ((entry->next= hash->root))
 
225
      entry->next->prev= &entry->next;
 
226
    entry->prev= &hash->root;
 
227
    hash->root= entry;
 
228
    if (my_hash_insert(&hash->hash, (unsigned char*) entry))
 
229
    {
 
230
      /* This can only happen if hash got out of memory */
 
231
      free((char*) entry);
 
232
      error= 1;
 
233
      goto end;
 
234
    }
 
235
  }
 
236
 
 
237
end:
 
238
  pthread_rwlock_unlock(&hash->mutex);
 
239
  return(error);
 
240
}
 
241
 
 
242
 
 
243
/*
 
244
  Change all entres with one data value to another data value
 
245
 
 
246
  SYONOPSIS
 
247
    safe_hash_change()
 
248
    hash                        Hash handle
 
249
    old_data                    Old data
 
250
    new_data                    Change all 'old_data' to this
 
251
 
 
252
  NOTES
 
253
    We use the linked list to traverse all elements in the hash as
 
254
    this allows us to delete elements in the case where 'new_data' is the
 
255
    default value.
 
256
*/
 
257
 
 
258
static void safe_hash_change(SAFE_HASH *hash, unsigned char *old_data, unsigned char *new_data)
 
259
{
 
260
  SAFE_HASH_ENTRY *entry, *next;
 
261
 
 
262
  pthread_rwlock_wrlock(&hash->mutex);
 
263
 
 
264
  for (entry= hash->root ; entry ; entry= next)
 
265
  {
 
266
    next= entry->next;
 
267
    if (entry->data == old_data)
 
268
    {
 
269
      if (new_data == hash->default_value)
 
270
      {
 
271
        if ((*entry->prev= entry->next))
 
272
          entry->next->prev= entry->prev;
 
273
        hash_delete(&hash->hash, (unsigned char*) entry);
 
274
      }
 
275
      else
 
276
        entry->data= new_data;
 
277
    }
 
278
  }
 
279
 
 
280
  pthread_rwlock_unlock(&hash->mutex);
 
281
  return;
 
282
}
 
283
 
 
284
 
 
285
/*****************************************************************************
 
286
  Functions to handle the key cache objects
 
287
*****************************************************************************/
 
288
 
 
289
/* Variable to store all key cache objects */
 
290
static SAFE_HASH key_cache_hash;
 
291
 
 
292
 
 
293
bool multi_keycache_init(void)
 
294
{
 
295
  return safe_hash_init(&key_cache_hash, 16, (unsigned char*) dflt_key_cache);
 
296
}
 
297
 
 
298
 
 
299
void multi_keycache_free(void)
 
300
{
 
301
  safe_hash_free(&key_cache_hash);
 
302
}
 
303
 
 
304
/*
 
305
  Get a key cache to be used for a specific table.
 
306
 
 
307
  SYNOPSIS
 
308
    multi_key_cache_search()
 
309
    key                         key to find (usually table path)
 
310
    uint32_t length                     Length of key.
 
311
 
 
312
  NOTES
 
313
    This function is coded in such a way that we will return the
 
314
    default key cache even if one never called multi_keycache_init.
 
315
    This will ensure that it works with old MyISAM clients.
 
316
 
 
317
  RETURN
 
318
    key cache to use
 
319
*/
 
320
 
 
321
KEY_CACHE *multi_key_cache_search(unsigned char *key, uint32_t length)
 
322
{
 
323
  if (!key_cache_hash.hash.records)
 
324
    return dflt_key_cache;
 
325
  return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
 
326
}
 
327
 
 
328
 
 
329
/*
 
330
  Assosiate a key cache with a key
 
331
 
 
332
 
 
333
  SYONOPSIS
 
334
    multi_key_cache_set()
 
335
    key                         key (path to table etc..)
 
336
    length                      Length of key
 
337
    key_cache                   cache to assococite with the table
 
338
 
 
339
  NOTES
 
340
    This can be used both to insert a new entry and change an existing
 
341
    entry
 
342
*/
 
343
 
 
344
 
 
345
bool multi_key_cache_set(const unsigned char *key, uint32_t length,
 
346
                            KEY_CACHE *key_cache)
 
347
{
 
348
  return safe_hash_set(&key_cache_hash, key, length, (unsigned char*) key_cache);
 
349
}
 
350
 
 
351
 
 
352
void multi_key_cache_change(KEY_CACHE *old_data,
 
353
                            KEY_CACHE *new_data)
 
354
{
 
355
  safe_hash_change(&key_cache_hash, (unsigned char*) old_data, (unsigned char*) new_data);
 
356
}