| 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 | |