summaryrefslogtreecommitdiffstats
path: root/storage/heap/hp_create.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
commit3f619478f796eddbba6e39502fe941b285dd97b1 (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/heap/hp_create.c
parentInitial commit. (diff)
downloadmariadb-3f619478f796eddbba6e39502fe941b285dd97b1.tar.xz
mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.zip
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/heap/hp_create.c')
-rw-r--r--storage/heap/hp_create.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/storage/heap/hp_create.c b/storage/heap/hp_create.c
new file mode 100644
index 00000000..935c6f8d
--- /dev/null
+++ b/storage/heap/hp_create.c
@@ -0,0 +1,368 @@
+/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include "heapdef.h"
+
+static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2);
+static void init_block(HP_BLOCK *block,uint reclength,ulong min_records,
+ ulong max_records);
+
+/* Create a heap table */
+
+int heap_create(const char *name, HP_CREATE_INFO *create_info,
+ HP_SHARE **res, my_bool *created_new_share)
+{
+ uint i, j, key_segs, max_length, length;
+ HP_SHARE *share= 0;
+ HA_KEYSEG *keyseg;
+ HP_KEYDEF *keydef= create_info->keydef;
+ uint reclength= create_info->reclength;
+ uint keys= create_info->keys;
+ ulong min_records= create_info->min_records;
+ ulong max_records= create_info->max_records;
+ uint visible_offset;
+ DBUG_ENTER("heap_create");
+
+ if (!create_info->internal_table)
+ {
+ mysql_mutex_lock(&THR_LOCK_heap);
+ share= hp_find_named_heap(name);
+ if (share && share->open_count == 0)
+ {
+ hp_free(share);
+ share= 0;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Creating internal (no named) temporary table"));
+ }
+ *created_new_share= (share == NULL);
+
+ if (!share)
+ {
+ HP_KEYDEF *keyinfo;
+ DBUG_PRINT("info",("Initializing new table"));
+
+ /*
+ We have to store sometimes uchar* del_link in records,
+ so the visible_offset must be least at sizeof(uchar*)
+ */
+ visible_offset= MY_MAX(reclength, sizeof (char*));
+
+ for (i= key_segs= max_length= 0, keyinfo= keydef; i < keys; i++, keyinfo++)
+ {
+ bzero((char*) &keyinfo->block,sizeof(keyinfo->block));
+ bzero((char*) &keyinfo->rb_tree ,sizeof(keyinfo->rb_tree));
+ for (j= length= 0; j < keyinfo->keysegs; j++)
+ {
+ length+= keyinfo->seg[j].length;
+ if (keyinfo->seg[j].null_bit)
+ {
+ length++;
+ if (!(keyinfo->flag & HA_NULL_ARE_EQUAL))
+ keyinfo->flag|= HA_NULL_PART_KEY;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ keyinfo->rb_tree.size_of_element++;
+ }
+ switch (keyinfo->seg[j].type) {
+ case HA_KEYTYPE_SHORT_INT:
+ case HA_KEYTYPE_LONG_INT:
+ case HA_KEYTYPE_FLOAT:
+ case HA_KEYTYPE_DOUBLE:
+ case HA_KEYTYPE_USHORT_INT:
+ case HA_KEYTYPE_ULONG_INT:
+ case HA_KEYTYPE_LONGLONG:
+ case HA_KEYTYPE_ULONGLONG:
+ case HA_KEYTYPE_INT24:
+ case HA_KEYTYPE_UINT24:
+ case HA_KEYTYPE_INT8:
+ keyinfo->seg[j].flag|= HA_SWAP_KEY;
+ break;
+ case HA_KEYTYPE_VARBINARY1:
+ /* Case-insensitiveness is handled in hash_sort */
+ keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
+ /* fall through */
+ case HA_KEYTYPE_VARTEXT1:
+ keyinfo->flag|= HA_VAR_LENGTH_KEY;
+ /*
+ For BTREE algorithm, key length, greater than or equal
+ to 255, is packed on 3 bytes.
+ */
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ length+= size_to_store_key_length(keyinfo->seg[j].length);
+ else
+ length+= 2;
+ /* Save number of bytes used to store length */
+ keyinfo->seg[j].bit_start= 1;
+ break;
+ case HA_KEYTYPE_VARBINARY2:
+ /* Case-insensitiveness is handled in hash_sort */
+ /* fall_through */
+ case HA_KEYTYPE_VARTEXT2:
+ keyinfo->flag|= HA_VAR_LENGTH_KEY;
+ /*
+ For BTREE algorithm, key length, greater than or equal
+ to 255, is packed on 3 bytes.
+ */
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ length+= size_to_store_key_length(keyinfo->seg[j].length);
+ else
+ length+= 2;
+ /* Save number of bytes used to store length */
+ keyinfo->seg[j].bit_start= 2;
+ /*
+ Make future comparison simpler by only having to check for
+ one type
+ */
+ keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
+ break;
+ case HA_KEYTYPE_BIT:
+ /*
+ The odd bits which stored separately (if they are present
+ (bit_pos, bit_length)) are already present in seg[j].length as
+ additional byte.
+ See field.h, function key_length()
+ */
+ break;
+ default:
+ break;
+ }
+ }
+ keyinfo->length= length;
+ length+= keyinfo->rb_tree.size_of_element +
+ ((keyinfo->algorithm == HA_KEY_ALG_BTREE) ? sizeof(uchar*) : 0);
+ if (length > max_length)
+ max_length= length;
+ key_segs+= keyinfo->keysegs;
+ if (keyinfo->algorithm == HA_KEY_ALG_BTREE)
+ {
+ key_segs++; /* additional HA_KEYTYPE_END segment */
+ if (keyinfo->flag & HA_VAR_LENGTH_KEY)
+ keyinfo->get_key_length= hp_rb_var_key_length;
+ else if (keyinfo->flag & HA_NULL_PART_KEY)
+ keyinfo->get_key_length= hp_rb_null_key_length;
+ else
+ keyinfo->get_key_length= hp_rb_key_length;
+ }
+ }
+ if (!(share= (HP_SHARE*) my_malloc(hp_key_memory_HP_SHARE,
+ sizeof(HP_SHARE)+
+ keys*sizeof(HP_KEYDEF)+
+ key_segs*sizeof(HA_KEYSEG),
+ MYF(MY_ZEROFILL |
+ (create_info->internal_table ?
+ MY_THREAD_SPECIFIC : 0)))))
+ goto err;
+ share->keydef= (HP_KEYDEF*) (share + 1);
+ share->key_stat_version= 1;
+ keyseg= (HA_KEYSEG*) (share->keydef + keys);
+ init_block(&share->block, visible_offset + 1, min_records, max_records);
+ /* Fix keys */
+ memcpy(share->keydef, keydef, (size_t) (sizeof(keydef[0]) * keys));
+ for (i= 0, keyinfo= share->keydef; i < keys; i++, keyinfo++)
+ {
+ keyinfo->seg= keyseg;
+ memcpy(keyseg, keydef[i].seg,
+ (size_t) (sizeof(keyseg[0]) * keydef[i].keysegs));
+ keyseg+= keydef[i].keysegs;
+
+ if (keydef[i].algorithm == HA_KEY_ALG_BTREE)
+ {
+ /* additional HA_KEYTYPE_END keyseg */
+ keyseg->type= HA_KEYTYPE_END;
+ keyseg->length= sizeof(uchar*);
+ keyseg->flag= 0;
+ keyseg->null_bit= 0;
+ keyseg++;
+
+ init_tree(&keyinfo->rb_tree, 0, 0, sizeof(uchar*),
+ (qsort_cmp2)keys_compare, NULL, NULL,
+ MYF((create_info->internal_table ? MY_THREAD_SPECIFIC : 0) |
+ MY_TREE_WITH_DELETE));
+ keyinfo->delete_key= hp_rb_delete_key;
+ keyinfo->write_key= hp_rb_write_key;
+ }
+ else
+ {
+ init_block(&keyinfo->block, sizeof(HASH_INFO), min_records,
+ max_records);
+ keyinfo->delete_key= hp_delete_key;
+ keyinfo->write_key= hp_write_key;
+ keyinfo->hash_buckets= 0;
+ }
+ if ((keyinfo->flag & HA_AUTO_KEY) && create_info->with_auto_increment)
+ share->auto_key= i + 1;
+ }
+ share->min_records= min_records;
+ share->max_records= max_records;
+ share->max_table_size= create_info->max_table_size;
+ share->data_length= share->index_length= 0;
+ share->reclength= reclength;
+ share->visible= visible_offset;
+ share->blength= 1;
+ share->keys= keys;
+ share->max_key_length= max_length;
+ share->changed= 0;
+ share->auto_key= create_info->auto_key;
+ share->auto_key_type= create_info->auto_key_type;
+ share->auto_increment= create_info->auto_increment;
+ share->create_time= (long) time((time_t*) 0);
+ share->internal= create_info->internal_table;
+ /* Must be allocated separately for rename to work */
+ if (!(share->name= my_strdup(hp_key_memory_HP_SHARE, name, MYF(0))))
+ {
+ my_free(share);
+ goto err;
+ }
+
+ if (!create_info->internal_table)
+ {
+ thr_lock_init(&share->lock);
+ share->open_list.data= (void*) share;
+ heap_share_list= list_add(heap_share_list,&share->open_list);
+ }
+ else
+ share->delete_on_close= 1;
+ }
+ if (!create_info->internal_table)
+ {
+ if (create_info->pin_share)
+ ++share->open_count;
+ mysql_mutex_unlock(&THR_LOCK_heap);
+ }
+
+ *res= share;
+ DBUG_RETURN(0);
+
+err:
+ if (!create_info->internal_table)
+ mysql_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(1);
+} /* heap_create */
+
+
+static int keys_compare(heap_rb_param *param, uchar *key1, uchar *key2)
+{
+ uint not_used[2];
+ return ha_key_cmp(param->keyseg, key1, key2, param->key_length,
+ param->search_flag, not_used);
+}
+
+static void init_block(HP_BLOCK *block, uint reclength, ulong min_records,
+ ulong max_records)
+{
+ ulong i,recbuffer,records_in_block;
+
+ /*
+ If not min_records and max_records are given, optimize for 1000 rows
+ */
+ if (!min_records)
+ min_records= MY_MIN(1000, max_records);
+ if (!max_records)
+ max_records= MY_MAX(min_records, 1000);
+
+ /*
+ We don't want too few records_in_block as otherwise the overhead of
+ of the HP_PTRS block will be too notable
+ */
+ records_in_block= MY_MAX(1000, min_records);
+ records_in_block= MY_MIN(records_in_block, max_records);
+ /* If big max_records is given, allocate bigger blocks */
+ records_in_block= MY_MAX(records_in_block, max_records / 10);
+ /* We don't want too few blocks per row either */
+ if (records_in_block < 10)
+ records_in_block= 10;
+
+ recbuffer= (uint) (reclength + sizeof(uchar**) - 1) & ~(sizeof(uchar**) - 1);
+ /*
+ Don't allocate more than my_default_record_cache_size per level.
+ The + 1 is there to ensure that we get at least 1 row per level (for
+ the exceptional case of very long rows)
+ */
+ if ((ulonglong) records_in_block*recbuffer >
+ (my_default_record_cache_size-sizeof(HP_PTRS)*HP_MAX_LEVELS))
+ records_in_block= (my_default_record_cache_size - sizeof(HP_PTRS) *
+ HP_MAX_LEVELS) / recbuffer + 1;
+ block->records_in_block= records_in_block;
+ block->recbuffer= recbuffer;
+ block->last_allocated= 0L;
+
+ for (i= 0; i <= HP_MAX_LEVELS; i++)
+ block->level_info[i].records_under_level=
+ (!i ? 1 : i == 1 ? records_in_block :
+ HP_PTRS_IN_NOD * block->level_info[i - 1].records_under_level);
+}
+
+
+static inline void heap_try_free(HP_SHARE *share)
+{
+ DBUG_ENTER("heap_try_free");
+ if (share->open_count == 0)
+ hp_free(share);
+ else
+ {
+ DBUG_PRINT("info", ("Table is still in use. Will be freed on close"));
+ share->delete_on_close= 1;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+int heap_delete_table(const char *name)
+{
+ int result;
+ reg1 HP_SHARE *share;
+ DBUG_ENTER("heap_delete_table");
+
+ mysql_mutex_lock(&THR_LOCK_heap);
+ if ((share= hp_find_named_heap(name)))
+ {
+ heap_try_free(share);
+ result= 0;
+ }
+ else
+ {
+ result= my_errno=ENOENT;
+ DBUG_PRINT("error", ("Could not find table '%s'", name));
+ }
+ mysql_mutex_unlock(&THR_LOCK_heap);
+ DBUG_RETURN(result);
+}
+
+
+void heap_drop_table(HP_INFO *info)
+{
+ DBUG_ENTER("heap_drop_table");
+ mysql_mutex_lock(&THR_LOCK_heap);
+ heap_try_free(info->s);
+ mysql_mutex_unlock(&THR_LOCK_heap);
+ DBUG_VOID_RETURN;
+}
+
+
+void hp_free(HP_SHARE *share)
+{
+ if (!share->internal)
+ {
+ heap_share_list= list_delete(heap_share_list, &share->open_list);
+ thr_lock_delete(&share->lock);
+ }
+ hp_clear(share); /* Remove blocks from memory */
+ my_free(share->name);
+ my_free(share);
+ return;
+}