Commit c08e6c88 authored by Alexey Kopytov's avatar Alexey Kopytov

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.

mysql-test/r/partition_bug18198.result:
  Corrected the error.
mysql-test/r/partition_error.result:
  Corrected error texts.
  Added test cases for bug #42849.
mysql-test/t/partition_bug18198.test:
  Corrected error code.
mysql-test/t/partition_error.test:
  Corrected error codes.
  Added test cases for bug #42849.
sql/item.h:
  Added is_timezone_dependent_processor() to Item.
sql/item_func.h:
  Added has_timestamp_args() and the implementation of
  is_timezone_dependent_processor() for Item_func.
sql/item_timefunc.h:
  Added is_timezone_dependent_processor() to 
  Item_func_unix_timestamp.
sql/share/errmsg.txt:
  Renamed ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR to
  ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR to better reflect the
  meaning. Adjusted the error message.
sql/sql_partition.cc:
  Modified fix_fields_part_func() to walk through partitioning
  expression tree with is_timezone_dependent_processor() and issue
  a warning/error if it depends on the timezone settings.
  
  Changed fix_fields_part_func() to a static function since it is
  not used anywhere except sql_partition.cc
sql/sql_partition.h:
  Removed the unneeded declaration of fix_fields_part_func()
  since it is now a static function.
sql/sql_yacc.yy:
  ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR ->
  ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR.
parent 39d0b1bd
......@@ -126,7 +126,7 @@ ERROR HY000: This partition function is not allowed
create table t1 (col1 date)
partition by range(unix_timestamp(col1))
(partition p0 values less than (10), partition p1 values less than (30));
ERROR HY000: This partition function is not allowed
ERROR HY000: Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed
create table t1 (col1 datetime)
partition by range(week(col1))
(partition p0 values less than (10), partition p1 values less than (30));
......
This diff is collapsed.
......@@ -158,7 +158,7 @@ create table t1 (col1 datetime)
partition by range(timestampdiff(day,5,col1))
(partition p0 values less than (10), partition p1 values less than (30));
-- error ER_PARTITION_FUNCTION_IS_NOT_ALLOWED
-- error ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR
create table t1 (col1 date)
partition by range(unix_timestamp(col1))
(partition p0 values less than (10), partition p1 values less than (30));
......
This diff is collapsed.
......@@ -950,6 +950,15 @@ class Item {
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
......
......@@ -200,6 +200,29 @@ class Item_func :public Item_result_field
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();
}
};
......
......@@ -305,6 +305,16 @@ class Item_func_unix_timestamp :public Item_int_func
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;
......
......@@ -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-Ausdrcke in (Unter-)Partitionsfunktionen sind nicht erlaubt"
swe "Konstanta uttryck eller slumpmssiga uttryck r inte tilltna (sub)partitioneringsfunktioner"
ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment