From 937033f7b3e706d5f93f94ff03ec80b08dd5b2b2 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 17 Jun 2019 16:54:47 +0300 Subject: NFC: refactor Field::is_equal() and related stuff Make Field::is_equal() const and return bool as it's a naturally fitting type for it. Also it's agrument was narrowed to Column_definition. InnoDB can change type of some columns by itself. InnoDB-specific code used to reside in Field_xxx:is_equal() methods. Now engine-specific stuff was moved to a virtual methods of handler::can_convert{string,varstring,blob,geom}. These methods are called by Field::can_be_converted_by_engine() which is a double dispatch pattern. Some InnoDB-specific code still resides in compare_keys_but_name(). It should be moved from here someday to handler::compare_key_parts(...) or similar. IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE: both was removed IS_EQUAL_NO, IS_EQUAL_YES are not needed now and should be removed along with deprecated handler::check_if_incompatible_data(). HA_EXTENDED_TYPES_CONVERSION: was removed as such logic is not needed now by server code. ALTER_COLUMN_EQUAL_PACK_LENGTH: was renamed to a more generic ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE --- sql/field.cc | 187 +++++++-------------- sql/field.h | 58 ++++--- sql/handler.cc | 4 +- sql/handler.h | 45 +++-- sql/sql_priv.h | 8 - sql/sql_table.cc | 141 ++++++---------- sql/sql_type.cc | 1 - storage/connect/ha_connect.cc | 4 +- .../connect/mysql-test/connect/r/xml2_mult.result | 2 + .../connect/mysql-test/connect/r/xml_mult.result | 2 + storage/innobase/handler/ha_innodb.cc | 146 +++++++++++++++- storage/innobase/handler/ha_innodb.h | 7 + storage/innobase/handler/handler0alter.cc | 8 +- storage/tokudb/ha_tokudb.h | 3 + storage/tokudb/ha_tokudb_alter_56.cc | 16 +- 15 files changed, 355 insertions(+), 277 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 761e2c66641..621f0136bea 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2449,12 +2449,12 @@ void Field_null::sql_type(String &res) const } -uint Field_null::is_equal(Create_field *new_field) +bool Field_null::is_equal(const Column_definition &new_field) const { DBUG_ASSERT(!compression_method()); - return new_field->type_handler() == type_handler() && - new_field->charset == field_charset && - new_field->length == max_display_length(); + return new_field.type_handler() == type_handler() && + new_field.charset == field_charset && + new_field.length == max_display_length(); } @@ -3492,15 +3492,15 @@ bool Field_new_decimal::compatible_field_size(uint field_metadata, } -uint Field_new_decimal::is_equal(Create_field *new_field) +bool Field_new_decimal::is_equal(const Column_definition &new_field) const { - return ((new_field->type_handler() == type_handler()) && - ((new_field->flags & UNSIGNED_FLAG) == + return ((new_field.type_handler() == type_handler()) && + ((new_field.flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && - ((new_field->flags & AUTO_INCREMENT_FLAG) <= + ((new_field.flags & AUTO_INCREMENT_FLAG) <= (uint) (flags & AUTO_INCREMENT_FLAG)) && - (new_field->length == max_display_length()) && - (new_field->decimals == dec)); + (new_field.length == max_display_length()) && + (new_field.decimals == dec)); } @@ -5585,10 +5585,10 @@ bool Field_timestampf::val_native(Native *to) /*************************************************************/ -uint Field_temporal::is_equal(Create_field *new_field) +bool Field_temporal::is_equal(const Column_definition &new_field) const { - return new_field->type_handler() == type_handler() && - new_field->length == max_display_length(); + return new_field.type_handler() == type_handler() && + new_field.length == max_display_length(); } @@ -7073,40 +7073,13 @@ int Field_str::store(double nr) return store(buff, (uint)length, &my_charset_numeric); } -uint Field_longstr:: - is_equal_for_different_charsets(const Column_definition &to) const -{ - Charset field_cs(field_charset); - if (!field_cs.encoding_allows_reinterpret_as(to.charset)) - return IS_EQUAL_NO; - - if (!field_cs.eq_collation_specific_names(to.charset)) - return IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE; - - return IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET; -} - -uint Field_string::is_equal(Create_field *new_field) +bool Field_string::is_equal(const Column_definition &new_field) const { DBUG_ASSERT(!compression_method()); - if (new_field->type_handler() != type_handler()) - return IS_EQUAL_NO; - if (new_field->char_length < char_length()) - return IS_EQUAL_NO; - - if (new_field->charset != field_charset) - { - if (new_field->length != max_display_length() && - table->file->ha_table_flags() & HA_EXTENDED_TYPES_CONVERSION) - return IS_EQUAL_NO; - - return is_equal_for_different_charsets(*new_field); - } - - if (new_field->length != max_display_length()) - return IS_EQUAL_NO; - - return IS_EQUAL_YES; + return new_field.type_handler() == type_handler() && + new_field.char_length == char_length() && + new_field.charset == field_charset && + new_field.length == max_display_length(); } @@ -7940,48 +7913,13 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, return res; } -/* - This check is InnoDB specific. ROW_FORMAT=REDUNDANT always allows such - enlargement. But other row formats can do this only for particular current - and new lengths. This is because InnoDB stores VARCHAR length in one or two - bytes. -*/ -static bool supports_such_enlargement(const Field *field, - const Create_field *new_field) +bool Field_varstring::is_equal(const Column_definition &new_field) const { - return field->field_length <= 127 || new_field->length <= 255 || - field->field_length > 255 || - (field->table->file->ha_table_flags() & HA_EXTENDED_TYPES_CONVERSION); -} - -uint Field_varstring::is_equal(Create_field *new_field) -{ - if (new_field->length < field_length) - return IS_EQUAL_NO; - if (new_field->char_length < char_length()) - return IS_EQUAL_NO; - if (!new_field->compression_method() != !compression_method()) - return IS_EQUAL_NO; - if (new_field->type_handler() != type_handler()) - return IS_EQUAL_NO; - - if (new_field->charset != field_charset) - { - if (!supports_such_enlargement(this, new_field)) - return IS_EQUAL_NO; - - return is_equal_for_different_charsets(*new_field); - } - - if (new_field->length != field_length) - { - if (!supports_such_enlargement(this, new_field)) - return IS_EQUAL_NO; - - return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer length - } - - return IS_EQUAL_YES; + return new_field.type_handler() == type_handler() && + new_field.length == field_length && + new_field.char_length == char_length() && + !new_field.compression_method() == !compression_method() && + new_field.charset == field_charset; } @@ -8745,21 +8683,12 @@ uint Field_blob::max_packed_col_length(uint max_length) again an already compressed field just because compression method changes. */ -uint Field_blob::is_equal(Create_field *new_field) +bool Field_blob::is_equal(const Column_definition &new_field) const { - if (new_field->type_handler() != type_handler()) - return IS_EQUAL_NO; - - if (!new_field->compression_method() != !compression_method()) - return IS_EQUAL_NO; - - if (new_field->pack_length != pack_length()) - return IS_EQUAL_NO; - - if (field_charset != new_field->charset) - return is_equal_for_different_charsets(*new_field); - - return IS_EQUAL_YES; + return new_field.type_handler() == type_handler() && + !new_field.compression_method() == !compression_method() && + new_field.pack_length == pack_length() && + new_field.charset == field_charset; } @@ -9068,16 +8997,16 @@ Field::geometry_type Field_geom::geometry_type_merge(geometry_type a, } -uint Field_geom::is_equal(Create_field *new_field) +bool Field_geom::is_equal(const Column_definition &new_field) const { - return new_field->type_handler() == type_handler() && + return new_field.type_handler() == type_handler() && /* - Allow ALTER..INPLACE to supertype (GEOMETRY), e.g. POINT to GEOMETRY or POLYGON to GEOMETRY. - Allow ALTER..INPLACE to the same geometry type: POINT -> POINT */ - (new_field->geom_type == geom_type || - new_field->geom_type == GEOM_GEOMETRY); + (new_field.geom_type == geom_type || + new_field.geom_type == GEOM_GEOMETRY); } @@ -9507,22 +9436,22 @@ bool Field_enum::eq_def(const Field *field) const alteration purposes. Fields are equal if they retain the same pack length and if new members are added to the end of the list. - @return IS_EQUAL_YES if fields are compatible. - IS_EQUAL_NO otherwise. + @return true if fields are compatible. + false otherwise. */ -uint Field_enum::is_equal(Create_field *new_field) +bool Field_enum::is_equal(const Column_definition &new_field) const { - TYPELIB *values= new_field->interval; + TYPELIB *values= new_field.interval; /* The fields are compatible if they have the same flags, type, charset and have the same underlying length. */ - if (new_field->type_handler() != type_handler() || - new_field->charset != field_charset || - new_field->pack_length != pack_length()) - return IS_EQUAL_NO; + if (new_field.type_handler() != type_handler() || + new_field.charset != field_charset || + new_field.pack_length != pack_length()) + return false; /* Changing the definition of an ENUM or SET column by adding a new @@ -9530,13 +9459,13 @@ uint Field_enum::is_equal(Create_field *new_field) values only alters table metadata and not table data. */ if (typelib->count > values->count) - return IS_EQUAL_NO; + return false; /* Check whether there are modification before the end. */ - if (! compare_type_names(field_charset, typelib, new_field->interval)) - return IS_EQUAL_NO; + if (! compare_type_names(field_charset, typelib, new_field.interval)) + return false; - return IS_EQUAL_YES; + return true; } @@ -9582,17 +9511,17 @@ bool Field_num::eq_def(const Field *field) const and retain the same pack length. */ -uint Field_num::is_equal(Create_field *new_field) +bool Field_num::is_equal(const Column_definition &new_field) const { - if (((new_field->flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) || - ((new_field->flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG))) - return IS_EQUAL_NO; + if (((new_field.flags & UNSIGNED_FLAG) != (flags & UNSIGNED_FLAG)) || + ((new_field.flags & AUTO_INCREMENT_FLAG) > (flags & AUTO_INCREMENT_FLAG))) + return false; - const Type_handler *th= type_handler(), *new_th = new_field->type_handler(); + const Type_handler *th= type_handler(), *new_th = new_field.type_handler(); - if (th == new_th && new_field->pack_length == pack_length()) - return IS_EQUAL_YES; - /* FIXME: Test and consider returning IS_EQUAL_YES for the following: + if (th == new_th && new_field.pack_length == pack_length()) + return true; + /* FIXME: Test and consider returning true for the following: TINYINT UNSIGNED to BIT(8) SMALLINT UNSIGNED to BIT(16) MEDIUMINT UNSIGNED to BIT(24) @@ -9607,10 +9536,10 @@ uint Field_num::is_equal(Create_field *new_field) Note: InnoDB stores integers in big-endian format, and BIT appears to use big-endian format. For storage engines that use little-endian - format for integers, we can only return IS_EQUAL_YES for the TINYINT + format for integers, we can only return true for the TINYINT conversion. */ - return IS_EQUAL_NO; + return false; } @@ -9757,10 +9686,10 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table, } -uint Field_bit::is_equal(Create_field *new_field) +bool Field_bit::is_equal(const Column_definition &new_field) const { - return new_field->type_handler() == type_handler() && - new_field->length == max_display_length(); + return new_field.type_handler() == type_handler() && + new_field.length == max_display_length(); } diff --git a/sql/field.h b/sql/field.h index 8c46633d550..0af2150adfa 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1503,13 +1503,16 @@ public: /* maximum possible display length */ virtual uint32 max_display_length() const= 0; /** - Whether a field being created is compatible with a existing one. - - Used by the ALTER TABLE code to evaluate whether the new definition - of a table is compatible with the old definition so that it can - determine if data needs to be copied over (table data change). + Whether a field being created has the samle type. + Used by the ALTER TABLE */ - virtual uint is_equal(Create_field *new_field)= 0; + virtual bool is_equal(const Column_definition &new_field) const= 0; + // Used as double dispatch pattern: calls virtual method of handler + virtual bool + can_be_converted_by_engine(const Column_definition &new_type) const + { + return false; + } /* convert decimal to longlong with overflow check */ longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, int *err); @@ -1809,7 +1812,7 @@ public: !((flags & UNSIGNED_FLAG) && !(from->flags & UNSIGNED_FLAG)) && decimals() == from->decimals(); } - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; uint row_pack_length() const { return pack_length(); } uint32 pack_length_from_metadata(uint field_metadata) { uint32 length= pack_length(); @@ -1874,7 +1877,7 @@ public: bool val_bool() { return val_real() != 0e0; } virtual bool str_needs_quotes() { return TRUE; } bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); } - virtual uint length_size() { return 0; } + virtual uint length_size() const { return 0; } double pos_in_interval(Field *min, Field *max) { return pos_in_interval_val_str(min, max, length_size()); @@ -1928,8 +1931,6 @@ protected: CHARSET_INFO *cs, size_t nchars); String *uncompress(String *val_buffer, String *val_ptr, const uchar *from, uint from_length); - uint is_equal_for_different_charsets(const Column_definition &to) const; - public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -2142,7 +2143,7 @@ public: uint row_pack_length() const { return pack_length(); } bool compatible_field_size(uint field_metadata, Relay_log_info *rli, uint16 mflags, int *order_var); - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data); Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); }; @@ -2646,7 +2647,7 @@ public: my_decimal *val_decimal(my_decimal *) { return 0; } String *val_str(String *value,String *value2) { value2->length(0); return value2;} - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; int cmp(const uchar *a, const uchar *b) { return 0;} void sort_string(uchar *buff, uint length) {} uint32 pack_length() const { return 0; } @@ -2729,7 +2730,7 @@ public: CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } bool binary() const { return true; } bool val_bool() { return val_real() != 0e0; } - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; bool eq_def(const Field *field) const { return (Field::eq_def(field) && decimals() == field->decimals()); @@ -3570,7 +3571,11 @@ public: int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); void sql_type(String &str) const; - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; + bool can_be_converted_by_engine(const Column_definition &new_type) const + { + return table->file->can_convert_string(this, new_type); + } virtual uchar *pack(uchar *to, const uchar *from, uint max_length); virtual const uchar *unpack(uchar* to, const uchar *from, @@ -3698,9 +3703,13 @@ public: Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uint32 length, uchar *new_null_ptr, uint new_null_bit); - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; + bool can_be_converted_by_engine(const Column_definition &new_type) const + { + return table->file->can_convert_varstring(this, new_type); + } void hash(ulong *nr, ulong *nr2); - uint length_size() { return length_bytes; } + uint length_size() const { return length_bytes; } void print_key_value(String *out, uint32 length); private: int save_field_metadata(uchar *first_byte); @@ -4039,7 +4048,11 @@ public: uint32 max_display_length() const; uint32 char_length() const; uint32 character_octet_length() const; - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; + bool can_be_converted_by_engine(const Column_definition &new_type) const + { + return table->file->can_convert_blob(this, new_type); + } void print_key_value(String *out, uint32 length); friend void TABLE::remember_blob_values(String *blob_storage); @@ -4154,7 +4167,12 @@ public: geom_type == static_cast(from)->geom_type) && !table->copy_blobs; } - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; + bool can_be_converted_by_engine(const Column_definition &new_type) const + { + return table->file->can_convert_geom(this, new_type); + } + int store(const char *to, size_t length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); @@ -4298,7 +4316,7 @@ public: bool is_eq_func) const; private: int save_field_metadata(uchar *first_byte); - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; }; @@ -4477,7 +4495,7 @@ public: bit_ptr == ((Field_bit *)field)->bit_ptr && bit_ofs == ((Field_bit *)field)->bit_ofs); } - uint is_equal(Create_field *new_field); + bool is_equal(const Column_definition &new_field) const; void move_field_offset(my_ptrdiff_t ptr_diff) { Field::move_field_offset(ptr_diff); diff --git a/sql/handler.cc b/sql/handler.cc index c8866c2dcc8..0b8d881a0a7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4593,7 +4593,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table, DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); alter_table_operations inplace_offline_operations= - ALTER_COLUMN_EQUAL_PACK_LENGTH | + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_NAME | ALTER_RENAME_COLUMN | ALTER_CHANGE_COLUMN_DEFAULT | @@ -4629,7 +4629,7 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table, DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); uint table_changes= (ha_alter_info->handler_flags & - ALTER_COLUMN_EQUAL_PACK_LENGTH) ? + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) ? IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES; if (table->file->check_if_incompatible_data(create_info, table_changes) == COMPATIBLE_DATA_YES) diff --git a/sql/handler.h b/sql/handler.h index 94e7ddef5b1..e144cbc6642 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -49,6 +49,11 @@ class Alter_info; class Virtual_column_info; class sequence_definition; class Rowid_filter; +class Field_string; +class Field_varstring; +class Field_blob; +class Field_geom; +class Column_definition; // the following is for checking tables @@ -321,10 +326,6 @@ enum enum_alter_inplace_result { /* Safe for online backup */ #define HA_CAN_ONLINE_BACKUPS (1ULL << 56) -/** whether every data field explicitly stores length -(holds for InnoDB ROW_FORMAT=REDUNDANT) */ -#define HA_EXTENDED_TYPES_CONVERSION (1ULL << 57) - /* Support native hash index */ #define HA_CAN_HASH_KEYS (1ULL << 58) #define HA_LAST_TABLE_FLAG HA_CAN_HASH_KEYS @@ -706,13 +707,9 @@ typedef ulonglong alter_table_operations; #define ALTER_VIRTUAL_COLUMN_TYPE (1ULL << 47) #define ALTER_STORED_COLUMN_TYPE (1ULL << 48) -/** - Change column datatype in such way that new type has compatible - packed representation with old type, so it is theoretically - possible to perform change by only updating data dictionary - without changing table rows. -*/ -#define ALTER_COLUMN_EQUAL_PACK_LENGTH (1ULL << 49) + +// Engine can handle type change by itself in ALGORITHM=INPLACE +#define ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE (1ULL << 49) // Reorder column #define ALTER_STORED_COLUMN_ORDER (1ULL << 50) @@ -4815,6 +4812,32 @@ public: virtual bool is_clustering_key(uint index) { return false; } + /** + Some engines can perform column type conversion with ALGORITHM=INPLACE. + These functions check for such possibility. + Implementation could be based on Field_xxx::is_equal() + */ + virtual bool can_convert_string(const Field_string *field, + const Column_definition &new_type) const + { + return false; + } + virtual bool can_convert_varstring(const Field_varstring *field, + const Column_definition &new_type) const + { + return false; + } + virtual bool can_convert_blob(const Field_blob *field, + const Column_definition &new_type) const + { + return false; + } + virtual bool can_convert_geom(const Field_geom *field, + const Column_definition &new_type) const + { + return false; + } + protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 0b06985f431..0d1c9881c17 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -354,14 +354,6 @@ data dictionary without changing table rows */ #define IS_EQUAL_PACK_LENGTH 2 -/** - charsets are the same or compatible, collates are the same, the rest is equal -*/ -#define IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET 3 -/** - charsets are the same or compatible, collates are different, the rest is equal -*/ -#define IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE 4 enum enum_parsing_place { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b6cc3a4290e..e93c3cede55 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6560,27 +6560,28 @@ Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key, if the user key part length is different. */ const Field *old_field= table->field[key_part->fieldnr - 1]; - auto old_field_len= old_field->pack_length(); - if (old_field->type() == MYSQL_TYPE_VARCHAR) + bool is_equal= key_part->field->is_equal(*new_field); + /* TODO: below is an InnoDB specific code which should be moved to InnoDB */ + if (!is_equal) { - old_field_len= (old_field->pack_length() - - ((Field_varstring *) old_field)->length_bytes); + if (!key_part->field->can_be_converted_by_engine(*new_field)) + return Compare_keys::NotEqual; + + if (!Charset(old_field->charset()) + .eq_collation_specific_names(new_field->charset)) + return Compare_keys::NotEqual; } - uint is_equal= key_part->field->is_equal(new_field); - if (key_part->length == old_field_len && - key_part->length < new_part->length && - (is_equal == IS_EQUAL_PACK_LENGTH || - is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET)) + if (key_part->length != new_part->length) { + if (key_part->length != old_field->field_length || + key_part->length >= new_part->length || is_equal) + { + return Compare_keys::NotEqual; + } result= Compare_keys::EqualButKeyPartLength; } - else if (key_part->length != new_part->length) - return Compare_keys::NotEqual; - - if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE) - return Compare_keys::NotEqual; } /* @@ -6602,40 +6603,6 @@ Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key, return result; } -/** - Change Field::is_equal() result depending on field being a part of some index. -*/ -static uint process_is_equal_result_for_key_parts(uint is_equal, - const Field *old_field, - const Create_field *new_field) -{ - if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET) - return IS_EQUAL_PACK_LENGTH; - - if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE) - { - bool part_of_a_key= !new_field->field->part_of_key.is_clear_all(); - - if (part_of_a_key) - { - const TABLE_SHARE *s= new_field->field->table->s; - - bool part_of_a_primary_key= - s->primary_key != MAX_KEY && - new_field->field->part_of_key.is_set(s->primary_key); - - if (part_of_a_primary_key) - return IS_EQUAL_NO; - - return IS_EQUAL_PACK_LENGTH; - } - - return IS_EQUAL_PACK_LENGTH; - } - - return is_equal; -} - /** Compare original and new versions of a table and fill Alter_inplace_info describing differences between those versions. @@ -6770,61 +6737,50 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, /* Field is not dropped. Evaluate changes bitmap for it. */ /* - Check if type of column has changed to some incompatible type. + Check if type of column has changed. */ - uint is_equal= field->is_equal(new_field); - switch (process_is_equal_result_for_key_parts(is_equal, field, new_field)) + bool is_equal= field->is_equal(*new_field); + if (!is_equal) { - case IS_EQUAL_NO: - /* New column type is incompatible with old one. */ - if (field->stored_in_db()) - ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE; + if (field->can_be_converted_by_engine(*new_field)) + { + /* + New column type differs from the old one, but storage engine can + change it by itself. + (for example, VARCHAR(300) is changed to VARCHAR(400)). + */ + ha_alter_info->handler_flags|= ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE; + } else - ha_alter_info->handler_flags|= ALTER_VIRTUAL_COLUMN_TYPE; - if (table->s->tmp_table == NO_TMP_TABLE) { - delete_statistics_for_column(thd, table, field); - KEY *key_info= table->key_info; - for (uint i=0; i < table->s->keys; i++, key_info++) + /* New column type is incompatible with old one. */ + ha_alter_info->handler_flags|= field->stored_in_db() + ? ALTER_STORED_COLUMN_TYPE + : ALTER_VIRTUAL_COLUMN_TYPE; + + if (table->s->tmp_table == NO_TMP_TABLE) { - if (field->part_of_key.is_set(i)) + delete_statistics_for_column(thd, table, field); + KEY *key_info= table->key_info; + for (uint i= 0; i < table->s->keys; i++, key_info++) { + if (!field->part_of_key.is_set(i)) + continue; + uint key_parts= table->actual_n_key_parts(key_info); for (uint j= 0; j < key_parts; j++) { - if (key_info->key_part[j].fieldnr-1 == field->field_index) + if (key_info->key_part[j].fieldnr - 1 == field->field_index) { - delete_statistics_for_index(thd, table, key_info, - j >= key_info->user_defined_key_parts); + delete_statistics_for_index( + thd, table, key_info, + j >= key_info->user_defined_key_parts); break; } - } + } } - } + } } - break; - case IS_EQUAL_YES: - /* - New column is the same as the old one or the fully compatible with - it (for example, ENUM('a','b') was changed to ENUM('a','b','c')). - Such a change if any can ALWAYS be carried out by simply updating - data-dictionary without even informing storage engine. - No flag is set in this case. - */ - break; - case IS_EQUAL_PACK_LENGTH: - /* - New column type differs from the old one, but has compatible packed - data representation. Depending on storage engine, such a change can - be carried out by simply updating data dictionary without changing - actual data (for example, VARCHAR(300) is changed to VARCHAR(400)). - */ - ha_alter_info->handler_flags|= ALTER_COLUMN_EQUAL_PACK_LENGTH; - break; - default: - DBUG_ASSERT(0); - /* Safety. */ - ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE; } if (field->vcol_info || new_field->vcol_info) @@ -6835,7 +6791,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, ALTER_VIRTUAL_COLUMN_TYPE); if (field->vcol_info && new_field->vcol_info) { - bool value_changes= is_equal == IS_EQUAL_NO; + bool value_changes= !is_equal; alter_table_operations alter_expr; if (field->stored_in_db()) alter_expr= ALTER_STORED_GCOL_EXPR; @@ -6929,7 +6885,6 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= new_field->option_struct; } - } else { @@ -7294,7 +7249,7 @@ bool mysql_compare_tables(TABLE *table, DBUG_RETURN(false); /* Evaluate changes bitmap and send to check_if_incompatible_data() */ - uint field_changes= field->is_equal(tmp_new_field); + uint field_changes= field->is_equal(*tmp_new_field); if (field_changes != IS_EQUAL_YES) DBUG_RETURN(false); @@ -8797,7 +8752,7 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info, return FK_COLUMN_RENAMED; } - if ((old_field->is_equal(new_field) == IS_EQUAL_NO) || + if ((old_field->is_equal(*new_field) == IS_EQUAL_NO) || ((new_field->flags & NOT_NULL_FLAG) && !(old_field->flags & NOT_NULL_FLAG))) { diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 446e753e006..2e3bd5f956f 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -8367,6 +8367,5 @@ Charset::eq_collation_specific_names(CHARSET_INFO *cs) const { LEX_CSTRING name0= collation_specific_name(); LEX_CSTRING name1= Charset(cs).collation_specific_name(); - /* Empty collations are not equal */ return name0.length && !cmp(&name0, &name1); } diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index b746bb3d962..9a154cf1af2 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -7018,7 +7018,7 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table, ALTER_DROP_PK_INDEX; alter_table_operations inplace_offline_operations= - ALTER_COLUMN_EQUAL_PACK_LENGTH | + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_NAME | ALTER_COLUMN_DEFAULT | ALTER_CHANGE_CREATE_OPTION | @@ -7117,7 +7117,7 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table, #if 0 uint table_changes= (ha_alter_info->handler_flags & - ALTER_COLUMN_EQUAL_PACK_LENGTH) ? + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) ? IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES; if (table->file->check_if_incompatible_data(create_info, table_changes) diff --git a/storage/connect/mysql-test/connect/r/xml2_mult.result b/storage/connect/mysql-test/connect/r/xml2_mult.result index 87d1118edd5..07c86d961e1 100644 --- a/storage/connect/mysql-test/connect/r/xml2_mult.result +++ b/storage/connect/mysql-test/connect/r/xml2_mult.result @@ -74,6 +74,8 @@ Warnings: Warning 1105 Truncated author content # increase author size ALTER TABLE bookstore MODIFY `author` VARCHAR(128) NOT NULL; +Warnings: +Warning 1105 This is an outward table, table data were not modified. SELECT * FROM bookstore; category title lang author year price COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 diff --git a/storage/connect/mysql-test/connect/r/xml_mult.result b/storage/connect/mysql-test/connect/r/xml_mult.result index 9922b40060c..c786a80819c 100644 --- a/storage/connect/mysql-test/connect/r/xml_mult.result +++ b/storage/connect/mysql-test/connect/r/xml_mult.result @@ -72,6 +72,8 @@ Warnings: Warning 1105 Truncated author content # increase author size ALTER TABLE bookstore MODIFY `author` VARCHAR(128) NOT NULL; +Warnings: +Warning 1105 This is an outward table, table data were not modified. SELECT * FROM bookstore; category title lang author year price COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 88ea6f5e642..107fcac762a 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -54,6 +54,7 @@ this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "field.h" // MYSQL_PLUGIN_IMPORT extern my_bool lower_case_file_system; // MYSQL_PLUGIN_IMPORT extern char mysql_unpacked_real_data_home[]; @@ -6141,11 +6142,6 @@ no_such_table: DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); } - if (!ib_table->not_redundant()) { - m_int_table_flags |= HA_EXTENDED_TYPES_CONVERSION; - cached_table_flags |= HA_EXTENDED_TYPES_CONVERSION; - } - size_t n_fields = omits_virtual_cols(*table_share) ? table_share->stored_fields : table_share->fields; size_t n_cols = dict_table_get_n_user_cols(ib_table) @@ -20875,6 +20871,146 @@ bool ha_innobase::rowid_filter_push(Rowid_filter* pk_filter) DBUG_RETURN(false); } +static bool +is_part_of_a_primary_key(const Field* field) +{ + const TABLE_SHARE* s = field->table->s; + + return s->primary_key != MAX_KEY + && field->part_of_key.is_set(s->primary_key); +} + +bool +ha_innobase::can_convert_string(const Field_string* field, + const Column_definition& new_type) const +{ + DBUG_ASSERT(!field->compression_method()); + if (new_type.type_handler() != field->type_handler()) { + return false; + } + + if (new_type.char_length < field->char_length()) { + return false; + } + + if (new_type.charset != field->charset()) { + if (new_type.length != field->max_display_length() + && !m_prebuilt->table->not_redundant()) { + return IS_EQUAL_NO; + } + + Charset field_cs(field->charset()); + if (!field_cs.encoding_allows_reinterpret_as( + new_type.charset)) { + return false; + } + + if (!field_cs.eq_collation_specific_names(new_type.charset)) { + return !is_part_of_a_primary_key(field); + } + + return true; + } + + if (new_type.length != field->max_display_length()) { + return false; + } + + return true; +} + +static bool +supports_enlarging(const dict_table_t* table, const Field_varstring* field, + const Column_definition& new_type) +{ + return field->field_length <= 127 || new_type.length <= 255 + || field->field_length > 255 || !table->not_redundant(); +} + +bool +ha_innobase::can_convert_varstring(const Field_varstring* field, + const Column_definition& new_type) const +{ + if (new_type.length < field->field_length) { + return false; + } + + if (new_type.char_length < field->char_length()) { + return false; + } + + if (!new_type.compression_method() != !field->compression_method()) { + return false; + } + + if (new_type.type_handler() != field->type_handler()) { + return false; + } + + if (new_type.charset != field->charset()) { + if (!supports_enlarging(m_prebuilt->table, field, new_type)) { + return false; + } + + Charset field_cs(field->charset()); + if (!field_cs.encoding_allows_reinterpret_as( + new_type.charset)) { + return false; + } + + if (!field_cs.eq_collation_specific_names(new_type.charset)) { + return !is_part_of_a_primary_key(field); + } + + return true; + } + + if (new_type.length != field->field_length) { + if (!supports_enlarging(m_prebuilt->table, field, new_type)) { + return false; + } + + return true; + } + + return true; +} + +bool +ha_innobase::can_convert_blob(const Field_blob* field, + const Column_definition& new_type) const +{ + if (new_type.type_handler() != field->type_handler()) { + return false; + } + + if (!new_type.compression_method() != !field->compression_method()) { + return false; + } + + if (new_type.pack_length != field->pack_length()) { + return false; + } + + if (new_type.charset != field->charset()) { + Charset field_cs(field->charset()); + if (!field_cs.encoding_allows_reinterpret_as( + new_type.charset)) { + return false; + } + + if (!field_cs.eq_collation_specific_names(new_type.charset)) { + bool is_part_of_a_key + = !field->part_of_key.is_clear_all(); + return !is_part_of_a_key; + } + + return true; + } + + return true; +} + /******************************************************************//** Use this when the args are passed to the format string from errmsg-utf8.txt directly as is. diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 11b69974558..60c560f9bc7 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -437,6 +437,13 @@ public: @retval false if pushed (always) */ bool rowid_filter_push(Rowid_filter *rowid_filter); + bool can_convert_string(const Field_string* field, + const Column_definition& new_field) const; + bool can_convert_varstring(const Field_varstring* field, + const Column_definition& new_field) const; + bool can_convert_blob(const Field_blob* field, + const Column_definition& new_field) const; + protected: /** MySQL calls this method at the end of each statement. This method diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index e4f5a04f668..08e549629fd 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -131,7 +131,7 @@ static const alter_table_operations INNOBASE_ALTER_INSTANT | ALTER_COLUMN_NAME | ALTER_ADD_VIRTUAL_COLUMN | INNOBASE_FOREIGN_OPERATIONS - | ALTER_COLUMN_EQUAL_PACK_LENGTH + | ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_UNVERSIONED | ALTER_RENAME_INDEX | ALTER_DROP_VIRTUAL_COLUMN; @@ -8332,7 +8332,7 @@ ok_exit: rebuild_templ = ctx->need_rebuild() || ((ha_alter_info->handler_flags - & ALTER_COLUMN_EQUAL_PACK_LENGTH) + & ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) && alter_templ_needs_rebuild( altered_table, ha_alter_info, ctx->new_table)); @@ -9194,7 +9194,7 @@ innobase_rename_or_enlarge_columns_try( DBUG_ENTER("innobase_rename_or_enlarge_columns_try"); if (!(ha_alter_info->handler_flags - & (ALTER_COLUMN_EQUAL_PACK_LENGTH + & (ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_NAME))) { DBUG_RETURN(false); } @@ -9242,7 +9242,7 @@ innobase_rename_or_enlarge_columns_cache( dict_table_t* user_table) { if (!(ha_alter_info->handler_flags - & (ALTER_COLUMN_EQUAL_PACK_LENGTH + & (ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_NAME))) { return; } diff --git a/storage/tokudb/ha_tokudb.h b/storage/tokudb/ha_tokudb.h index 933055a9087..c36c93a4c74 100644 --- a/storage/tokudb/ha_tokudb.h +++ b/storage/tokudb/ha_tokudb.h @@ -916,6 +916,9 @@ public: Item* idx_cond_push(uint keyno, class Item* idx_cond); void cancel_pushed_idx_cond(); + bool can_convert_varstring(const Field_varstring* field, + const Column_definition&new_type) const; + #if defined(TOKU_INCLUDE_ALTER_56) && TOKU_INCLUDE_ALTER_56 public: enum_alter_inplace_result check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info *ha_alter_info); diff --git a/storage/tokudb/ha_tokudb_alter_56.cc b/storage/tokudb/ha_tokudb_alter_56.cc index 6a0802e2d86..4bdf2cf7bda 100644 --- a/storage/tokudb/ha_tokudb_alter_56.cc +++ b/storage/tokudb/ha_tokudb_alter_56.cc @@ -465,10 +465,10 @@ enum_alter_inplace_result ha_tokudb::check_if_supported_inplace_alter( result = HA_ALTER_INPLACE_EXCLUSIVE_LOCK; } } else if ((ctx->handler_flags & - ALTER_COLUMN_EQUAL_PACK_LENGTH) && + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) && only_flags( ctx->handler_flags, - ALTER_COLUMN_EQUAL_PACK_LENGTH | + ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE | ALTER_COLUMN_DEFAULT) && table->s->fields == altered_table->s->fields && find_changed_fields( @@ -1180,6 +1180,18 @@ static bool change_varchar_length_is_supported(Field* old_field, return true; } +bool ha_tokudb::can_convert_varstring(const Field_varstring* field, + const Column_definition& new_type) const { + if (new_type.length < field->field_length || + new_type.char_length < field->char_length() || + !new_type.compression_method() != !field->compression_method() || + new_type.type_handler() != field->type_handler()) { + return false; + } + + return true; +} + // Return true if all changed field lengths can be changed inplace, otherwise // return false static bool change_length_is_supported(TABLE* table, -- cgit v1.2.1