~drizzle-trunk/drizzle/development

1 by brian
clean slate
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
53.2.37 by Monty Taylor
Moved keycache code to MyISAM.
24
#include "my_global.h"
25
#include "mysys_err.h"
26
#include <my_sys.h>
1 by brian
clean slate
27
#include <keycache.h>
28
#include <hash.h>
29
#include <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
  uchar *key;
50
  uint length;
51
  uchar *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
  rw_lock_t mutex;
59
  HASH hash;
60
  uchar *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
  DBUG_ENTER("free_assign_entry");
74
  my_free((uchar*) entry, MYF(0));
75
  DBUG_VOID_RETURN;
76
}
77
78
79
/* Get key and length for a SAFE_HASH_ENTRY */
80
81
static uchar *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length,
82
                                  my_bool not_used __attribute__((unused)))
83
{
84
  *length=entry->length;
85
  return (uchar*) 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 my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
108
			      uchar *default_value)
109
{
110
  DBUG_ENTER("safe_hash");
111
  if (hash_init(&hash->hash, &my_charset_bin, elements,
112
		0, 0, (hash_get_key) safe_hash_entry_get,
113
		(void (*)(void*)) safe_hash_entry_free, 0))
114
  {
115
    hash->default_value= 0;
116
    DBUG_RETURN(1);
117
  }
118
  my_rwlock_init(&hash->mutex, 0);
119
  hash->default_value= default_value;
120
  hash->root= 0;
121
  DBUG_RETURN(0);
122
}
123
124
125
/*
126
  Free a SAFE_HASH object
127
128
  NOTES
129
    This is safe to call on any object that has been sent to safe_hash_init()
130
*/
131
132
static void safe_hash_free(SAFE_HASH *hash)
133
{
134
  /*
135
    Test if safe_hash_init succeeded. This will also guard us against multiple
136
    free calls.
137
  */
138
  if (hash->default_value)
139
  {
140
    hash_free(&hash->hash);
141
    rwlock_destroy(&hash->mutex);
142
    hash->default_value=0;
143
  }
144
}
145
146
/*
147
  Return the value stored for a key or default value if no key
148
*/
149
150
static uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length)
151
{
152
  uchar *result;
153
  DBUG_ENTER("safe_hash_search");
154
  rw_rdlock(&hash->mutex);
155
  result= hash_search(&hash->hash, key, length);
156
  rw_unlock(&hash->mutex);
157
  if (!result)
158
    result= hash->default_value;
159
  else
160
    result= ((SAFE_HASH_ENTRY*) result)->data;
161
  DBUG_PRINT("exit",("data: 0x%lx", (long) result));
162
  DBUG_RETURN(result);
163
}
164
165
166
/*
167
  Associate a key with some data
168
169
  SYONOPSIS
170
    safe_hash_set()
171
    hash			Hash handle
172
    key				key (path to table etc..)
173
    length			Length of key
174
    data			data to to associate with the data
175
176
  NOTES
177
    This can be used both to insert a new entry and change an existing
178
    entry.
179
    If one associates a key with the default key cache, the key is deleted
180
181
  RETURN
182
    0  ok
183
    1  error (Can only be EOM). In this case my_message() is called.
184
*/
185
186
static my_bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length,
187
			     uchar *data)
188
{
189
  SAFE_HASH_ENTRY *entry;
190
  my_bool error= 0;
191
  DBUG_ENTER("safe_hash_set");
192
  DBUG_PRINT("enter",("key: %.*s  data: 0x%lx", length, key, (long) data));
193
194
  rw_wrlock(&hash->mutex);
195
  entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length);
196
197
  if (data == hash->default_value)
198
  {
199
    /*
200
      The key is to be associated with the default entry. In this case
201
      we can just delete the entry (if it existed) from the hash as a
202
      search will return the default entry
203
    */
204
    if (!entry)					/* nothing to do */
205
      goto end;
206
    /* unlink entry from list */
207
    if ((*entry->prev= entry->next))
208
      entry->next->prev= entry->prev;
209
    hash_delete(&hash->hash, (uchar*) entry);
210
    goto end;
211
  }
212
  if (entry)
213
  {
214
    /* Entry existed;  Just change the pointer to point at the new data */
215
    entry->data= data;
216
  }
217
  else
218
  {
219
    if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length,
220
					       MYF(MY_WME))))
221
    {
222
      error= 1;
223
      goto end;
224
    }
225
    entry->key= (uchar*) (entry +1);
226
    memcpy((char*) entry->key, (char*) key, length);
227
    entry->length= length;
228
    entry->data= data;
229
    /* Link entry to list */
230
    if ((entry->next= hash->root))
231
      entry->next->prev= &entry->next;
232
    entry->prev= &hash->root;
233
    hash->root= entry;
234
    if (my_hash_insert(&hash->hash, (uchar*) entry))
235
    {
236
      /* This can only happen if hash got out of memory */
237
      my_free((char*) entry, MYF(0));
238
      error= 1;
239
      goto end;
240
    }
241
  }
242
243
end:
244
  rw_unlock(&hash->mutex);
245
  DBUG_RETURN(error);
246
}
247
248
249
/*
250
  Change all entres with one data value to another data value
251
252
  SYONOPSIS
253
    safe_hash_change()
254
    hash			Hash handle
255
    old_data			Old data
256
    new_data			Change all 'old_data' to this
257
258
  NOTES
259
    We use the linked list to traverse all elements in the hash as
260
    this allows us to delete elements in the case where 'new_data' is the
261
    default value.
262
*/
263
264
static void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data)
265
{
266
  SAFE_HASH_ENTRY *entry, *next;
267
  DBUG_ENTER("safe_hash_set");
268
269
  rw_wrlock(&hash->mutex);
270
271
  for (entry= hash->root ; entry ; entry= next)
272
  {
273
    next= entry->next;
274
    if (entry->data == old_data)
275
    {
276
      if (new_data == hash->default_value)
277
      {
278
        if ((*entry->prev= entry->next))
279
          entry->next->prev= entry->prev;
280
	hash_delete(&hash->hash, (uchar*) entry);
281
      }
282
      else
283
	entry->data= new_data;
284
    }
285
  }
286
287
  rw_unlock(&hash->mutex);
288
  DBUG_VOID_RETURN;
289
}
290
291
292
/*****************************************************************************
293
  Functions to handle the key cache objects
294
*****************************************************************************/
295
296
/* Variable to store all key cache objects */
297
static SAFE_HASH key_cache_hash;
298
299
300
my_bool multi_keycache_init(void)
301
{
302
  return safe_hash_init(&key_cache_hash, 16, (uchar*) dflt_key_cache);
303
}
304
305
306
void multi_keycache_free(void)
307
{
308
  safe_hash_free(&key_cache_hash);
309
}
310
311
/*
312
  Get a key cache to be used for a specific table.
313
314
  SYNOPSIS
315
    multi_key_cache_search()
316
    key				key to find (usually table path)
317
    uint length			Length of key.
318
319
  NOTES
320
    This function is coded in such a way that we will return the
321
    default key cache even if one never called multi_keycache_init.
322
    This will ensure that it works with old MyISAM clients.
323
324
  RETURN
325
    key cache to use
326
*/
327
328
KEY_CACHE *multi_key_cache_search(uchar *key, uint length)
329
{
330
  if (!key_cache_hash.hash.records)
331
    return dflt_key_cache;
332
  return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
333
}
334
335
336
/*
337
  Assosiate a key cache with a key
338
339
340
  SYONOPSIS
341
    multi_key_cache_set()
342
    key				key (path to table etc..)
343
    length			Length of key
344
    key_cache			cache to assococite with the table
345
346
  NOTES
347
    This can be used both to insert a new entry and change an existing
348
    entry
349
*/
350
351
352
my_bool multi_key_cache_set(const uchar *key, uint length,
353
			    KEY_CACHE *key_cache)
354
{
355
  return safe_hash_set(&key_cache_hash, key, length, (uchar*) key_cache);
356
}
357
358
359
void multi_key_cache_change(KEY_CACHE *old_data,
360
			    KEY_CACHE *new_data)
361
{
362
  safe_hash_change(&key_cache_hash, (uchar*) old_data, (uchar*) new_data);
363
}