summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorDavi Arnaut <Davi.Arnaut@Sun.COM>2009-09-29 07:58:42 -0300
committerDavi Arnaut <Davi.Arnaut@Sun.COM>2009-09-29 07:58:42 -0300
commit8d3d35ea5715739ac94785b8c9ff1a185699a84a (patch)
tree2f88cce9088e50566569406c02f2499103528857 /sql
parent1d1a293b26b08d17b5f32bf4cf2354cb8364b3b6 (diff)
downloadmariadb-git-8d3d35ea5715739ac94785b8c9ff1a185699a84a.tar.gz
Bug#45567: Fast ALTER TABLE broken for enum and set
The problem was that appending values to the end of an existing ENUM or SET column was being treated as table data modification, preventing a immediately (fast) table alteration that occurs when only table metadata is being modified. The cause was twofold: adding a enumeration or set members to the end of the list of valid member values was not being considered a "compatible" table alteration, and for SET columns, the check was being done upon the max display length and not the underlying (pack) length of the field. The solution is to augment the function that checks wether two ENUM or SET fields are compatible -- by comparing the pack lengths and performing a limited comparison of the member values. mysql-test/r/alter_table.result: Add test case result for Bug#45567 mysql-test/t/alter_table.test: Add test case for Bug#45567 sql/field.cc: Check whether two fields can be considered 'equal' for table alteration purposes. Fields are equal if they retain the same pack length and if new members are added to the end of the list. sql/field.h: Add comment and remove method.
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc75
-rw-r--r--sql/field.h8
2 files changed, 66 insertions, 17 deletions
diff --git a/sql/field.cc b/sql/field.cc
index 9f46552d5bd..0df9b0fc2e4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -8833,38 +8833,81 @@ bool Field::eq_def(Field *field)
/**
+ Compare the first t1::count type names.
+
+ @return TRUE if the type names of t1 match those of t2. FALSE otherwise.
+*/
+
+static bool compare_type_names(CHARSET_INFO *charset, TYPELIB *t1, TYPELIB *t2)
+{
+ for (uint i= 0; i < t1->count; i++)
+ if (my_strnncoll(charset,
+ (const uchar*) t1->type_names[i],
+ t1->type_lengths[i],
+ (const uchar*) t2->type_names[i],
+ t2->type_lengths[i]))
+ return FALSE;
+ return TRUE;
+}
+
+/**
@return
returns 1 if the fields are equally defined
*/
bool Field_enum::eq_def(Field *field)
{
+ TYPELIB *values;
+
if (!Field::eq_def(field))
- return 0;
- return compare_enum_values(((Field_enum*) field)->typelib);
-}
+ return FALSE;
+ values= ((Field_enum*) field)->typelib;
-bool Field_enum::compare_enum_values(TYPELIB *values)
-{
+ /* Definition must be strictly equal. */
if (typelib->count != values->count)
return FALSE;
- for (uint i= 0; i < typelib->count; i++)
- if (my_strnncoll(field_charset,
- (const uchar*) typelib->type_names[i],
- typelib->type_lengths[i],
- (const uchar*) values->type_names[i],
- values->type_lengths[i]))
- return FALSE;
- return TRUE;
+
+ return compare_type_names(field_charset, typelib, values);
}
+/**
+ Check whether two fields can be considered 'equal' for table
+ 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.
+*/
+
uint Field_enum::is_equal(Create_field *new_field)
{
- if (!Field_str::is_equal(new_field))
- return 0;
- return compare_enum_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 (compare_str_field_flags(new_field, flags) ||
+ new_field->sql_type != real_type() ||
+ new_field->charset != field_charset ||
+ new_field->pack_length != pack_length())
+ return IS_EQUAL_NO;
+
+ /*
+ Changing the definition of an ENUM or SET column by adding a new
+ enumeration or set members to the end of the list of valid member
+ values only alters table metadata and not table data.
+ */
+ if (typelib->count > values->count)
+ return IS_EQUAL_NO;
+
+ /* Check whether there are modification before the end. */
+ if (! compare_type_names(field_charset, typelib, new_field->interval))
+ return IS_EQUAL_NO;
+
+ return IS_EQUAL_YES;
}
diff --git a/sql/field.h b/sql/field.h
index a9299256f88..7a9b69eff40 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -472,6 +472,13 @@ public:
/* maximum possible display length */
virtual uint32 max_display_length()= 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).
+ */
virtual uint is_equal(Create_field *new_field);
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
@@ -1862,7 +1869,6 @@ public:
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
private:
int do_save_field_metadata(uchar *first_byte);
- bool compare_enum_values(TYPELIB *values);
uint is_equal(Create_field *new_field);
};