diff options
author | Alexey Kopytov <Alexey.Kopytov@Sun.com> | 2009-12-13 23:29:50 +0300 |
---|---|---|
committer | Alexey Kopytov <Alexey.Kopytov@Sun.com> | 2009-12-13 23:29:50 +0300 |
commit | a8cfe3d4f74db680084c3ec8a8b6fbd8f71780d6 (patch) | |
tree | 9618da2b288832f2f203077e1254b626a4bd6475 /sql | |
parent | 13f7a81b3781c7e5496874641d35e5b20ff81635 (diff) | |
download | mariadb-git-a8cfe3d4f74db680084c3ec8a8b6fbd8f71780d6.tar.gz |
Bug #42849: innodb crash with varying time_zone on partitioned
timestamp primary key
Since TIMESTAMP values are adjusted by the current time zone
settings in both numeric and string contexts, using any
expressions involving TIMESTAMP values as a
(sub)partitioning function leads to undeterministic behavior of
partitioned tables. The effect may vary depending on a storage
engine, it can be either incorrect data being retrieved or
stored, or an assertion failure. The root cause of this is the
fact that the calculated partition ID may differ from a
previously calculated ID for the same data due to timezone
adjustments of the partitioning expression value.
Fixed by disabling any expressions involving TIMESTAMP values
to be used in partitioning functions with the follwing two
exceptions:
1. Creating or altering into a partitioned table that violates
the above rule is not allowed, but opening existing such tables
results in a warning rather than an error so that such tables
could be fixed.
2. UNIX_TIMESTAMP() is the only way to get a
timezone-independent value from a TIMESTAMP column, because it
returns the internal representation (a time_t value) of a
TIMESTAMP argument verbatim. So UNIX_TIMESTAMP(timestamp_column)
is allowed and should be used to fix existing tables if one
wants to use TIMESTAMP columns with partitioning.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.h | 9 | ||||
-rw-r--r-- | sql/item_func.h | 23 | ||||
-rw-r--r-- | sql/item_timefunc.h | 10 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 4 | ||||
-rw-r--r-- | sql/sql_partition.cc | 39 | ||||
-rw-r--r-- | sql/sql_partition.h | 3 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 |
7 files changed, 78 insertions, 12 deletions
diff --git a/sql/item.h b/sql/item.h index 3dfcd7c2612..4135ff2b097 100644 --- a/sql/item.h +++ b/sql/item.h @@ -950,6 +950,15 @@ public: virtual Item *equal_fields_propagator(uchar * arg) { return this; } virtual bool set_no_const_sub(uchar *arg) { return FALSE; } virtual Item *replace_equal_field(uchar * arg) { return this; } + /* + Check if an expression value depends on the current timezone. Used by + partitioning code to reject timezone-dependent expressions in a + (sub)partitioning function. + */ + virtual bool is_timezone_dependent_processor(uchar *bool_arg) + { + return FALSE; + } /* For SP local variable returns pointer to Item representing its diff --git a/sql/item_func.h b/sql/item_func.h index 67049af81a2..738f6544204 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -200,6 +200,29 @@ public: null_value=1; return 0.0; } + bool has_timestamp_args() + { + DBUG_ASSERT(fixed == TRUE); + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->type() == Item::FIELD_ITEM && + args[i]->field_type() == MYSQL_TYPE_TIMESTAMP) + return TRUE; + } + return FALSE; + } + /* + We assume the result of any function that has a TIMESTAMP argument to be + timezone-dependent, since a TIMESTAMP value in both numeric and string + contexts is interpreted according to the current timezone. + The only exception is UNIX_TIMESTAMP() which returns the internal + representation of a TIMESTAMP argument verbatim, and thus does not depend on + the timezone. + */ + virtual bool is_timezone_dependent_processor(uchar *bool_arg) + { + return has_timestamp_args(); + } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 9e3c2e8c89f..a7a64090f6c 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -305,6 +305,16 @@ public: Item_func_unix_timestamp(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "unix_timestamp"; } + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + /* + UNIX_TIMESTAMP() depends on the current timezone + (and thus may not be used as a partitioning function) + when its argument is NOT of the TIMESTAMP type. + */ + bool is_timezone_dependent_processor(uchar *int_arg) + { + return !has_timestamp_args(); + } void fix_length_and_dec() { decimals=0; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 7c92c827c87..cc319667060 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5696,8 +5696,8 @@ ER_PARTITION_WRONG_NO_SUBPART_ERROR eng "Wrong number of subpartitions defined, mismatch with previous setting" ger "Falsche Anzahl von Unterpartitionen definiert, stimmt nicht mit vorherigen Einstellungen überein" swe "Antal subpartitioner definierade och antal subpartitioner är inte lika" -ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR - eng "Constant/Random expression in (sub)partitioning function is not allowed" +ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR + eng "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed" ger "Konstante oder Random-Ausdrücke in (Unter-)Partitionsfunktionen sind nicht erlaubt" swe "Konstanta uttryck eller slumpmässiga uttryck är inte tillåtna (sub)partitioneringsfunktioner" ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 4a50650b6f4..f3f33ab1dd1 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -869,6 +869,8 @@ int check_signed_flag(partition_info *part_info) part_info Reference to partitioning data structure is_sub_part Is the table subpartitioned as well is_field_to_be_setup Flag if we are to set-up field arrays + is_create_table_ind Indicator of whether openfrm was called as part of + CREATE or ALTER TABLE RETURN VALUE TRUE An error occurred, something was wrong with the @@ -891,8 +893,9 @@ int check_signed_flag(partition_info *part_info) on the field object. */ -bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, - bool is_sub_part, bool is_field_to_be_setup) +static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, + bool is_sub_part, bool is_field_to_be_setup, + bool is_create_table_ind) { partition_info *part_info= table->part_info; uint dir_length, home_dir_length; @@ -982,10 +985,31 @@ bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, thd->where= save_where; if (unlikely(func_expr->const_item())) { - my_error(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0)); + my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0)); clear_field_flag(table); goto end; } + + /* + We don't allow creating partitions with timezone-dependent expressions as + a (sub)partitioning function, but we want to allow such expressions when + opening existing tables for easier maintenance. This exception should be + deprecated at some point in future so that we always throw an error. + */ + if (func_expr->walk(&Item::is_timezone_dependent_processor, + 0, NULL)) + { + if (is_create_table_ind) + { + my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0)); + goto end; + } + else + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, + ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR)); + } + if ((!is_sub_part) && (error= check_signed_flag(part_info))) goto end; result= FALSE; @@ -1593,7 +1617,8 @@ bool fix_partition_func(THD *thd, TABLE *table, else { if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr, - table, TRUE, TRUE))) + table, TRUE, TRUE, + is_create_table_ind))) goto end; if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT)) { @@ -1621,7 +1646,8 @@ bool fix_partition_func(THD *thd, TABLE *table, else { if (unlikely(fix_fields_part_func(thd, part_info->part_expr, - table, FALSE, TRUE))) + table, FALSE, TRUE, + is_create_table_ind))) goto end; if (unlikely(part_info->part_expr->result_type() != INT_RESULT)) { @@ -1635,7 +1661,8 @@ bool fix_partition_func(THD *thd, TABLE *table, { const char *error_str; if (unlikely(fix_fields_part_func(thd, part_info->part_expr, - table, FALSE, TRUE))) + table, FALSE, TRUE, + is_create_table_ind))) goto end; if (part_info->part_type == RANGE_PARTITION) { diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 282e24f1853..b9efbf25a00 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -91,9 +91,6 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, uint32 get_partition_id_range_for_endpoint(partition_info *part_info, bool left_endpoint, bool include_endpoint); -bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, - bool is_sub_part, bool is_field_to_be_setup); - bool check_part_func_fields(Field **ptr, bool ok_with_charsets); bool field_is_partition_charset(Field *field); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c40062e5d52..ec6d2cc72cd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3931,7 +3931,7 @@ part_func_expr: lex->safe_to_cache_query= 1; if (not_corr_func) { - my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); + my_parse_error(ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR)); MYSQL_YYABORT; } $$=$1; |