MDEV-35078 Server crash or ASAN errors in mhnsw_insert

when adding a column or index that uses plugin-defined
sysvar-based options with ALTER TABLE ... ADD, the server
was using the default value of the sysvar, not the current one.

CREATE TABLE was correctly using the current sysvar value.

Fix it so that new columns/indexes added in ALTER TABLE ... ADD
would use a current sysvar value. Existing columns/indexes
in ALTER TABLE would keep using the default sysvar value
(unless they had an explicit value in frm).
This commit is contained in:
Sergei Golubchik 2024-10-06 11:55:54 +02:00
parent 855aefb7b5
commit ea1e720391
7 changed files with 61 additions and 18 deletions

View file

@ -51,3 +51,28 @@ create table t (a int, v blob not null, vector index(v)) engine=myisam;
create table tm (a int, v blob not null, vector index(v)) engine=merge union=(t);
ERROR HY000: Table storage engine 'MERGE' does not support the create option 'VECTOR'
drop table t;
#
# MDEV-35078 Server crash or ASAN errors in mhnsw_insert
#
set session mhnsw_max_edges_per_node = 4;
create table t (a int, v blob not null);
insert into t select seq, x'00000000' from seq_1_to_10;
alter table t add vector(v);
show create table t;
Table Create Table
t CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
`v` blob NOT NULL,
VECTOR KEY `v` (`v`) `max_edges_per_node`=4
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
create table x like t;
show create table x;
Table Create Table
x CREATE TABLE `x` (
`a` int(11) DEFAULT NULL,
`v` blob NOT NULL,
VECTOR KEY `v` (`v`) `max_edges_per_node`=4
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
insert into t values (11,x'00000000');
drop table t, x;
set session mhnsw_max_edges_per_node = default;

View file

@ -1,6 +1,7 @@
#
# tests that don't need to be run on many engines
#
--source include/have_sequence.inc
--echo # Aria doesn't support VECTOR yet
--error ER_ILLEGAL_HA_CREATE_OPTION
@ -55,3 +56,17 @@ create table t (a int, v blob not null, vector index(v)) engine=myisam;
--error ER_ILLEGAL_HA_CREATE_OPTION
create table tm (a int, v blob not null, vector index(v)) engine=merge union=(t);
drop table t;
--echo #
--echo # MDEV-35078 Server crash or ASAN errors in mhnsw_insert
--echo #
set session mhnsw_max_edges_per_node = 4;
create table t (a int, v blob not null);
insert into t select seq, x'00000000' from seq_1_to_10;
alter table t add vector(v);
show create table t;
create table x like t;
show create table x;
insert into t values (11,x'00000000');
drop table t, x;
set session mhnsw_max_edges_per_node = default;

View file

@ -220,6 +220,7 @@ static const size_t ha_option_type_sizeof[]=
@param option_list list of options given by user
@param rules list of option description by engine
@param suppress_warning second parse so we do not need warnings
@param create a new object is created, use current sysvar value
@param root MEM_ROOT where allocate memory
@retval TRUE Error
@ -229,7 +230,7 @@ static const size_t ha_option_type_sizeof[]=
bool parse_option_list(THD* thd, st_plugin_int *plugin, void *option_struct_arg,
engine_option_value **option_list,
ha_create_table_option *rules,
bool suppress_warning, MEM_ROOT *root)
bool suppress_warning, bool create, MEM_ROOT *root)
{
ha_create_table_option *opt;
size_t option_struct_size= 0;
@ -276,7 +277,7 @@ bool parse_option_list(THD* thd, st_plugin_int *plugin, void *option_struct_arg,
/*
Okay, here's the logic for sysvar options:
1. When we parse CREATE TABLE and sysvar option was not explicitly
1. When an object is created and sysvar option was not explicitly
mentioned we add it to the list as if it was specified with the
*current* value of the underlying sysvar.
2. But only if the underlying sysvar value is different from the
@ -298,12 +299,12 @@ bool parse_option_list(THD* thd, st_plugin_int *plugin, void *option_struct_arg,
Note that if the option was set explicitly (not =DEFAULT) it wouldn't
have passes the if() condition above.
*/
if (!suppress_warning && opt->var &&
(thd->lex->sql_command == SQLCOM_CREATE_TABLE || seen))
if (opt->var && (create || seen))
{
// take a value from the variable and add it to the list
sys_var *sysvar= find_plugin_sysvar(plugin, opt->var);
DBUG_ASSERT(sysvar);
DBUG_ASSERT(!suppress_warning); // always warn when creating
if (!sysvar->session_is_default(thd))
{
@ -438,14 +439,14 @@ bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
DBUG_ENTER("parse_engine_table_options");
if (parse_option_list(thd, ht, &share->option_struct, & share->option_list,
ht->table_options, TRUE, root))
ht->table_options, TRUE, FALSE, root))
DBUG_RETURN(TRUE);
for (Field **field= share->field; *field; field++)
{
if (parse_option_list(thd, ht, &(*field)->option_struct,
& (*field)->option_list,
ht->field_options, TRUE, root))
ht->field_options, TRUE, FALSE, root))
DBUG_RETURN(TRUE);
}
@ -453,7 +454,7 @@ bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
{
if (parse_option_list(thd, ht, &share->key_info[index].option_struct,
& share->key_info[index].option_list,
ht->index_options, TRUE, root))
ht->index_options, TRUE, FALSE, root))
DBUG_RETURN(TRUE);
}
@ -497,7 +498,7 @@ bool parse_engine_part_options(THD *thd, TABLE *table)
{
ht= part_elem->engine_type;
if (parse_option_list(thd, ht, &part_elem->option_struct,
&tmp_option_list, ht->table_options, TRUE, root))
&tmp_option_list, ht->table_options, TRUE, FALSE, root))
DBUG_RETURN(TRUE);
}
else
@ -507,7 +508,7 @@ bool parse_engine_part_options(THD *thd, TABLE *table)
{
ht= sub_part_elem->engine_type;
if (parse_option_list(thd, ht, &sub_part_elem->option_struct,
&tmp_option_list, ht->table_options, TRUE, root))
&tmp_option_list, ht->table_options, TRUE, FALSE, root))
DBUG_RETURN(TRUE);
}
}

View file

@ -114,14 +114,15 @@ bool parse_engine_part_options(THD *thd, TABLE *table);
bool parse_option_list(THD* thd, st_plugin_int *plugin, void *option_struct,
engine_option_value **option_list,
ha_create_table_option *rules,
bool suppress_warning, MEM_ROOT *root);
bool suppress_warning, bool create, MEM_ROOT *root);
static inline bool parse_option_list(THD* thd, handlerton *hton,
void *option_struct, engine_option_value **option_list,
ha_create_table_option *rules, bool suppress_warning, MEM_ROOT *root)
ha_create_table_option *rules, bool suppress_warning, bool create,
MEM_ROOT *root)
{
return parse_option_list(thd, hton2plugin[hton->slot], option_struct,
option_list, rules, suppress_warning, root);
option_list, rules, suppress_warning, create, root);
}
bool engine_table_options_frm_read(const uchar *buff, size_t length,

View file

@ -3035,7 +3035,7 @@ mysql_prepare_create_table_finalize(THD *thd, HA_CREATE_INFO *create_info,
if (parse_option_list(thd, create_info->db_type, &sql_field->option_struct,
&sql_field->option_list,
create_info->db_type->field_options, FALSE,
thd->mem_root))
sql_field->field == NULL, thd->mem_root))
DBUG_RETURN(TRUE);
/*
For now skip fields that are not physically stored in the database
@ -3320,7 +3320,7 @@ mysql_prepare_create_table_finalize(THD *thd, HA_CREATE_INFO *create_info,
key_info->option_list= key->option_list;
if (parse_option_list(thd, index_plugin, &key_info->option_struct,
&key_info->option_list, index_options,
FALSE, thd->mem_root))
FALSE, !key->old, thd->mem_root))
DBUG_RETURN(TRUE);
if (key->type == Key::FULLTEXT)
@ -3916,6 +3916,7 @@ without_overlaps_err:
if (parse_option_list(thd, file->partition_ht(), &create_info->option_struct,
&create_info->option_list,
file->partition_ht()->table_options, FALSE,
create_table_mode > C_ALTER_TABLE,
thd->mem_root))
DBUG_RETURN(TRUE);

View file

@ -129,9 +129,9 @@ bool add_keyword_to_query(THD *thd, String *result, const LEX_CSTRING *keyword,
*/
#define C_CREATE_SELECT(X) ((X) > 0 ? (X) : 0)
#define C_ORDINARY_CREATE 0
#define C_ALTER_TABLE -1
#define C_ALTER_TABLE_FRM_ONLY -2
#define C_ASSISTED_DISCOVERY -3
#define C_ASSISTED_DISCOVERY -1
#define C_ALTER_TABLE -2
#define C_ALTER_TABLE_FRM_ONLY -3
int mysql_create_table_no_lock(THD *thd,
DDL_LOG_STATE *ddl_log_state,

View file

@ -3432,7 +3432,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
keyinfo= share->key_info + share->keys;
if (parse_option_list(thd, mhnsw_plugin, &keyinfo->option_struct,
&keyinfo->option_list, mhnsw_index_options,
TRUE, thd->mem_root))
TRUE, FALSE, thd->mem_root))
goto err;
}