1/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
2 Copyright (c) 2010, 2017, MariaDB Corporation.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17#include "heapdef.h"
18
19static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2);
20static void init_block(HP_BLOCK *block,uint reclength,ulong min_records,
21 ulong max_records);
22
23/* Create a heap table */
24
25int heap_create(const char *name, HP_CREATE_INFO *create_info,
26 HP_SHARE **res, my_bool *created_new_share)
27{
28 uint i, j, key_segs, max_length, length;
29 HP_SHARE *share= 0;
30 HA_KEYSEG *keyseg;
31 HP_KEYDEF *keydef= create_info->keydef;
32 uint reclength= create_info->reclength;
33 uint keys= create_info->keys;
34 ulong min_records= create_info->min_records;
35 ulong max_records= create_info->max_records;
36 uint visible_offset;
37 DBUG_ENTER("heap_create");
38
39 if (!create_info->internal_table)
40 {
41 mysql_mutex_lock(&THR_LOCK_heap);
42 share= hp_find_named_heap(name);
43 if (share && share->open_count == 0)
44 {
45 hp_free(share);
46 share= 0;
47 }
48 }
49 else
50 {
51 DBUG_PRINT("info", ("Creating internal (no named) temporary table"));
52 }
53 *created_new_share= (share == NULL);
54
55 if (!share)
56 {
57 HP_KEYDEF *keyinfo;
58 DBUG_PRINT("info",("Initializing new table"));
59
60 /*
61 We have to store sometimes uchar* del_link in records,
62 so the visible_offset must be least at sizeof(uchar*)
63 */
64 visible_offset= MY_MAX(reclength, sizeof (char*));
65
66 for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
67 {
68 bzero((char*) &keyinfo->block,sizeof(keyinfo->block));
69 bzero((char*) &keyinfo->rb_tree ,sizeof(keyinfo->rb_tree));
70 for (j= length= 0; j < keyinfo->keysegs; j++)
71 {
72 length+= keyinfo->seg[j].length;
73 if (keyinfo->seg[j].null_bit)
74 {
75 length++;
76 if (!(keyinfo->flag & HA_NULL_ARE_EQUAL))
77 keyinfo->flag|= HA_NULL_PART_KEY;
78 if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
79 keyinfo->rb_tree.size_of_element++;
80 }
81 switch (keyinfo->seg[j].type) {
82 case HA_KEYTYPE_SHORT_INT:
83 case HA_KEYTYPE_LONG_INT:
84 case HA_KEYTYPE_FLOAT:
85 case HA_KEYTYPE_DOUBLE:
86 case HA_KEYTYPE_USHORT_INT:
87 case HA_KEYTYPE_ULONG_INT:
88 case HA_KEYTYPE_LONGLONG:
89 case HA_KEYTYPE_ULONGLONG:
90 case HA_KEYTYPE_INT24:
91 case HA_KEYTYPE_UINT24:
92 case HA_KEYTYPE_INT8:
93 keyinfo->seg[j].flag|= HA_SWAP_KEY;
94 break;
95 case HA_KEYTYPE_VARBINARY1:
96 /* Case-insensitiveness is handled in coll->hash_sort */
97 keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
98 /* fall through */
99 case HA_KEYTYPE_VARTEXT1:
100 keyinfo->flag|= HA_VAR_LENGTH_KEY;
101 length+= 2;
102 /* Save number of bytes used to store length */
103 keyinfo->seg[j].bit_start= 1;
104 break;
105 case HA_KEYTYPE_VARBINARY2:
106 /* Case-insensitiveness is handled in coll->hash_sort */
107 /* fall_through */
108 case HA_KEYTYPE_VARTEXT2:
109 keyinfo->flag|= HA_VAR_LENGTH_KEY;
110 length+= 2;
111 /* Save number of bytes used to store length */
112 keyinfo->seg[j].bit_start= 2;
113 /*
114 Make future comparison simpler by only having to check for
115 one type
116 */
117 keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
118 break;
119 case HA_KEYTYPE_BIT:
120 /*
121 The odd bits which stored separately (if they are present
122 (bit_pos, bit_length)) are already present in seg[j].length as
123 additional byte.
124 See field.h, function key_length()
125 */
126 break;
127 default:
128 break;
129 }
130 }
131 keyinfo->length= length;
132 length+= keyinfo->rb_tree.size_of_element +
133 ((keyinfo->algorithm == HA_KEY_ALG_BTREE) ? sizeof(uchar*) : 0);
134 if (length > max_length)
135 max_length= length;
136 key_segs+= keyinfo->keysegs;
137 if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
138 {
139 key_segs++; /* additional HA_KEYTYPE_END segment */
140 if (keyinfo->flag & HA_VAR_LENGTH_KEY)
141 keyinfo->get_key_length= hp_rb_var_key_length;
142 else if (keyinfo->flag & HA_NULL_PART_KEY)
143 keyinfo->get_key_length= hp_rb_null_key_length;
144 else
145 keyinfo->get_key_length= hp_rb_key_length;
146 }
147 }
148 if (!(share= (HP_SHARE*) my_malloc((uint) sizeof(HP_SHARE)+
149 keys*sizeof(HP_KEYDEF)+
150 key_segs*sizeof(HA_KEYSEG),
151 MYF(MY_ZEROFILL |
152 (create_info->internal_table ?
153 MY_THREAD_SPECIFIC : 0)))))
154 goto err;
155 share->keydef= (HP_KEYDEF*) (share + 1);
156 share->key_stat_version= 1;
157 keyseg= (HA_KEYSEG*) (share->keydef + keys);
158 init_block(&share->block, visible_offset + 1, min_records, max_records);
159 /* Fix keys */
160 memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
161 for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
162 {
163 keyinfo->seg= keyseg;
164 memcpy(keyseg, keydef[i].seg,
165 (size_t) (sizeof(keyseg[0]) * keydef[i].keysegs));
166 keyseg+= keydef[i].keysegs;
167
168 if (keydef[i].algorithm == HA_KEY_ALG_BTREE)
169 {
170 /* additional HA_KEYTYPE_END keyseg */
171 keyseg->type= HA_KEYTYPE_END;
172 keyseg->length= sizeof(uchar*);
173 keyseg->flag= 0;
174 keyseg->null_bit= 0;
175 keyseg++;
176
177 init_tree(&keyinfo->rb_tree, 0, 0, sizeof(uchar*),
178 (qsort_cmp2)keys_compare, NULL, NULL,
179 MYF((create_info->internal_table ? MY_THREAD_SPECIFIC : 0) |
180 MY_TREE_WITH_DELETE));
181 keyinfo->delete_key= hp_rb_delete_key;
182 keyinfo->write_key= hp_rb_write_key;
183 }
184 else
185 {
186 init_block(&keyinfo->block, sizeof(HASH_INFO), min_records,
187 max_records);
188 keyinfo->delete_key= hp_delete_key;
189 keyinfo->write_key= hp_write_key;
190 keyinfo->hash_buckets= 0;
191 }
192 if ((keyinfo->flag & HA_AUTO_KEY) && create_info->with_auto_increment)
193 share->auto_key= i + 1;
194 }
195 share->min_records= min_records;
196 share->max_records= max_records;
197 share->max_table_size= create_info->max_table_size;
198 share->data_length= share->index_length= 0;
199 share->reclength= reclength;
200 share->visible= visible_offset;
201 share->blength= 1;
202 share->keys= keys;
203 share->max_key_length= max_length;
204 share->changed= 0;
205 share->auto_key= create_info->auto_key;
206 share->auto_key_type= create_info->auto_key_type;
207 share->auto_increment= create_info->auto_increment;
208 share->create_time= (long) time((time_t*) 0);
209 share->internal= create_info->internal_table;
210 /* Must be allocated separately for rename to work */
211 if (!(share->name= my_strdup(name,MYF(0))))
212 {
213 my_free(share);
214 goto err;
215 }
216
217 if (!create_info->internal_table)
218 {
219 thr_lock_init(&share->lock);
220 mysql_mutex_init(hp_key_mutex_HP_SHARE_intern_lock,
221 &share->intern_lock, MY_MUTEX_INIT_FAST);
222 share->open_list.data= (void*) share;
223 heap_share_list= list_add(heap_share_list,&share->open_list);
224 }
225 else
226 share->delete_on_close= 1;
227 }
228 if (!create_info->internal_table)
229 {
230 if (create_info->pin_share)
231 ++share->open_count;
232 mysql_mutex_unlock(&THR_LOCK_heap);
233 }
234
235 *res= share;
236 DBUG_RETURN(0);
237
238err:
239 if (!create_info->internal_table)
240 mysql_mutex_unlock(&THR_LOCK_heap);
241 DBUG_RETURN(1);
242} /* heap_create */
243
244
245static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2)
246{
247 uint not_used[2];
248 return ha_key_cmp(param->keyseg, key1, key2, param->key_length,
249 param->search_flag, not_used);
250}
251
252static void init_block(HP_BLOCK *block, uint reclength, ulong min_records,
253 ulong max_records)
254{
255 ulong i,recbuffer,records_in_block;
256
257 /*
258 If not min_records and max_records are given, optimize for 1000 rows
259 */
260 if (!min_records)
261 min_records= MY_MIN(1000, max_records);
262 if (!max_records)
263 max_records= MY_MAX(min_records, 1000);
264
265 /*
266 We don't want too few records_in_block as otherwise the overhead of
267 of the HP_PTRS block will be too notable
268 */
269 records_in_block= MY_MAX(1000, min_records);
270 records_in_block= MY_MIN(records_in_block, max_records);
271 /* If big max_records is given, allocate bigger blocks */
272 records_in_block= MY_MAX(records_in_block, max_records / 10);
273 /* We don't want too few blocks per row either */
274 if (records_in_block < 10)
275 records_in_block= 10;
276
277 recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1);
278 /*
279 Don't allocate more than my_default_record_cache_size per level.
280 The + 1 is there to ensure that we get at least 1 row per level (for
281 the exceptional case of very long rows)
282 */
283 if ((ulonglong) records_in_block*recbuffer >
284 (my_default_record_cache_size-sizeof(HP_PTRS)*HP_MAX_LEVELS))
285 records_in_block= (my_default_record_cache_size - sizeof(HP_PTRS) *
286 HP_MAX_LEVELS) / recbuffer + 1;
287 block->records_in_block= records_in_block;
288 block->recbuffer= recbuffer;
289 block->last_allocated= 0L;
290
291 for (i= 0; i <= HP_MAX_LEVELS; i++)
292 block->level_info[i].records_under_level=
293 (!i ? 1 : i == 1 ? records_in_block :
294 HP_PTRS_IN_NOD * block->level_info[i - 1].records_under_level);
295}
296
297
298static inline void heap_try_free(HP_SHARE *share)
299{
300 DBUG_ENTER("heap_try_free");
301 if (share->open_count == 0)
302 hp_free(share);
303 else
304 {
305 DBUG_PRINT("info", ("Table is still in use. Will be freed on close"));
306 share->delete_on_close= 1;
307 }
308 DBUG_VOID_RETURN;
309}
310
311
312int heap_delete_table(const char *name)
313{
314 int result;
315 reg1 HP_SHARE *share;
316 DBUG_ENTER("heap_delete_table");
317
318 mysql_mutex_lock(&THR_LOCK_heap);
319 if ((share= hp_find_named_heap(name)))
320 {
321 heap_try_free(share);
322 result= 0;
323 }
324 else
325 {
326 result= my_errno=ENOENT;
327 DBUG_PRINT("error", ("Could not find table '%s'", name));
328 }
329 mysql_mutex_unlock(&THR_LOCK_heap);
330 DBUG_RETURN(result);
331}
332
333
334void heap_drop_table(HP_INFO *info)
335{
336 DBUG_ENTER("heap_drop_table");
337 mysql_mutex_lock(&THR_LOCK_heap);
338 heap_try_free(info->s);
339 mysql_mutex_unlock(&THR_LOCK_heap);
340 DBUG_VOID_RETURN;
341}
342
343
344void hp_free(HP_SHARE *share)
345{
346 if (!share->internal)
347 {
348 heap_share_list= list_delete(heap_share_list, &share->open_list);
349 thr_lock_delete(&share->lock);
350 mysql_mutex_destroy(&share->intern_lock);
351 }
352 hp_clear(share); /* Remove blocks from memory */
353 my_free(share->name);
354 my_free(share);
355 return;
356}
357