diff options
author | Alexander Barkov <bar@mariadb.com> | 2021-03-25 06:55:18 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2021-04-07 12:09:53 +0400 |
commit | 58780b5afbd00a87899c524735ec11b3534e51ee (patch) | |
tree | cbec6735a683969c04c544ed1996156ac00d0152 | |
parent | f69c1c9dcb815d7597ec2035470a81ac3b6c9380 (diff) | |
download | mariadb-git-58780b5afbd00a87899c524735ec11b3534e51ee.tar.gz |
MDEV-22775 [HY000][1553] Changing name of primary key column with foreign key constraint fails.
Problem:
The problem happened because of a conceptual flaw in the server code:
a. The table level CHARSET/COLLATE clause affected all data types,
including numeric and temporal ones:
CREATE TABLE t1 (a INT) CHARACTER SET utf8 [COLLATE utf8_general_ci];
In the above example, the Column_definition_attributes
(and then the FRM record) for the column "a" erroneously inherited
"utf8" as its character set.
b. The "ALTER TABLE t1 CONVERT TO CHARACTER SET csname" statement
also erroneously affected Column_definition_attributes::charset
for numeric and temporal data types and wrote "csname" as their
character set into FRM files.
So now we have arbitrary non-relevant charset ID values for numeric
and temporal data types in all FRM files in the world :)
The code in the server and the other engines did not seem to be affected
by this flaw. Only InnoDB inplace ALTER was affected.
Solution:
Fixing the code in the way that only character string data types
(CHAR,VARCHAR,TEXT,ENUM,SET):
- inherit the table level CHARSET/COLLATE clause
- get the charset value according to "CONVERT TO CHARACTER SET csname".
Numeric and temporal data types now always get &my_charset_numeric
in Column_definition_attributes::charset and always write its ID into FRM files:
- no matter what the table level CHARSET/COLLATE clause is, and
- no matter what "CONVERT TO CHARACTER SET" says.
Details:
1. Adding helper classes to pass small parts of HA_CREATE_INFO
into Type_handler methods:
- Column_derived_attributes - to pass table level CHARSET/COLLATE,
so columns that do not have explicit CHARSET/COLLATE clauses
can derive them from the table level, e.g.
CREATE TABLE t1 (a VARCHAR(1), b CHAR(1)) CHARACTER SET utf8;
- Column_bulk_alter_attributes - to pass bulk attribute changes
generated by the ALTER related code. These bulk changes affect
multiple columns at the same time:
ALTER TABLE ... CONVERT TO CHARACTER SET csname;
Note, passing the whole HA_CREATE_INFO directly to Type_handler
would not be good: HA_CREATE_INFO is huge and would need not desired
dependencies in sql_type.h and sql_type.cc. The Type_handler API should
use smallest possible data types!
2. Type_handler::Column_definition_prepare_stage1() is now responsible
to set Column_definition::charset properly, according to the data type,
for example:
- For string data types, Column_definition_attributes::charset is set from
the table level CHARSET/COLLATE clause (if not specified explicitly in
the column definition).
- For numeric and temporal fields, Column_definition_attributes::charset is
set to &my_charset_numeric, no matter what the table level
CHARSET/COLLATE says.
- For GEOMETRY, Column_definition_attributes::charset is set to
&my_charset_bin, no matter what the table level CHARSET/COLLATE says.
Previously this code (setting `charset`) was outside of of
Column_definition_prepare_stage1(), namely in
mysql_prepare_create_table(), and was erroneously called for
all data types.
3. Adding Type_handler::Column_definition_bulk_alter(), to handle
"ALTER TABLE .. CONVERT TO". Previously this code was inside
get_sql_field_charset() and was erroneously called for all data types.
4. Removing the Schema_specification_st parameter from
Type_handler::Column_definition_redefine_stage1().
Column_definition_attributes::charset is now fully properly initialized by
Column_definition_prepare_stage1(). So we don't need access to the
table level CHARSET/COLLATE clause in Column_definition_redefine_stage1()
any more.
5. Other changes:
- Removing global function get_sql_field_charset()
- Moving the part of the former get_sql_field_charset(), which was
responsible to inherit the table level CHARSET/COLLATE clause to
new methods:
-- Column_definition_attributes::explicit_or_derived_charset() and
-- Column_definition::prepare_charset_for_string().
This code is only needed for string data types.
Previously it was erroneously called for all data types.
- Moving another part, which was responsible to apply the
"CONVERT TO" clause, to
Type_handler_general_purpose_string::Column_definition_bulk_alter().
- Replacing the call for get_sql_field_charset() in sql_partition.cc
to sql_field->explicit_or_derived_charset() - it is perfectly enough.
The old code was redundant: get_sql_field_charset() was called from
sql_partition.cc only when there were no a "CONVERT TO CHARACTER SET"
clause involved, so its purpose was only to inherit the table
level CHARSET/COLLATE clause.
- Moving the code handling the BINCMP_FLAG flag from
mysql_prepare_create_table() to
Column_definition::prepare_charset_for_string():
This code is responsible to resolve the BINARY comparison style
into the corresponding _bin collation, to do the following transparent
rewrite:
CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8; ->
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin);
This code is only needed for string data types.
Previously it was erroneously called for all data types.
6. Renaming Table_scope_and_contents_source_pod_st::table_charset
to alter_table_convert_to_charset, because the only purpose it's used for
is handlering "ALTER .. CONVERT". The new name is much more self-descriptive.
-rw-r--r-- | mysql-test/suite/innodb/r/instant_alter_charset.result | 11 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/instant_alter_charset.test | 14 | ||||
-rw-r--r-- | sql/field.cc | 18 | ||||
-rw-r--r-- | sql/field.h | 38 | ||||
-rw-r--r-- | sql/handler.h | 4 | ||||
-rw-r--r-- | sql/sql_partition.cc | 4 | ||||
-rw-r--r-- | sql/sql_table.cc | 80 | ||||
-rw-r--r-- | sql/sql_table.h | 2 | ||||
-rw-r--r-- | sql/sql_type.cc | 140 | ||||
-rw-r--r-- | sql/sql_type.h | 139 | ||||
-rw-r--r-- | sql/table.cc | 2 | ||||
-rw-r--r-- | storage/federatedx/ha_federatedx.cc | 4 |
12 files changed, 338 insertions, 118 deletions
diff --git a/mysql-test/suite/innodb/r/instant_alter_charset.result b/mysql-test/suite/innodb/r/instant_alter_charset.result index 6b60c79b558..8b1171191fa 100644 --- a/mysql-test/suite/innodb/r/instant_alter_charset.result +++ b/mysql-test/suite/innodb/r/instant_alter_charset.result @@ -2032,3 +2032,14 @@ ALTER TABLE t1 MODIFY a VARCHAR(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; INSERT INTO t1 VALUES ('a'); DROP TABLE t1; +# +# MDEV-22775 [HY000][1553] Changing name of primary key column with foreign key constraint fails. +# +create table t1 (id int primary key) engine=innodb default charset=utf8; +create table t2 (input_id int primary key, id int not null, +key a (id), +constraint a foreign key (id) references t1 (id) +)engine=innodb default charset=utf8; +alter table t1 change id id2 int; +drop table t2; +drop table t1; diff --git a/mysql-test/suite/innodb/t/instant_alter_charset.test b/mysql-test/suite/innodb/t/instant_alter_charset.test index a5ddd49830c..1d444b88a7f 100644 --- a/mysql-test/suite/innodb/t/instant_alter_charset.test +++ b/mysql-test/suite/innodb/t/instant_alter_charset.test @@ -843,3 +843,17 @@ ALTER TABLE t1 MODIFY a VARCHAR(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; INSERT INTO t1 VALUES ('a'); DROP TABLE t1; + + +--echo # +--echo # MDEV-22775 [HY000][1553] Changing name of primary key column with foreign key constraint fails. +--echo # + +create table t1 (id int primary key) engine=innodb default charset=utf8; +create table t2 (input_id int primary key, id int not null, + key a (id), + constraint a foreign key (id) references t1 (id) +)engine=innodb default charset=utf8; +alter table t1 change id id2 int; +drop table t2; +drop table t1; diff --git a/sql/field.cc b/sql/field.cc index 7aea222ca07..89c51288de8 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -11076,16 +11076,26 @@ Column_definition::Column_definition(THD *thd, Field *old_field, CREATE TABLE t1 (a INT) AS SELECT a FROM t2; See Type_handler::Column_definition_redefine_stage1() for data type specific code. + + @param this - The field definition corresponding to the expression + in the "AS SELECT.." part. + + @param dup_field - The field definition from the "CREATE TABLE (...)" part. + It has already underwent prepare_stage1(), so + must be fully initialized: + -- dup_field->charset is set and BINARY + sorting style is applied, see find_bin_collation(). + + @param file - The table handler */ void Column_definition::redefine_stage1_common(const Column_definition *dup_field, - const handler *file, - const Schema_specification_st *schema) + const handler *file) { set_handler(dup_field->type_handler()); default_value= dup_field->default_value; - charset= dup_field->charset ? dup_field->charset : - schema->default_table_charset; + DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1() + charset= dup_field->charset; length= dup_field->char_length; pack_length= dup_field->pack_length; key_length= dup_field->key_length; diff --git a/sql/field.h b/sql/field.h index b04e29ac6fb..6cb19d1e238 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4631,6 +4631,11 @@ public: void frm_pack_charset(uchar *buff) const; void frm_unpack_basic(const uchar *buff); bool frm_unpack_charset(TABLE_SHARE *share, const uchar *buff); + CHARSET_INFO *explicit_or_derived_charset(const Column_derived_attributes + *derived_attr) const + { + return charset ? charset : derived_attr->charset(); + } }; @@ -4765,6 +4770,15 @@ public: void create_length_to_internal_length_bit(); void create_length_to_internal_length_newdecimal(); + /* + Prepare the "charset" member for string data types, + such as CHAR, VARCHAR, TEXT, ENUM, SET: + - derive the charset if not specified explicitly + - find a _bin collation if the BINARY comparison style was specified, e.g.: + CREATE TABLE t1 (a VARCHAR(10) BINARY) CHARSET utf8; + */ + bool prepare_charset_for_string(const Column_derived_attributes *dattr); + /** Prepare a SET/ENUM field. Create "interval" from "interval_list" if needed, and adjust "length". @@ -4800,7 +4814,13 @@ public: bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root); bool prepare_stage1(THD *thd, MEM_ROOT *mem_root, - handler *file, ulonglong table_flags); + handler *file, ulonglong table_flags, + const Column_derived_attributes *derived_attr); + void prepare_stage1_simple(CHARSET_INFO *cs) + { + charset= cs; + create_length_to_internal_length_simple(); + } bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root, handler *file, ulonglong table_flags); bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root, @@ -4808,15 +4828,19 @@ public: bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root, handler *file, ulonglong table_flags); + bool bulk_alter(const Column_derived_attributes *derived_attr, + const Column_bulk_alter_attributes *bulk_attr) + { + return type_handler()->Column_definition_bulk_alter(this, + derived_attr, + bulk_attr); + } void redefine_stage1_common(const Column_definition *dup_field, - const handler *file, - const Schema_specification_st *schema); - bool redefine_stage1(const Column_definition *dup_field, const handler *file, - const Schema_specification_st *schema) + const handler *file); + bool redefine_stage1(const Column_definition *dup_field, const handler *file) { const Type_handler *handler= dup_field->type_handler(); - return handler->Column_definition_redefine_stage1(this, dup_field, - file, schema); + return handler->Column_definition_redefine_stage1(this, dup_field, file); } bool prepare_stage2(handler *handler, ulonglong table_flags); bool prepare_stage2_blob(handler *handler, diff --git a/sql/handler.h b/sql/handler.h index 81a18137955..891187db171 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2092,7 +2092,7 @@ public: struct Table_scope_and_contents_source_pod_st // For trivial members { - CHARSET_INFO *table_charset; + CHARSET_INFO *alter_table_convert_to_charset; LEX_CUSTRING tabledef_version; LEX_CSTRING connect_string; LEX_CSTRING comment; @@ -2237,7 +2237,7 @@ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st, DBUG_ASSERT(cs); if (check_conflicting_charset_declarations(cs)) return true; - table_charset= default_table_charset= cs; + alter_table_convert_to_charset= default_table_charset= cs; used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); return false; } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 7ed1eb7aa52..373326c75c6 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2388,6 +2388,8 @@ static int add_column_list_values(String *str, partition_info *part_info, */ if (create_info) { + const Column_derived_attributes + derived_attr(create_info->default_table_charset); Create_field *sql_field; if (!(sql_field= get_sql_field(field_name, @@ -2402,7 +2404,7 @@ static int add_column_list_values(String *str, partition_info *part_info, &need_cs_check)) return 1; if (need_cs_check) - field_cs= get_sql_field_charset(sql_field, create_info); + field_cs= sql_field->explicit_or_derived_charset(&derived_attr); else field_cs= NULL; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ac1984a074c..ea1a35a9ddd 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2926,6 +2926,15 @@ bool check_duplicates_in_interval(const char *set_or_name, } +bool Column_definition:: + prepare_charset_for_string(const Column_derived_attributes *dattr) +{ + if (!charset) + charset= dattr->charset(); + return (flags & BINCMP_FLAG) && !(charset= find_bin_collation(charset)); +} + + bool Column_definition::prepare_stage2_blob(handler *file, ulonglong table_flags, uint field_flags) @@ -3007,38 +3016,6 @@ bool Column_definition::prepare_stage2(handler *file, } -/* - Get character set from field object generated by parser using - default values when not set. - - SYNOPSIS - get_sql_field_charset() - sql_field The sql_field object - create_info Info generated by parser - - RETURN VALUES - cs Character set -*/ - -CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, - HA_CREATE_INFO *create_info) -{ - CHARSET_INFO *cs= sql_field->charset; - - if (!cs) - cs= create_info->default_table_charset; - /* - table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname - if we want change character set for all varchar/char columns. - But the table charset must not affect the BLOB fields, so don't - allow to change my_charset_bin to somethig else. - */ - if (create_info->table_charset && cs != &my_charset_bin) - cs= create_info->table_charset; - return cs; -} - - /** Modifies the first column definition whose SQL type is TIMESTAMP by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. @@ -3211,11 +3188,14 @@ bool Column_definition::prepare_stage1_bit(THD *thd, bool Column_definition::prepare_stage1(THD *thd, MEM_ROOT *mem_root, handler *file, - ulonglong table_flags) + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) { return type_handler()->Column_definition_prepare_stage1(thd, mem_root, this, file, - table_flags); + table_flags, + derived_attr); } @@ -3428,6 +3408,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, int select_field_count= C_CREATE_SELECT(create_table_mode); bool tmp_table= create_table_mode == C_ALTER_TABLE; bool is_hash_field_needed= false; + const Column_derived_attributes dattr(create_info->default_table_charset); + const Column_bulk_alter_attributes + battr(create_info->alter_table_convert_to_charset); DBUG_ENTER("mysql_prepare_create_table"); DBUG_EXECUTE_IF("test_pseudo_invisible",{ @@ -3484,26 +3467,27 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, for (field_no=0; (sql_field=it++) ; field_no++) { + /* Virtual fields are always NULL */ + if (sql_field->vcol_info) + sql_field->flags&= ~NOT_NULL_FLAG; + /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're executing a prepared statement for the second time. */ sql_field->length= sql_field->char_length; - /* Set field charset. */ - sql_field->charset= get_sql_field_charset(sql_field, create_info); - if ((sql_field->flags & BINCMP_FLAG) && - !(sql_field->charset= find_bin_collation(sql_field->charset))) - DBUG_RETURN(true); - /* Virtual fields are always NULL */ - if (sql_field->vcol_info) - sql_field->flags&= ~NOT_NULL_FLAG; + if (sql_field->bulk_alter(&dattr, &battr)) + DBUG_RETURN(true); if (sql_field->prepare_stage1(thd, thd->mem_root, - file, file->ha_table_flags())) + file, file->ha_table_flags(), + &dattr)) DBUG_RETURN(true); + DBUG_ASSERT(sql_field->charset); + if (sql_field->real_field_type() == MYSQL_TYPE_BIT && file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length+= sql_field->length & 7; @@ -3554,7 +3538,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields--; - if (sql_field->redefine_stage1(dup_field, file, create_info)) + if (sql_field->redefine_stage1(dup_field, file)) DBUG_RETURN(true); it2.remove(); // Remove first (create) definition @@ -4569,7 +4553,9 @@ bool Column_definition::prepare_blob_field(THD *thd) bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) { - return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) || + DBUG_ASSERT(charset); + const Column_derived_attributes dattr(&my_charset_bin); + return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY, &dattr) || prepare_stage2(NULL, HA_CAN_GEOMETRY); } @@ -11331,8 +11317,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd) { create_info.used_fields&= ~HA_CREATE_USED_CHARSET; create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; - create_info.default_table_charset= create_info.table_charset; - create_info.table_charset= 0; + create_info.default_table_charset= create_info.alter_table_convert_to_charset; + create_info.alter_table_convert_to_charset= 0; } /* diff --git a/sql/sql_table.h b/sql/sql_table.h index 35bff0873ea..62b61684286 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -252,8 +252,6 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, const char *table_path=0); void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Column_definition *sql_field); -CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, - HA_CREATE_INFO *create_info); bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int write_bin_log(THD *thd, bool clear_error, char const *query, ulong query_length, diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 7dea9cdea4a..e92dc97771e 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2671,9 +2671,12 @@ bool Type_handler:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - def->create_length_to_internal_length_simple(); + def->prepare_stage1_simple(&my_charset_bin); return false; } @@ -2682,8 +2685,12 @@ bool Type_handler_null:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->prepare_charset_for_string(derived_attr); def->create_length_to_internal_length_null(); return false; } @@ -2693,19 +2700,56 @@ bool Type_handler_row:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_bin; def->create_length_to_internal_length_null(); return false; } +bool Type_handler_temporal_result:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const +{ + def->prepare_stage1_simple(&my_charset_numeric); + return false; +} + + +bool Type_handler_numeric:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const +{ + def->prepare_stage1_simple(&my_charset_numeric); + return false; +} + bool Type_handler_newdecimal:: Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_numeric; def->create_length_to_internal_length_newdecimal(); return false; } @@ -2715,8 +2759,12 @@ bool Type_handler_bit:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_numeric; return def->prepare_stage1_bit(thd, mem_root, file, table_flags); } @@ -2725,9 +2773,13 @@ bool Type_handler_typelib:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - return def->prepare_stage1_typelib(thd, mem_root, file, table_flags); + return def->prepare_charset_for_string(derived_attr) || + def->prepare_stage1_typelib(thd, mem_root, file, table_flags); } @@ -2736,20 +2788,31 @@ bool Type_handler_string_result:: MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { - return def->prepare_stage1_string(thd, mem_root, file, table_flags); + return def->prepare_charset_for_string(derived_attr) || + def->prepare_stage1_string(thd, mem_root, file, table_flags); } #ifdef HAVE_SPATIAL +#if MYSQL_VERSION_ID > 100500 +#error The below method is in sql/sql_type_geom.cc starting from 10.5 +#endif bool Type_handler_geometry:: Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, Column_definition *def, handler *file, - ulonglong table_flags) const + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const { + def->charset= &my_charset_bin; def->create_length_to_internal_length_string(); return def->prepare_blob_field(thd); } @@ -2758,14 +2821,38 @@ bool Type_handler_geometry:: /*************************************************************************/ +bool Type_handler_general_purpose_string:: + Column_definition_bulk_alter(Column_definition *def, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const +{ + if (!bulk_alter_attr->alter_table_convert_to_charset()) + return false; // No "CONVERT TO" clause. + CHARSET_INFO *defcs= def->explicit_or_derived_charset(derived_attr); + DBUG_ASSERT(defcs); + /* + Handle 'ALTER TABLE t1 CONVERT TO CHARACTER SET csname'. + Change character sets for all varchar/char/text columns, + but do not touch varbinary/binary/blob columns. + */ + if (defcs != &my_charset_bin) + def->charset= bulk_alter_attr->alter_table_convert_to_charset(); + return false; +}; + + +/*************************************************************************/ + bool Type_handler:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_simple(); return false; } @@ -2774,11 +2861,10 @@ bool Type_handler:: bool Type_handler_null:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_null(); return false; } @@ -2787,11 +2873,10 @@ bool Type_handler_null:: bool Type_handler_newdecimal:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_newdecimal(); return false; } @@ -2800,11 +2885,10 @@ bool Type_handler_newdecimal:: bool Type_handler_string_result:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->set_compression_method(dup->compression_method()); def->create_length_to_internal_length_string(); return false; @@ -2814,11 +2898,10 @@ bool Type_handler_string_result:: bool Type_handler_typelib:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); def->create_length_to_internal_length_typelib(); return false; } @@ -2827,11 +2910,10 @@ bool Type_handler_typelib:: bool Type_handler_bit:: Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { - def->redefine_stage1_common(dup, file, schema); + def->redefine_stage1_common(dup, file); /* If we are replacing a field with a BIT field, we need to initialize pack_flag. diff --git a/sql/sql_type.h b/sql/sql_type.h index 6958d4f970f..7a514643bf6 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -76,7 +76,6 @@ class Spvar_definition; struct st_value; class Protocol; class handler; -struct Schema_specification_st; struct TABLE; struct SORT_FIELD_ATTR; class Vers_history_point; @@ -110,6 +109,53 @@ enum scalar_comparison_op }; +/* + A helper class to store column attributes that are inherited + by columns (from the table level) when not specified explicitly. +*/ +class Column_derived_attributes +{ + /* + Table level CHARACTER SET and COLLATE value: + + CREATE TABLE t1 (a VARCHAR(1), b CHAR(2)) CHARACTER SET latin1; + + All character string columns (CHAR, VARCHAR, TEXT) + inherit CHARACTER SET from the table level. + */ + CHARSET_INFO *m_charset; +public: + explicit Column_derived_attributes(CHARSET_INFO *cs) + :m_charset(cs) + { } + CHARSET_INFO *charset() const { return m_charset; } +}; + + +/* + A helper class to store requests for changes + in multiple column data types during ALTER. +*/ +class Column_bulk_alter_attributes +{ + /* + Target CHARACTER SET specification in ALTER .. CONVERT, e.g. + + ALTER TABLE t1 CONVERT TO CHARACTER SET utf8; + + All character string columns (CHAR, VARCHAR, TEXT) + get converted to the "CONVERT TO CHARACTER SET". + */ + CHARSET_INFO *m_alter_table_convert_to_charset; +public: + explicit Column_bulk_alter_attributes(CHARSET_INFO *convert) + :m_alter_table_convert_to_charset(convert) + { } + CHARSET_INFO *alter_table_convert_to_charset() const + { return m_alter_table_convert_to_charset; } +}; + + class Native: public Binary_string { public: @@ -3597,7 +3643,17 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; + virtual bool Column_definition_bulk_alter(Column_definition *c, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const + { return false; } /* This method is called on queries like: CREATE TABLE t2 (a INT) AS SELECT a FROM t1; @@ -3616,9 +3672,7 @@ public: */ virtual bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st * - schema) + const handler *file) const; virtual bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -4008,11 +4062,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const { DBUG_ASSERT(0); @@ -4294,6 +4350,14 @@ class Type_handler_numeric: public Type_handler { public: String *print_item_value(THD *thd, Item *item, String *str) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; double Item_func_min_max_val_real(Item_func_min_max *) const; longlong Item_func_min_max_val_int(Item_func_min_max *) const; my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, @@ -4734,6 +4798,14 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Item_const_eq(const Item_const *a, const Item_const *b, bool binary_cmp) const; bool Item_param_set_from_value(THD *thd, @@ -4820,11 +4892,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; uint32 max_display_length(const Item *item) const; /* @@ -4935,6 +5009,12 @@ class Type_handler_general_purpose_string: public Type_handler_string_result public: bool is_general_purpose_string_type() const { return true; } bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; + bool Column_definition_bulk_alter(Column_definition *c, + const Column_derived_attributes + *derived_attr, + const Column_bulk_alter_attributes + *bulk_alter_attr) + const; }; @@ -5291,11 +5371,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -5975,11 +6057,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -6021,11 +6105,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, @@ -6348,7 +6434,10 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_prepare_stage2(Column_definition *c, handler *file, ulonglong table_flags) const; @@ -6424,11 +6513,13 @@ public: MEM_ROOT *mem_root, Column_definition *c, handler *file, - ulonglong table_flags) const; + ulonglong table_flags, + const Column_derived_attributes + *derived_attr) + const; bool Column_definition_redefine_stage1(Column_definition *def, const Column_definition *dup, - const handler *file, - const Schema_specification_st *schema) + const handler *file) const; void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const; diff --git a/sql/table.cc b/sql/table.cc index b7c3a33aa40..12299271ab3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4479,7 +4479,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->row_type= share->row_type; create_info->key_block_size= share->key_block_size; create_info->default_table_charset= share->table_charset; - create_info->table_charset= 0; + create_info->alter_table_convert_to_charset= 0; create_info->comment= share->comment; create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index de4e20ee112..19b56980714 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -3419,7 +3419,9 @@ int ha_federatedx::create(const char *name, TABLE *table_arg, { FEDERATEDX_SERVER server; - fill_server(thd->mem_root, &server, &tmp_share, create_info->table_charset); + // It's possibly wrong to use alter_table_convert_to_charset here. + fill_server(thd->mem_root, &server, &tmp_share, + create_info->alter_table_convert_to_charset); #ifndef DBUG_OFF mysql_mutex_init(fe_key_mutex_FEDERATEDX_SERVER_mutex, |