diff options
author | Sergei Petrunia <sergey@mariadb.com> | 2023-05-03 15:15:37 +0300 |
---|---|---|
committer | Sergei Petrunia <sergey@mariadb.com> | 2023-05-09 10:12:27 +0300 |
commit | b3edbf25a1f3bb4c8d7e9824096fc0538c04a977 (patch) | |
tree | 03ec8f43b0a2a5383ac50d1baeeb74c12632df04 | |
parent | 63df43a0e830c6b54178574e4f72f21d19ed765e (diff) | |
download | mariadb-git-b3edbf25a1f3bb4c8d7e9824096fc0538c04a977.tar.gz |
MDEV-31022: SIGSEGV in maria_create from create_internal_tmp_tablebb-11.0-MDEV-31022-variant4
The code in create_internal_tmp_table() didn't take into account that
now temporary (derived) tables may have multiple indexes:
- one index due to duplicate removal
= In this example created by conversion of big-IN(...) into subquery
= this index might be converted into a "unique constraint" if the key
length is too large.
- one index added by derived_with_keys optimization.
Make create_internal_tmp_table() handle multiple indexes.
Before this patch, use of a unique constraint was indicated in
TABLE_SHARE::uniques. This was ok as unique constraint was the only index
in the table. Now it's no longer the case so TABLE_SHARE::uniques is removed
and replaced with an in-memory-only flag HA_UNIQUE_HASH.
This patch is based on Monty's patch.
Co-Author: Monty <monty@mariadb.org>
-rw-r--r-- | include/my_base.h | 11 | ||||
-rw-r--r-- | mysql-test/main/derived.result | 28 | ||||
-rw-r--r-- | mysql-test/main/derived.test | 24 | ||||
-rw-r--r-- | sql/item_subselect.cc | 2 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 6 | ||||
-rw-r--r-- | sql/sql_select.cc | 212 | ||||
-rw-r--r-- | sql/sql_show.cc | 1 | ||||
-rw-r--r-- | sql/table.h | 13 | ||||
-rw-r--r-- | storage/maria/ha_maria.cc | 3 |
9 files changed, 195 insertions, 105 deletions
diff --git a/include/my_base.h b/include/my_base.h index 22f1ea0a5df..05b3b56359e 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -104,7 +104,8 @@ enum ha_key_alg { HA_KEY_ALG_RTREE= 2, /* R-tree, for spatial searches */ HA_KEY_ALG_HASH= 3, /* HASH keys (HEAP tables) */ HA_KEY_ALG_FULLTEXT= 4, /* FULLTEXT (MyISAM tables) */ - HA_KEY_ALG_LONG_HASH= 5 /* long BLOB keys */ + HA_KEY_ALG_LONG_HASH= 5, /* long BLOB keys */ + HA_KEY_ALG_UNIQUE_HASH= 6 /* Internal UNIQUE hash (Aria) */ }; /* Storage media types */ @@ -276,11 +277,17 @@ enum ha_base_keytype { #define HA_SPATIAL 1024U /* For spatial search */ #define HA_NULL_ARE_EQUAL 2048U /* NULL in key are cmp as equal */ #define HA_GENERATED_KEY 8192U /* Automatically generated key */ +/* + Part of unique hash key. Used only for temporary (work) tables so is not + written to .frm files. +*/ +#define HA_UNIQUE_HASH 262144U /* The combination of the above can be used for key type comparison. */ #define HA_KEYFLAG_MASK (HA_NOSAME | HA_AUTO_KEY | \ HA_FULLTEXT | HA_UNIQUE_CHECK | \ - HA_SPATIAL | HA_NULL_ARE_EQUAL | HA_GENERATED_KEY) + HA_SPATIAL | HA_NULL_ARE_EQUAL | HA_GENERATED_KEY | \ + HA_UNIQUE_HASH) /* Key contains partial segments. diff --git a/mysql-test/main/derived.result b/mysql-test/main/derived.result index e15be3aecc4..091ca13d579 100644 --- a/mysql-test/main/derived.result +++ b/mysql-test/main/derived.result @@ -1499,5 +1499,33 @@ a 2 drop table t1; # +# MDEV-31022: SIGSEGV in maria_create from create_internal_tmp_table +# keydef incorrectly allocated on the stack in create_internal_tmp_table() +# +CREATE TABLE t1 (c CHAR(1) NULL) ENGINE=MyISAM; +INSERT INTO t1 (c) VALUES (1); +SET statement +optimizer_where_cost=1, +big_tables=1, +in_predicate_conversion_threshold=2 +FOR +SELECT * FROM t1 WHERE c IN ('',''); +c +Warnings: +Warning 1287 '@@big_tables' is deprecated and will be removed in a future release +Warning 1287 '@@big_tables' is deprecated and will be removed in a future release +SET statement +optimizer_where_cost=1, +big_tables=1, +in_predicate_conversion_threshold=2, +sql_mode='' +FOR +SELECT * FROM t1 WHERE c IN ('',''); +c +Warnings: +Warning 1287 '@@big_tables' is deprecated and will be removed in a future release +Warning 1287 '@@big_tables' is deprecated and will be removed in a future release +DROP TABLE t1; +# # End of 11.0 tests # diff --git a/mysql-test/main/derived.test b/mysql-test/main/derived.test index 83de98c5d89..78ccba0b362 100644 --- a/mysql-test/main/derived.test +++ b/mysql-test/main/derived.test @@ -1274,5 +1274,29 @@ SELECT a FROM t1 WHERE a IN ( 1, 1, 2, 194 ); drop table t1; --echo # +--echo # MDEV-31022: SIGSEGV in maria_create from create_internal_tmp_table +--echo # keydef incorrectly allocated on the stack in create_internal_tmp_table() +--echo # + +CREATE TABLE t1 (c CHAR(1) NULL) ENGINE=MyISAM; +INSERT INTO t1 (c) VALUES (1); +SET statement + optimizer_where_cost=1, + big_tables=1, + in_predicate_conversion_threshold=2 +FOR +SELECT * FROM t1 WHERE c IN ('',''); + +SET statement + optimizer_where_cost=1, + big_tables=1, + in_predicate_conversion_threshold=2, + sql_mode='' +FOR +SELECT * FROM t1 WHERE c IN ('',''); + +DROP TABLE t1; + +--echo # --echo # End of 11.0 tests --echo # diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 80228917210..3adc65008e0 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -5266,7 +5266,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) //fprintf(stderr, "Q: %s\n", current_thd->query()); DBUG_ASSERT(0); DBUG_ASSERT( - tmp_table->s->uniques || + (tmp_table->key_info->flags & HA_UNIQUE_HASH) || tmp_table->key_info->key_length >= tmp_table->file->max_key_length() || tmp_table->key_info->user_defined_key_parts > tmp_table->file->max_key_parts()); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 1d0b1ff1874..94adae02a17 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4903,11 +4903,13 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) { DBUG_PRINT("info",("Creating group key in temporary table")); share->keys=1; - share->uniques= MY_TEST(using_unique_constraint); table->key_info= share->key_info= keyinfo; keyinfo->key_part=key_part_info; - keyinfo->flags=HA_NOSAME; + keyinfo->flags= HA_NOSAME | (using_unique_constraint ? HA_UNIQUE_HASH : 0); + keyinfo->ext_key_flags= keyinfo->flags; keyinfo->usable_key_parts= keyinfo->user_defined_key_parts= 1; + keyinfo->ext_key_parts= 1; + share->key_parts= 1; keyinfo->key_length=0; keyinfo->rec_per_key=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9c665bf5df0..048f101c046 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21410,12 +21410,13 @@ bool Create_tmp_table::finalize(THD *thd, table->group= m_group; /* Table is grouped by key */ param->group_buff= m_group_buff; share->keys=1; - share->uniques= MY_TEST(m_using_unique_constraint); table->key_info= table->s->key_info= keyinfo; table->keys_in_use_for_query.set_bit(0); share->keys_in_use.set_bit(0); keyinfo->key_part= m_key_part_info; keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY; + if (m_using_unique_constraint) + keyinfo->flags|= HA_UNIQUE_HASH; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->usable_key_parts=keyinfo->user_defined_key_parts= param->group_parts; @@ -21514,6 +21515,7 @@ bool Create_tmp_table::finalize(THD *thd, */ DBUG_PRINT("info",("hidden_field_count: %d", param->hidden_field_count)); + keyinfo->flags= 0; if (m_blobs_count[distinct]) { /* @@ -21521,10 +21523,11 @@ bool Create_tmp_table::finalize(THD *thd, indexes on blobs with arbitrary length. Such indexes cannot be used for lookups. */ - share->uniques= 1; + keyinfo->flags|= HA_UNIQUE_HASH; } keyinfo->user_defined_key_parts= m_field_count[distinct] + - (share->uniques ? MY_TEST(null_pack_length[distinct]) : 0); + ((keyinfo->flags & HA_UNIQUE_HASH) ? + MY_TEST(null_pack_length[distinct]) : 0); keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->usable_key_parts= keyinfo->user_defined_key_parts; table->distinct= 1; @@ -21539,7 +21542,8 @@ bool Create_tmp_table::finalize(THD *thd, share->keys_in_use.set_bit(0); table->key_info= table->s->key_info= keyinfo; keyinfo->key_part= m_key_part_info; - keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY; + keyinfo->flags|= (HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | + HA_PACK_KEY); keyinfo->ext_key_flags= keyinfo->flags; keyinfo->key_length= 0; // Will compute the sum of the parts below. keyinfo->name= distinct_key; @@ -21568,7 +21572,7 @@ bool Create_tmp_table::finalize(THD *thd, blobs can distinguish NULL from 0. This extra field is not needed when we do not use UNIQUE indexes for blobs. */ - if (null_pack_length[distinct] && share->uniques) + if (null_pack_length[distinct] && (keyinfo->flags & HA_UNIQUE_HASH)) { m_key_part_info->null_bit=0; m_key_part_info->offset= null_pack_base[distinct]; @@ -21986,113 +21990,125 @@ bool open_tmp_table(TABLE *table) */ -bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, +bool create_internal_tmp_table(TABLE *table, KEY *org_keyinfo, TMP_ENGINE_COLUMNDEF *start_recinfo, TMP_ENGINE_COLUMNDEF **recinfo, ulonglong options) { int error; - MARIA_KEYDEF keydef; + MARIA_KEYDEF *keydefs= 0, *keydef; MARIA_UNIQUEDEF uniquedef; TABLE_SHARE *share= table->s; MARIA_CREATE_INFO create_info; + bool use_unique= false; DBUG_ENTER("create_internal_tmp_table"); if (share->keys) { // Get keys for ni_create - bool using_unique_constraint=0; - HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root, - sizeof(*seg) * keyinfo->user_defined_key_parts); - if (!seg) - goto err; + HA_KEYSEG *seg; + DBUG_ASSERT(share->key_parts); - bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts); - /* - Note that a similar check is performed during - subquery_types_allow_materialization. See MDEV-7122 for more details as - to why. Whenever this changes, it must be updated there as well, for - all tmp_table engines. - */ - if (keyinfo->key_length > table->file->max_key_length() || - keyinfo->user_defined_key_parts > table->file->max_key_parts() || - share->uniques) - { - if (!share->uniques && !(keyinfo->flags & HA_NOSAME)) - { - my_error(ER_INTERNAL_ERROR, MYF(0), - "Using too big key for internal temp tables"); - DBUG_RETURN(1); - } - - /* Can't create a key; Make a unique constraint instead of a key */ - share->keys= 0; - share->key_parts= share->ext_key_parts= 0; - share->uniques= 1; - using_unique_constraint=1; - bzero((char*) &uniquedef,sizeof(uniquedef)); - uniquedef.keysegs=keyinfo->user_defined_key_parts; - uniquedef.seg=seg; - uniquedef.null_are_equal=1; + if (!(multi_alloc_root(&table->mem_root, + &seg, sizeof(*seg) * share->key_parts, + &keydefs, sizeof(*keydefs) * share->keys, + NullS))) + goto err; + keydef= keydefs; - /* Create extra column for hash value */ - bzero((uchar*) *recinfo,sizeof(**recinfo)); - (*recinfo)->type= FIELD_CHECK; - (*recinfo)->length= MARIA_UNIQUE_HASH_LENGTH; - (*recinfo)++; + bzero(seg, sizeof(*seg) * share->key_parts); - /* Avoid warnings from valgrind */ - bzero(table->record[0]+ share->reclength, MARIA_UNIQUE_HASH_LENGTH); - bzero(share->default_values+ share->reclength, MARIA_UNIQUE_HASH_LENGTH); - share->reclength+= MARIA_UNIQUE_HASH_LENGTH; - } - else - { - /* Create a key */ - bzero((char*) &keydef,sizeof(keydef)); - keydef.flag= keyinfo->flags & HA_NOSAME; - keydef.keysegs= keyinfo->user_defined_key_parts; - keydef.seg= seg; - } - for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++) + /* Note that share->keys may change in the loop ! */ + for (KEY *keyinfo= org_keyinfo, *end_keyinfo= keyinfo + share->keys; + keyinfo < end_keyinfo ; + keyinfo++) { - Field *field=keyinfo->key_part[i].field; - seg->flag= 0; - seg->language= field->charset()->number; - seg->length= keyinfo->key_part[i].length; - seg->start= keyinfo->key_part[i].offset; - if (field->flags & BLOB_FLAG) + /* + Note that a similar check is performed during + subquery_types_allow_materialization. See MDEV-7122 for more details as + to why. Whenever this changes, it must be updated there as well, for + all tmp_table engines. + */ + if (keyinfo->key_length > table->file->max_key_length() || + keyinfo->user_defined_key_parts > table->file->max_key_parts() || + (keyinfo->flags & HA_UNIQUE_HASH)) { - seg->type= - ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ? - HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2); - seg->bit_start= (uint8)(field->pack_length() - - portable_sizeof_char_ptr); - seg->flag= HA_BLOB_PART; - seg->length=0; // Whole blob in unique constraint + if (!(keyinfo->flags & (HA_NOSAME | HA_UNIQUE_HASH))) + { + my_error(ER_INTERNAL_ERROR, MYF(0), + "Using too big key for internal temp tables"); + DBUG_RETURN(1); + } + /* Can't create a key; Make a unique constraint instead of a key */ + share->keys--; + share->key_parts-= keyinfo->user_defined_key_parts; + share->ext_key_parts-= keyinfo->ext_key_parts; + use_unique= true; + bzero((char*) &uniquedef,sizeof(uniquedef)); + uniquedef.keysegs= keyinfo->user_defined_key_parts; + uniquedef.seg=seg; + uniquedef.null_are_equal=1; + keyinfo->flags|= HA_UNIQUE_HASH; + keyinfo->algorithm= HA_KEY_ALG_UNIQUE_HASH; + + /* Create extra column for hash value */ + bzero((uchar*) *recinfo,sizeof(**recinfo)); + (*recinfo)->type= FIELD_CHECK; + (*recinfo)->length= MARIA_UNIQUE_HASH_LENGTH; + (*recinfo)++; + + /* Avoid warnings from valgrind */ + bzero(table->record[0]+ share->reclength, MARIA_UNIQUE_HASH_LENGTH); + bzero(share->default_values+ share->reclength, + MARIA_UNIQUE_HASH_LENGTH); + share->reclength+= MARIA_UNIQUE_HASH_LENGTH; } else { - seg->type= keyinfo->key_part[i].type; - /* Tell handler if it can do suffic space compression */ - if (field->real_type() == MYSQL_TYPE_STRING && - keyinfo->key_part[i].length > 32) - seg->flag|= HA_SPACE_PACK; - } - if (!(field->flags & NOT_NULL_FLAG)) - { - seg->null_bit= field->null_bit; - seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]); - /* - We are using a GROUP BY on something that contains NULL - In this case we have to tell Aria that two NULL should - on INSERT be regarded at the same value - */ - if (!using_unique_constraint) - keydef.flag|= HA_NULL_ARE_EQUAL; + /* Create a key */ + bzero((char*) keydef,sizeof(*keydef)); + /* + We are using a GROUP BY on something that contains NULL + In this case we have to tell Aria that two NULL should + on INSERT be regarded at the same value. + */ + keydef->flag= (keyinfo->flags & HA_NOSAME) | HA_NULL_ARE_EQUAL; + keydef->keysegs= keyinfo->user_defined_key_parts; + keydef->seg= seg; + keydef++; + } + for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++) + { + Field *field=keyinfo->key_part[i].field; + seg->flag= 0; + seg->language= field->charset()->number; + seg->length= keyinfo->key_part[i].length; + seg->start= keyinfo->key_part[i].offset; + if (field->flags & BLOB_FLAG) + { + seg->type= + ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ? + HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2); + seg->bit_start= (uint8)(field->pack_length() - + portable_sizeof_char_ptr); + seg->flag= HA_BLOB_PART; + seg->length=0; // Whole blob in unique constraint + } + else + { + seg->type= keyinfo->key_part[i].type; + /* Tell handler if it can do suffic space compression */ + if (field->real_type() == MYSQL_TYPE_STRING && + keyinfo->key_part[i].length > 32) + seg->flag|= HA_SPACE_PACK; + } + if (!(field->flags & NOT_NULL_FLAG)) + { + seg->null_bit= field->null_bit; + seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]); + } } - } - if (share->keys) keyinfo->index_flags= table->file->index_flags(0, 0, 1); + } } bzero((char*) &create_info,sizeof(create_info)); create_info.data_file_length= table->in_use->variables.tmp_disk_table_size; @@ -22138,8 +22154,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } if (unlikely((error= maria_create(share->path.str, file_type, share->keys, - &keydef, (uint) (*recinfo-start_recinfo), - start_recinfo, share->uniques, &uniquedef, + keydefs, (uint) (*recinfo-start_recinfo), + start_recinfo, use_unique, &uniquedef, &create_info, create_flags)))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ @@ -22191,7 +22207,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, /* Create internal MyISAM temporary table */ -bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, +bool create_internal_tmp_table(TABLE *table, KEY *org_keyinfo, TMP_ENGINE_COLUMNDEF *start_recinfo, TMP_ENGINE_COLUMNDEF **recinfo, ulonglong options) @@ -22206,11 +22222,12 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, { // Get keys for ni_create bool using_unique_constraint=0; HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root, - sizeof(*seg) * keyinfo->user_defined_key_parts); + sizeof(*seg) * + share->user_defined_key_parts); if (!seg) goto err; - bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts); + bzero(seg, sizeof(*seg) * share->user_defined_key_parts); /* Note that a similar check is performed during subquery_types_allow_materialization. See MDEV-7122 for more details as @@ -22546,7 +22563,7 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab) Note for MyISAM tmp tables: if uniques is true keys won't be created. */ - if (table->s->keys && !table->s->uniques) + if (table->s->keys && !table->s->have_unique_constraint()) { DBUG_PRINT("info",("Using end_update")); aggr->set_write_func(end_update); @@ -22856,6 +22873,8 @@ bool instantiate_tmp_table(TABLE *table, KEY *keyinfo, TMP_ENGINE_COLUMNDEF **recinfo, ulonglong options) { + DBUG_ASSERT(table->s->keys == 0 || table->key_info == keyinfo); + DBUG_ASSERT(table->s->keys <= 1); if (table->s->db_type() == TMP_ENGINE_HTON) { /* @@ -24768,7 +24787,6 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error if (is_duplicate) goto end; - table->s->uniques=0; // To ensure rows are the same } if (++join_tab->send_records >= join_tab->tmp_table_param->end_write_records && diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1a99f6f0bae..fa95aa66b0f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -8841,7 +8841,6 @@ bool optimize_schema_tables_memory_usage(List<TABLE_LIST> &tables) TMP_TABLE_PARAM *p= table_list->schema_table_param; TMP_ENGINE_COLUMNDEF *from_recinfo, *to_recinfo; DBUG_ASSERT(table->s->keys == 0); - DBUG_ASSERT(table->s->uniques == 0); uchar *cur= table->field[0]->ptr; /* first recinfo could be a NULL bitmap, not an actual Field */ diff --git a/sql/table.h b/sql/table.h index c67a5783c89..3874ccacb9a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -853,7 +853,18 @@ struct TABLE_SHARE uint keys, key_parts; uint ext_key_parts; /* Total number of key parts in extended keys */ uint max_key_length, max_unique_length; - uint uniques; /* Number of UNIQUE index */ + + /* + Older versions had TABLE_SHARE::uniques but now it is replaced with + per-index HA_UNIQUE_HASH flag + */ + bool have_unique_constraint() const + { + for (uint i=0; i < keys; i++) + if (key_info[i].flags & HA_UNIQUE_HASH) + return true; + return false; + } uint db_create_options; /* Create options from database */ uint db_options_in_use; /* Options in use */ uint db_record_offset; /* if HA_REC_IN_SEQ */ diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 08e2b34a90e..2f58110cb91 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -1082,7 +1082,8 @@ const char *ha_maria::index_type(uint key_number) ulong ha_maria::index_flags(uint inx, uint part, bool all_parts) const { ulong flags; - if (table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) + if (table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT || + table_share->key_info[inx].algorithm == HA_KEY_ALG_UNIQUE_HASH) flags= 0; else if ((table_share->key_info[inx].flags & HA_SPATIAL || |