summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Kosov <claprix@yandex.ru>2019-06-17 16:54:47 +0300
committerEugene Kosov <claprix@yandex.ru>2019-06-22 12:06:57 +0300
commit937033f7b3e706d5f93f94ff03ec80b08dd5b2b2 (patch)
tree8dd16c2abe884fa4f92c6ac1f4940f8bea56b3ba
parentb6e88535d42d01ab6c566967159d1b4c8fd0fa11 (diff)
downloadmariadb-git-bb-10.4-MDEV-17301-charset-nocopy.tar.gz
NFC: refactor Field::is_equal() and related stuffbb-10.4-MDEV-17301-charset-nocopy
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
-rw-r--r--sql/field.cc187
-rw-r--r--sql/field.h58
-rw-r--r--sql/handler.cc4
-rw-r--r--sql/handler.h45
-rw-r--r--sql/sql_priv.h8
-rw-r--r--sql/sql_table.cc141
-rw-r--r--sql/sql_type.cc1
-rw-r--r--storage/connect/ha_connect.cc4
-rw-r--r--storage/connect/mysql-test/connect/r/xml2_mult.result2
-rw-r--r--storage/connect/mysql-test/connect/r/xml_mult.result2
-rw-r--r--storage/innobase/handler/ha_innodb.cc146
-rw-r--r--storage/innobase/handler/ha_innodb.h7
-rw-r--r--storage/innobase/handler/handler0alter.cc8
-rw-r--r--storage/tokudb/ha_tokudb.h3
-rw-r--r--storage/tokudb/ha_tokudb_alter_56.cc16
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<const Field_geom*>(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;
}
/*
@@ -6603,40 +6604,6 @@ Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key,
}
/**
- 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 <my_bitmap.h>
#include <mysql/service_thd_alloc.h>
#include <mysql/service_thd_wait.h>
+#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,