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.
This commit is contained in:
Alexander Barkov 2021-03-25 06:55:18 +04:00
parent f69c1c9dcb
commit 58780b5afb
12 changed files with 338 additions and 118 deletions

View File

@ -2032,3 +2032,14 @@ ALTER TABLE t1 MODIFY a VARCHAR(2)
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ('a'); INSERT INTO t1 VALUES ('a');
DROP TABLE t1; 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;

View File

@ -843,3 +843,17 @@ ALTER TABLE t1 MODIFY a VARCHAR(2)
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
INSERT INTO t1 VALUES ('a'); INSERT INTO t1 VALUES ('a');
DROP TABLE t1; 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;

View File

@ -11076,16 +11076,26 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
CREATE TABLE t1 (a INT) AS SELECT a FROM t2; CREATE TABLE t1 (a INT) AS SELECT a FROM t2;
See Type_handler::Column_definition_redefine_stage1() See Type_handler::Column_definition_redefine_stage1()
for data type specific code. 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 void
Column_definition::redefine_stage1_common(const Column_definition *dup_field, Column_definition::redefine_stage1_common(const Column_definition *dup_field,
const handler *file, const handler *file)
const Schema_specification_st *schema)
{ {
set_handler(dup_field->type_handler()); set_handler(dup_field->type_handler());
default_value= dup_field->default_value; default_value= dup_field->default_value;
charset= dup_field->charset ? dup_field->charset : DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
schema->default_table_charset; charset= dup_field->charset;
length= dup_field->char_length; length= dup_field->char_length;
pack_length= dup_field->pack_length; pack_length= dup_field->pack_length;
key_length= dup_field->key_length; key_length= dup_field->key_length;

View File

@ -4631,6 +4631,11 @@ public:
void frm_pack_charset(uchar *buff) const; void frm_pack_charset(uchar *buff) const;
void frm_unpack_basic(const uchar *buff); void frm_unpack_basic(const uchar *buff);
bool frm_unpack_charset(TABLE_SHARE *share, 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_bit();
void create_length_to_internal_length_newdecimal(); 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. Prepare a SET/ENUM field.
Create "interval" from "interval_list" if needed, and adjust "length". 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 sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root);
bool prepare_stage1(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, bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root,
handler *file, ulonglong table_flags); handler *file, ulonglong table_flags);
bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root, 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, bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root,
handler *file, ulonglong table_flags); 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, void redefine_stage1_common(const Column_definition *dup_field,
const handler *file, const handler *file);
const Schema_specification_st *schema); bool redefine_stage1(const Column_definition *dup_field, const handler *file)
bool redefine_stage1(const Column_definition *dup_field, const handler *file,
const Schema_specification_st *schema)
{ {
const Type_handler *handler= dup_field->type_handler(); const Type_handler *handler= dup_field->type_handler();
return handler->Column_definition_redefine_stage1(this, dup_field, return handler->Column_definition_redefine_stage1(this, dup_field, file);
file, schema);
} }
bool prepare_stage2(handler *handler, ulonglong table_flags); bool prepare_stage2(handler *handler, ulonglong table_flags);
bool prepare_stage2_blob(handler *handler, bool prepare_stage2_blob(handler *handler,

View File

@ -2092,7 +2092,7 @@ public:
struct Table_scope_and_contents_source_pod_st // For trivial members 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_CUSTRING tabledef_version;
LEX_CSTRING connect_string; LEX_CSTRING connect_string;
LEX_CSTRING comment; LEX_CSTRING comment;
@ -2237,7 +2237,7 @@ struct HA_CREATE_INFO: public Table_scope_and_contents_source_st,
DBUG_ASSERT(cs); DBUG_ASSERT(cs);
if (check_conflicting_charset_declarations(cs)) if (check_conflicting_charset_declarations(cs))
return true; 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); used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET);
return false; return false;
} }

View File

@ -2388,6 +2388,8 @@ static int add_column_list_values(String *str, partition_info *part_info,
*/ */
if (create_info) if (create_info)
{ {
const Column_derived_attributes
derived_attr(create_info->default_table_charset);
Create_field *sql_field; Create_field *sql_field;
if (!(sql_field= get_sql_field(field_name, 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)) &need_cs_check))
return 1; return 1;
if (need_cs_check) 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 else
field_cs= NULL; field_cs= NULL;
} }

View File

@ -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, bool Column_definition::prepare_stage2_blob(handler *file,
ulonglong table_flags, ulonglong table_flags,
uint field_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 Modifies the first column definition whose SQL type is TIMESTAMP
by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_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, bool Column_definition::prepare_stage1(THD *thd,
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
handler *file, handler *file,
ulonglong table_flags) ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
{ {
return type_handler()->Column_definition_prepare_stage1(thd, mem_root, return type_handler()->Column_definition_prepare_stage1(thd, mem_root,
this, file, 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); int select_field_count= C_CREATE_SELECT(create_table_mode);
bool tmp_table= create_table_mode == C_ALTER_TABLE; bool tmp_table= create_table_mode == C_ALTER_TABLE;
bool is_hash_field_needed= false; 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_ENTER("mysql_prepare_create_table");
DBUG_EXECUTE_IF("test_pseudo_invisible",{ 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++) 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), Initialize length from its original value (number of characters),
which was set in the parser. This is necessary if we're which was set in the parser. This is necessary if we're
executing a prepared statement for the second time. executing a prepared statement for the second time.
*/ */
sql_field->length= sql_field->char_length; 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->bulk_alter(&dattr, &battr))
if (sql_field->vcol_info) DBUG_RETURN(true);
sql_field->flags&= ~NOT_NULL_FLAG;
if (sql_field->prepare_stage1(thd, thd->mem_root, if (sql_field->prepare_stage1(thd, thd->mem_root,
file, file->ha_table_flags())) file, file->ha_table_flags(),
&dattr))
DBUG_RETURN(true); DBUG_RETURN(true);
DBUG_ASSERT(sql_field->charset);
if (sql_field->real_field_type() == MYSQL_TYPE_BIT && if (sql_field->real_field_type() == MYSQL_TYPE_BIT &&
file->ha_table_flags() & HA_CAN_BIT_FIELD) file->ha_table_flags() & HA_CAN_BIT_FIELD)
total_uneven_bit_length+= sql_field->length & 7; 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)) if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields--; null_fields--;
if (sql_field->redefine_stage1(dup_field, file, create_info)) if (sql_field->redefine_stage1(dup_field, file))
DBUG_RETURN(true); DBUG_RETURN(true);
it2.remove(); // Remove first (create) definition 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) 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); 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_CHARSET;
create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
create_info.default_table_charset= create_info.table_charset; create_info.default_table_charset= create_info.alter_table_convert_to_charset;
create_info.table_charset= 0; create_info.alter_table_convert_to_charset= 0;
} }
/* /*

View File

@ -252,8 +252,6 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
const char *table_path=0); const char *table_path=0);
void close_cached_table(THD *thd, TABLE *table); void close_cached_table(THD *thd, TABLE *table);
void sp_prepare_create_field(THD *thd, Column_definition *sql_field); 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); bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int write_bin_log(THD *thd, bool clear_error, int write_bin_log(THD *thd, bool clear_error,
char const *query, ulong query_length, char const *query, ulong query_length,

View File

@ -2671,9 +2671,12 @@ bool Type_handler::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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; return false;
} }
@ -2682,8 +2685,12 @@ bool Type_handler_null::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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(); def->create_length_to_internal_length_null();
return false; return false;
} }
@ -2693,19 +2700,56 @@ bool Type_handler_row::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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(); def->create_length_to_internal_length_null();
return false; 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:: bool Type_handler_newdecimal::
Column_definition_prepare_stage1(THD *thd, Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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(); def->create_length_to_internal_length_newdecimal();
return false; return false;
} }
@ -2715,8 +2759,12 @@ bool Type_handler_bit::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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); return def->prepare_stage1_bit(thd, mem_root, file, table_flags);
} }
@ -2725,9 +2773,13 @@ bool Type_handler_typelib::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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,36 +2788,71 @@ bool Type_handler_string_result::
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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 #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:: bool Type_handler_geometry::
Column_definition_prepare_stage1(THD *thd, Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *def, Column_definition *def,
handler *file, 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(); def->create_length_to_internal_length_string();
return def->prepare_blob_field(thd); return def->prepare_blob_field(thd);
} }
#endif #endif
/*************************************************************************/
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:: bool Type_handler::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
def->redefine_stage1_common(dup, file, schema); def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_simple(); def->create_length_to_internal_length_simple();
return false; return false;
} }
@ -2774,11 +2861,10 @@ bool Type_handler::
bool Type_handler_null:: bool Type_handler_null::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
def->redefine_stage1_common(dup, file, schema); def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_null(); def->create_length_to_internal_length_null();
return false; return false;
} }
@ -2787,11 +2873,10 @@ bool Type_handler_null::
bool Type_handler_newdecimal:: bool Type_handler_newdecimal::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
def->redefine_stage1_common(dup, file, schema); def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_newdecimal(); def->create_length_to_internal_length_newdecimal();
return false; return false;
} }
@ -2800,11 +2885,10 @@ bool Type_handler_newdecimal::
bool Type_handler_string_result:: bool Type_handler_string_result::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
def->redefine_stage1_common(dup, file, schema); def->redefine_stage1_common(dup, file);
def->set_compression_method(dup->compression_method()); def->set_compression_method(dup->compression_method());
def->create_length_to_internal_length_string(); def->create_length_to_internal_length_string();
return false; return false;
@ -2814,11 +2898,10 @@ bool Type_handler_string_result::
bool Type_handler_typelib:: bool Type_handler_typelib::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
def->redefine_stage1_common(dup, file, schema); def->redefine_stage1_common(dup, file);
def->create_length_to_internal_length_typelib(); def->create_length_to_internal_length_typelib();
return false; return false;
} }
@ -2827,11 +2910,10 @@ bool Type_handler_typelib::
bool Type_handler_bit:: bool Type_handler_bit::
Column_definition_redefine_stage1(Column_definition *def, Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const 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 If we are replacing a field with a BIT field, we need
to initialize pack_flag. to initialize pack_flag.

View File

@ -76,7 +76,6 @@ class Spvar_definition;
struct st_value; struct st_value;
class Protocol; class Protocol;
class handler; class handler;
struct Schema_specification_st;
struct TABLE; struct TABLE;
struct SORT_FIELD_ATTR; struct SORT_FIELD_ATTR;
class Vers_history_point; 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 class Native: public Binary_string
{ {
public: public:
@ -3597,7 +3643,17 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, 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: This method is called on queries like:
CREATE TABLE t2 (a INT) AS SELECT a FROM t1; CREATE TABLE t2 (a INT) AS SELECT a FROM t1;
@ -3616,9 +3672,7 @@ public:
*/ */
virtual bool Column_definition_redefine_stage1(Column_definition *def, virtual bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *
schema)
const; const;
virtual bool Column_definition_prepare_stage2(Column_definition *c, virtual bool Column_definition_prepare_stage2(Column_definition *c,
handler *file, handler *file,
@ -4008,11 +4062,13 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const const
{ {
DBUG_ASSERT(0); DBUG_ASSERT(0);
@ -4294,6 +4350,14 @@ class Type_handler_numeric: public Type_handler
{ {
public: public:
String *print_item_value(THD *thd, Item *item, String *str) const; 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; double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(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 *, my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
@ -4734,6 +4798,14 @@ public:
void sortlength(THD *thd, void sortlength(THD *thd,
const Type_std_attributes *item, const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const; 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 Item_const_eq(const Item_const *a, const Item_const *b,
bool binary_cmp) const; bool binary_cmp) const;
bool Item_param_set_from_value(THD *thd, bool Item_param_set_from_value(THD *thd,
@ -4820,11 +4892,13 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const; const;
uint32 max_display_length(const Item *item) 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: public:
bool is_general_purpose_string_type() const { return true; } bool is_general_purpose_string_type() const { return true; }
bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const; 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, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const; const;
bool Column_definition_prepare_stage2(Column_definition *c, bool Column_definition_prepare_stage2(Column_definition *c,
handler *file, handler *file,
@ -5975,11 +6057,13 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const; const;
bool Column_definition_prepare_stage2(Column_definition *c, bool Column_definition_prepare_stage2(Column_definition *c,
handler *file, handler *file,
@ -6021,11 +6105,13 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const; const;
bool Column_definition_prepare_stage2(Column_definition *c, bool Column_definition_prepare_stage2(Column_definition *c,
handler *file, handler *file,
@ -6348,7 +6434,10 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_prepare_stage2(Column_definition *c, bool Column_definition_prepare_stage2(Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags) const;
@ -6424,11 +6513,13 @@ public:
MEM_ROOT *mem_root, MEM_ROOT *mem_root,
Column_definition *c, Column_definition *c,
handler *file, handler *file,
ulonglong table_flags) const; ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const;
bool Column_definition_redefine_stage1(Column_definition *def, bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup, const Column_definition *dup,
const handler *file, const handler *file)
const Schema_specification_st *schema)
const; const;
void Item_param_set_param_func(Item_param *param, void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const; uchar **pos, ulong len) const;

View File

@ -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->row_type= share->row_type;
create_info->key_block_size= share->key_block_size; create_info->key_block_size= share->key_block_size;
create_info->default_table_charset= share->table_charset; 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->comment= share->comment;
create_info->transactional= share->transactional; create_info->transactional= share->transactional;
create_info->page_checksum= share->page_checksum; create_info->page_checksum= share->page_checksum;

View File

@ -3419,7 +3419,9 @@ int ha_federatedx::create(const char *name, TABLE *table_arg,
{ {
FEDERATEDX_SERVER server; 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 #ifndef DBUG_OFF
mysql_mutex_init(fe_key_mutex_FEDERATEDX_SERVER_mutex, mysql_mutex_init(fe_key_mutex_FEDERATEDX_SERVER_mutex,