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 | |
19 | static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2); |
20 | static void init_block(HP_BLOCK *block,uint reclength,ulong min_records, |
21 | ulong max_records); |
22 | |
23 | /* Create a heap table */ |
24 | |
25 | int 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 | |
238 | err: |
239 | if (!create_info->internal_table) |
240 | mysql_mutex_unlock(&THR_LOCK_heap); |
241 | DBUG_RETURN(1); |
242 | } /* heap_create */ |
243 | |
244 | |
245 | static 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 | |
252 | static 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 | |
298 | static 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 | |
312 | int 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 | |
334 | void 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 | |
344 | void 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 | |