diff --git a/sql/field.cc b/sql/field.cc index 6fa6838e063e0..480fcfb92871e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -9801,6 +9801,84 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root, } +bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::prepare_interval_field"); + DBUG_ASSERT(sql_type == MYSQL_TYPE_ENUM || sql_type == MYSQL_TYPE_SET); + /* + Interval values are either in "interval" or in "interval_list", + but not in both at the same time, and are not empty at the same time. + - Values are in "interval_list" when we're coming from the parser + in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}. + - Values are in "interval" when we're in ALTER TABLE. + + In a corner case with an empty set like SET(''): + - after the parser we have interval_list.elements==1 + - in ALTER TABLE we have a non-NULL interval with interval->count==1, + with interval->type_names[0]=="" and interval->type_lengths[0]==0. + So the assert is still valid for this corner case. + + ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible, + as the parser requires at least one element, so for a ENUM or SET field it + should never happen that both internal_list.elements and interval are 0. + */ + DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0)); + + /* + Create typelib from interval_list, and if necessary + convert strings from client character set to the + column character set. + */ + if (interval_list.elements && + create_interval_from_interval_list(mem_root, + reuse_interval_list_values)) + DBUG_RETURN(true); + + if (!reuse_interval_list_values) + { + /* + We're initializing from an existing table or view Field_enum + (e.g. for a %TYPE variable) rather than from the parser. + The constructor Column_definition(THD*,Field*,Field*) has already + copied the TYPELIB pointer from the original Field_enum. + Now we need to make a permanent copy of that TYPELIB, + as the original field can be freed before the end of the life + cycle of "this". + */ + DBUG_ASSERT(interval); + if (!(interval= copy_typelib(mem_root, interval))) + DBUG_RETURN(true); + } + prepare_interval_field_calc_length(); + DBUG_RETURN(false); +} + + +void Column_definition::set_attributes(const Lex_field_type_st &type, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(sql_type == MYSQL_TYPE_NULL); + DBUG_ASSERT(charset == &my_charset_bin || charset == NULL); + DBUG_ASSERT(length == 0); + DBUG_ASSERT(decimals == 0); + + sql_type= type.field_type(); + charset= cs; + + if (type.length()) + { + int err; + length= my_strtoll10(type.length(), NULL, &err); + if (err) + length= ~0ULL; // safety + } + + if (type.dec()) + decimals= (uint) atoi(type.dec()); +} + + /** Convert create_field::length from number of characters to number of bytes. */ @@ -10540,6 +10618,7 @@ Field *make_field(TABLE_SHARE *share, Column_definition::Column_definition(THD *thd, Field *old_field, Field *orig_field) { + on_update= NULL; field_name= old_field->field_name; length= old_field->field_length; flags= old_field->flags; diff --git a/sql/field.h b/sql/field.h index 76cdfe633198d..7410288fa4862 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3800,9 +3800,10 @@ class Column_definition: public Sql_alloc Column_definition(): comment(null_lex_str), - on_update(0), sql_type(MYSQL_TYPE_NULL), + on_update(NULL), sql_type(MYSQL_TYPE_NULL), length(0), decimals(0), flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), - interval(0), srid(0), geom_type(Field::GEOM_GEOMETRY), + interval(0), charset(&my_charset_bin), + srid(0), geom_type(Field::GEOM_GEOMETRY), option_list(NULL), vcol_info(0), default_value(0), check_constraint(0) { @@ -3810,7 +3811,9 @@ class Column_definition: public Sql_alloc } Column_definition(THD *thd, Field *field, Field *orig_field); + void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); void create_length_to_internal_length(void); + /** Prepare a SET/ENUM field. Create "interval" from "interval_list" if needed, and adjust "length". @@ -3823,39 +3826,10 @@ class Column_definition: public Sql_alloc is not needed */ bool prepare_interval_field(MEM_ROOT *mem_root, - bool reuse_interval_list_values) - { - DBUG_ENTER("Column_definition::prepare_interval_field"); - DBUG_ASSERT(sql_type == MYSQL_TYPE_ENUM || sql_type == MYSQL_TYPE_SET); - /* - Interval values are either in "interval" or in "interval_list", - but not in both at the same time, and are not empty at the same time. - - Values are in "interval_list" when we're coming from the parser - in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}. - - Values are in "interval" when we're in ALTER TABLE. - - In a corner case with an empty set like SET(''): - - after the parser we have interval_list.elements==1 - - in ALTER TABLE we have a non-NULL interval with interval->count==1, - with interval->type_names[0]=="" and interval->type_lengths[0]==0. - So the assert is still valid for this corner case. - - ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible, - as the parser requires at least one element, so for a ENUM or SET field it - should never happen that both internal_list.elements and interval are 0. - */ - DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0)); - - /* - Create typelib from interval_list, and if necessary - convert strings from client character set to the - column character set. - */ - if (interval_list.elements && - create_interval_from_interval_list(mem_root, - reuse_interval_list_values)) - DBUG_RETURN(true); + bool reuse_interval_list_values); + void prepare_interval_field_calc_length() + { uint32 field_length, dummy; if (sql_type == MYSQL_TYPE_SET) { @@ -3868,7 +3842,6 @@ class Column_definition: public Sql_alloc length= field_length; } set_if_smaller(length, MAX_FIELD_WIDTH - 1); - DBUG_RETURN(false); } bool prepare_blob_field(THD *thd); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5591b591debb6..edcbf1cb51bb2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2995,7 +2995,6 @@ struct LEX: public Query_tables_list void restore_set_statement_var(); void init_last_field(Column_definition *field, const char *name, CHARSET_INFO *cs); - void set_last_field_type(const Lex_field_type_st &type); bool set_bincmp(CHARSET_INFO *cs, bool bin); bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f519d458fddc4..f826dbb8dd5c6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -833,23 +833,6 @@ void LEX::init_last_field(Column_definition *field, const char *field_name, charset= cs; } -void LEX::set_last_field_type(const Lex_field_type_st &type) -{ - last_field->sql_type= type.field_type(); - last_field->charset= charset; - - if (type.length()) - { - int err; - last_field->length= my_strtoll10(type.length(), NULL, &err); - if (err) - last_field->length= ~0ULL; // safety - } - else - last_field->length= 0; - - last_field->decimals= type.dec() ? (uint)atoi(type.dec()) : 0; -} bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) { @@ -6109,7 +6092,7 @@ field_spec: lex->init_last_field(f, $1.str, NULL); $$= f; } - field_type { Lex->set_last_field_type($3); } + field_type { Lex->last_field->set_attributes($3, Lex->charset); } field_def { LEX *lex=Lex; @@ -6615,7 +6598,7 @@ type_with_opt_collate: if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) MYSQL_YYABORT; } - Lex->set_last_field_type($1); + Lex->last_field->set_attributes($1, Lex->charset); } ;