1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-01 03:26:54 +03:00

MDEV-35144 CREATE TABLE ... LIKE uses current innodb_compression_default instead of the create value

When adding a column or index that uses plugin-defined
sysvar-based options with CREATE ... LIKE the server
was using the current value of the sysvar, not the default one.

Because parse_option_list() function was used both in create
and open and it tried to guess when it's create (need to use
current sysvar value and add a new name=value pair to the list)
or open (need to use default, without extending the list).

Let's move the list extending functionality into a separate
function and call it explicitly when needed. Operations that
add new objects (CREATE, ALTER ... ADD) will extend the list,
other operations (ALTER, CREATE ... LIKE, open) will not.
This commit is contained in:
Sergei Golubchik
2024-10-17 15:57:03 +02:00
parent 600c42ea86
commit 3da565c41d
9 changed files with 184 additions and 77 deletions

View File

@ -216,7 +216,7 @@ show create table t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL `a` int(11) DEFAULT NULL
) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci `VAROPT`=33 ) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci `varopt`=33
drop table t1; drop table t1;
SET @@SQL_MODE=@OLD_SQL_MODE; SET @@SQL_MODE=@OLD_SQL_MODE;
select 1; select 1;

View File

@ -0,0 +1,21 @@
#
# MDEV-35144 CREATE TABLE ... LIKE uses current innodb_compression_default instead of the create value
#
set innodb_compression_default= off;
create table t1 (a int, b blob) engine=innodb;
set innodb_compression_default= on;
create table s_import like t1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` blob DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
show create table s_import;
Table Create Table
s_import CREATE TABLE `s_import` (
`a` int(11) DEFAULT NULL,
`b` blob DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
DROP TABLE t1,s_import;
# End of 10.5 tests

View File

@ -0,0 +1,16 @@
--source include/have_innodb.inc
--echo #
--echo # MDEV-35144 CREATE TABLE ... LIKE uses current innodb_compression_default instead of the create value
--echo #
set innodb_compression_default= off;
create table t1 (a int, b blob) engine=innodb;
set innodb_compression_default= on;
create table s_import like t1;
show create table t1;
show create table s_import;
DROP TABLE t1,s_import;
--echo # End of 10.5 tests

View File

@ -340,5 +340,39 @@ next_not_cached_value minimum_value maximum_value start_value increment cache_si
1001 1 9223372036854775806 1 1 1000 0 0 1001 1 9223372036854775806 1 1 1000 0 0
drop sequence s1,s2; drop sequence s1,s2;
# #
# End of 10.5 tests # MDEV-35144 CREATE TABLE ... LIKE uses current innodb_compression_default instead of the create value
# #
set @@innodb_compression_default= off;
create or replace sequence s engine=innodb;
set @@innodb_compression_default= on;
create or replace table s_import like s;
show create table s;
Table Create Table
s CREATE TABLE `s` (
`next_not_cached_value` bigint(21) NOT NULL,
`minimum_value` bigint(21) NOT NULL,
`maximum_value` bigint(21) NOT NULL,
`start_value` bigint(21) NOT NULL COMMENT 'start value when sequences is created or value if RESTART is used',
`increment` bigint(21) NOT NULL COMMENT 'increment value',
`cache_size` bigint(21) unsigned NOT NULL,
`cycle_option` tinyint(1) unsigned NOT NULL COMMENT '0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed',
`cycle_count` bigint(21) NOT NULL COMMENT 'How many cycles have been done'
) ENGINE=InnoDB SEQUENCE=1
show create table s_import;
Table Create Table
s_import CREATE TABLE `s_import` (
`next_not_cached_value` bigint(21) NOT NULL,
`minimum_value` bigint(21) NOT NULL,
`maximum_value` bigint(21) NOT NULL,
`start_value` bigint(21) NOT NULL COMMENT 'start value when sequences is created or value if RESTART is used',
`increment` bigint(21) NOT NULL COMMENT 'increment value',
`cache_size` bigint(21) unsigned NOT NULL,
`cycle_option` tinyint(1) unsigned NOT NULL COMMENT '0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed',
`cycle_count` bigint(21) NOT NULL COMMENT 'How many cycles have been done'
) ENGINE=InnoDB SEQUENCE=1
alter table s_import discard tablespace;
flush table s for export;
UNLOCK TABLES;
alter table s_import import tablespace;
drop table s,s_import;
# End of 10.5 tests

View File

@ -239,5 +239,23 @@ drop sequence s1,s2;
--enable_ps_protocol --enable_ps_protocol
--echo # --echo #
--echo # End of 10.5 tests --echo # MDEV-35144 CREATE TABLE ... LIKE uses current innodb_compression_default instead of the create value
--echo # --echo #
set @@innodb_compression_default= off;
create or replace sequence s engine=innodb;
set @@innodb_compression_default= on;
create or replace table s_import like s;
show create table s;
show create table s_import;
alter table s_import discard tablespace;
flush table s for export;
--copy_file $MYSQLD_DATADIR/test/s.ibd $MYSQLD_DATADIR/test/s_import.ibd
--copy_file $MYSQLD_DATADIR/test/s.cfg $MYSQLD_DATADIR/test/s_import.cfg
UNLOCK TABLES;
alter table s_import import tablespace;
drop table s,s_import;
--echo # End of 10.5 tests

View File

@ -117,15 +117,13 @@ static bool report_unknown_option(THD *thd, engine_option_value *val,
#define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset) #define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset)
static bool set_one_value(ha_create_table_option *opt, static bool set_one_value(ha_create_table_option *opt, THD *thd,
THD *thd, const LEX_CSTRING *value, void *base, const LEX_CSTRING *value, void *base,
bool suppress_warning, bool suppress_warning, MEM_ROOT *root)
MEM_ROOT *root)
{ {
DBUG_ENTER("set_one_value"); DBUG_ENTER("set_one_value");
DBUG_PRINT("enter", ("opt: %p type: %u name '%s' value: '%s'", DBUG_PRINT("enter", ("opt: %p type: %u name '%s' value: '%s'",
opt, opt, opt->type, opt->name,
opt->type, opt->name,
(value->str ? value->str : "<DEFAULT>"))); (value->str ? value->str : "<DEFAULT>")));
switch (opt->type) switch (opt->type)
{ {
@ -141,10 +139,9 @@ static bool set_one_value(ha_create_table_option *opt,
DBUG_RETURN(0); DBUG_RETURN(0);
} }
my_option optp= my_option optp= { opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL,
{ opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL,
REQUIRED_ARG, (longlong)opt->def_value, (longlong)opt->min_value, REQUIRED_ARG, (longlong)opt->def_value, (longlong)opt->min_value,
opt->max_value, 0, (long) opt->block_size, 0}; opt->max_value, 0, (long) opt->block_size, 0 };
ulonglong orig_val= strtoull(value->str, NULL, 10); ulonglong orig_val= strtoull(value->str, NULL, 10);
my_bool unused; my_bool unused;
@ -236,6 +233,63 @@ static bool set_one_value(ha_create_table_option *opt,
static const size_t ha_option_type_sizeof[]= static const size_t ha_option_type_sizeof[]=
{ sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)}; { sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)};
/**
Appends values of sysvar-based options if needed
@param thd thread handler
@param option_list list of options given by user
@param rules list of option description by engine
@param root MEM_ROOT where allocate memory
@retval TRUE Error
@retval FALSE OK
*/
bool extend_option_list(THD* thd, handlerton *hton, bool create,
engine_option_value **option_list,
ha_create_table_option *rules, MEM_ROOT *root)
{
DBUG_ENTER("extend_option_list");
for (ha_create_table_option *opt= rules; rules && opt->name; opt++)
{
if (opt->var)
{
engine_option_value *found= NULL, *last;
for (engine_option_value *val= *option_list; val; val= val->next)
{
last= val;
if (!system_charset_info->strnncoll(opt->name, opt->name_length,
val->name.str, val->name.length))
found= val; // find the last matching
}
if (found ? !found->value.str : create)
{
/* add the current value of the corresponding sysvar to the list */
sys_var *sysvar= find_hton_sysvar(hton, opt->var);
DBUG_ASSERT(sysvar);
if (!sysvar->session_is_default(thd))
{
StringBuffer<256> sbuf(system_charset_info);
String *str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_clex_str);
DBUG_ASSERT(str);
LEX_CSTRING name= { opt->name, opt->name_length };
LEX_CSTRING value= safe_lexcstrdup_root(root, str->to_lex_cstring());
if (found)
found->value= value;
else
new (root) engine_option_value(name, value,
opt->type != HA_OPTION_TYPE_ULL, option_list, &last);
}
}
}
}
DBUG_RETURN(FALSE);
}
/** /**
Creates option structure and parses list of options in it Creates option structure and parses list of options in it
@ -250,7 +304,7 @@ static const size_t ha_option_type_sizeof[]=
@retval FALSE OK @retval FALSE OK
*/ */
bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, bool parse_option_list(THD* thd, void *option_struct_arg,
engine_option_value **option_list, engine_option_value **option_list,
ha_create_table_option *rules, ha_create_table_option *rules,
bool suppress_warning, MEM_ROOT *root) bool suppress_warning, MEM_ROOT *root)
@ -296,58 +350,8 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
break; break;
} }
if (!seen || (opt->var && !last->value.str)) if (!seen || (opt->var && !last->value.str))
{ set_one_value(opt, thd, &null_clex_str, *option_struct,
LEX_CSTRING default_val= null_clex_str;
/*
Okay, here's the logic for sysvar options:
1. When we parse CREATE TABLE 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
sysvar's default.
3. If it's ALTER TABLE or CREATE_SEQUENCE and the sysvar option was
not explicitly mentioned - do nothing, do not add it to the list.
4. But if it was ALTER TABLE with sysvar option = DEFAULT, we
add it to the list (under the same condition #2).
5. If we're here parsing the option list from the .frm file
for a normal open_table() and the sysvar option was not there -
do not add it to the list (makes no sense anyway) and
use the *default* value of the underlying sysvar. Because
sysvar value can change, but it should not affect existing tables.
This is how it's implemented: the current sysvar value is added
to the list if suppress_warning is FALSE (meaning a table is created,
that is CREATE TABLE or ALTER TABLE) and it's actually a CREATE TABLE
command or it's an ALTER TABLE and the option was seen (=DEFAULT).
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))
{
// take a value from the variable and add it to the list
sys_var *sysvar= find_hton_sysvar(hton, opt->var);
DBUG_ASSERT(sysvar);
if (!sysvar->session_is_default(thd))
{
char buf[256];
String sbuf(buf, sizeof(buf), system_charset_info), *str;
if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_clex_str)))
{
LEX_CSTRING name= { opt->name, opt->name_length };
default_val.str= strmake_root(root, str->ptr(), str->length());
default_val.length= str->length();
val= new (root) engine_option_value(name, default_val,
opt->type != HA_OPTION_TYPE_ULL, option_list, &last);
val->parsed= true;
}
}
}
set_one_value(opt, thd, &default_val, *option_struct,
suppress_warning, root); suppress_warning, root);
}
} }
for (val= *option_list; val; val= val->next) for (val= *option_list; val; val= val->next)
@ -474,13 +478,13 @@ bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
MEM_ROOT *root= &share->mem_root; MEM_ROOT *root= &share->mem_root;
DBUG_ENTER("parse_engine_table_options"); DBUG_ENTER("parse_engine_table_options");
if (parse_option_list(thd, ht, &share->option_struct, & share->option_list, if (parse_option_list(thd, &share->option_struct, & share->option_list,
ht->table_options, TRUE, root)) ht->table_options, TRUE, root))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
for (Field **field= share->field; *field; field++) for (Field **field= share->field; *field; field++)
{ {
if (parse_option_list(thd, ht, &(*field)->option_struct, if (parse_option_list(thd, &(*field)->option_struct,
& (*field)->option_list, & (*field)->option_list,
ht->field_options, TRUE, root)) ht->field_options, TRUE, root))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@ -488,7 +492,7 @@ bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
for (uint index= 0; index < share->keys; index ++) for (uint index= 0; index < share->keys; index ++)
{ {
if (parse_option_list(thd, ht, &share->key_info[index].option_struct, if (parse_option_list(thd, &share->key_info[index].option_struct,
& share->key_info[index].option_list, & share->key_info[index].option_list,
ht->index_options, TRUE, root)) ht->index_options, TRUE, root))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);

View File

@ -83,10 +83,14 @@ class Create_field;
bool resolve_sysvar_table_options(handlerton *hton); bool resolve_sysvar_table_options(handlerton *hton);
void free_sysvar_table_options(handlerton *hton); void free_sysvar_table_options(handlerton *hton);
bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share); bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share);
bool parse_option_list(THD* thd, handlerton *hton, void *option_struct, bool parse_option_list(THD* thd, void *option_struct,
engine_option_value **option_list, engine_option_value **option_list,
ha_create_table_option *rules, ha_create_table_option *rules,
bool suppress_warning, MEM_ROOT *root); bool suppress_warning, MEM_ROOT *root);
bool extend_option_list(THD* thd, handlerton *hton, bool create,
engine_option_value **option_list,
ha_create_table_option *rules, MEM_ROOT *root);
bool engine_table_options_frm_read(const uchar *buff, size_t length, bool engine_table_options_frm_read(const uchar *buff, size_t length,
TABLE_SHARE *share); TABLE_SHARE *share);
engine_option_value *merge_engine_table_options(engine_option_value *source, engine_option_value *merge_engine_table_options(engine_option_value *source,

View File

@ -3767,7 +3767,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->offset= record_offset; sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
auto_increment++; auto_increment++;
if (parse_option_list(thd, create_info->db_type, &sql_field->option_struct, extend_option_list(thd, create_info->db_type, !sql_field->field,
&sql_field->option_list,
create_info->db_type->field_options,
thd->stmt_arena->mem_root);
if (parse_option_list(thd, &sql_field->option_struct,
&sql_field->option_list, &sql_field->option_list,
create_info->db_type->field_options, FALSE, create_info->db_type->field_options, FALSE,
thd->mem_root)) thd->mem_root))
@ -4034,7 +4038,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->usable_key_parts= key_number; key_info->usable_key_parts= key_number;
key_info->algorithm= key->key_create_info.algorithm; key_info->algorithm= key->key_create_info.algorithm;
key_info->option_list= key->option_list; key_info->option_list= key->option_list;
if (parse_option_list(thd, create_info->db_type, &key_info->option_struct, extend_option_list(thd, create_info->db_type, !key->old,
&key_info->option_list, create_info->db_type->index_options,
thd->stmt_arena->mem_root);
if (parse_option_list(thd, &key_info->option_struct,
&key_info->option_list, &key_info->option_list,
create_info->db_type->index_options, FALSE, create_info->db_type->index_options, FALSE,
thd->mem_root)) thd->mem_root))
@ -4624,10 +4631,13 @@ without_overlaps_err:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_OPTION, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_OPTION,
ER_THD(thd, ER_UNKNOWN_OPTION), "transactional"); ER_THD(thd, ER_UNKNOWN_OPTION), "transactional");
if (parse_option_list(thd, file->partition_ht(), &create_info->option_struct, extend_option_list(thd, file->partition_ht(),
&create_info->option_list, !thd->lex->create_like() && create_table_mode > C_ALTER_TABLE,
file->partition_ht()->table_options, FALSE, &create_info->option_list, file->partition_ht()->table_options,
thd->mem_root)) thd->stmt_arena->mem_root);
if (parse_option_list(thd, &create_info->option_struct,
&create_info->option_list,
file->partition_ht()->table_options, FALSE, thd->mem_root))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
DBUG_EXECUTE_IF("key", DBUG_EXECUTE_IF("key",

View File

@ -190,9 +190,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_CREATE_SELECT(X) ((X) > 0 ? (X) : 0)
#define C_ORDINARY_CREATE 0 #define C_ORDINARY_CREATE 0
#define C_ALTER_TABLE -1 #define C_ASSISTED_DISCOVERY -1
#define C_ALTER_TABLE_FRM_ONLY -2 #define C_ALTER_TABLE -2
#define C_ASSISTED_DISCOVERY -3 #define C_ALTER_TABLE_FRM_ONLY -3
int mysql_create_table_no_lock(THD *thd, Table_specification_st *create_info, int mysql_create_table_no_lock(THD *thd, Table_specification_st *create_info,
Alter_info *alter_info, bool *is_trans, Alter_info *alter_info, bool *is_trans,