summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/timezone2.result8
-rw-r--r--mysql-test/t/timezone2.test17
-rw-r--r--sql/item_timefunc.cc30
-rw-r--r--sql/item_timefunc.h8
-rw-r--r--sql/set_var.cc28
-rw-r--r--sql/set_var.h1
-rw-r--r--sql/sql_lex.cc35
-rw-r--r--sql/tztime.cc10
8 files changed, 88 insertions, 49 deletions
diff --git a/mysql-test/r/timezone2.result b/mysql-test/r/timezone2.result
index a1a2fec739f..206ff79e7ba 100644
--- a/mysql-test/r/timezone2.result
+++ b/mysql-test/r/timezone2.result
@@ -303,3 +303,11 @@ delete from mysql.db where user like 'mysqltest\_%';
delete from mysql.tables_priv where user like 'mysqltest\_%';
flush privileges;
drop table t1, t2;
+select convert_tz('2005-01-14 17:00:00', 'UTC', custTimeZone) from (select 'UTC' as custTimeZone) as tmp;
+convert_tz('2005-01-14 17:00:00', 'UTC', custTimeZone)
+2005-01-14 17:00:00
+create table t1 select convert_tz(NULL, NULL, NULL);
+select * from t1;
+convert_tz(NULL, NULL, NULL)
+NULL
+drop table t1;
diff --git a/mysql-test/t/timezone2.test b/mysql-test/t/timezone2.test
index d185a647921..32ed359a2db 100644
--- a/mysql-test/t/timezone2.test
+++ b/mysql-test/t/timezone2.test
@@ -266,3 +266,20 @@ delete from mysql.db where user like 'mysqltest\_%';
delete from mysql.tables_priv where user like 'mysqltest\_%';
flush privileges;
drop table t1, t2;
+
+#
+# Test for bug #7705 "CONVERT_TZ() crashes with subquery/WHERE on index
+# column". Queries in which one of time zone arguments of CONVERT_TZ() is
+# determined as constant only at val() stage (not at fix_fields() stage),
+# should not crash server.
+#
+select convert_tz('2005-01-14 17:00:00', 'UTC', custTimeZone) from (select 'UTC' as custTimeZone) as tmp;
+
+#
+# Test for bug #7899 "CREATE TABLE .. SELECT .. and CONVERT_TZ() function
+# does not work well together". The following statement should return only
+# one NULL row and not result of full join.
+#
+create table t1 select convert_tz(NULL, NULL, NULL);
+select * from t1;
+drop table t1;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 0d652a431cb..80563b9cc83 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1656,6 +1656,7 @@ void Item_func_convert_tz::fix_length_and_dec()
collation.set(&my_charset_bin);
decimals= 0;
max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+ maybe_null= 1;
}
@@ -1668,12 +1669,6 @@ Item_func_convert_tz::fix_fields(THD *thd_arg, TABLE_LIST *tables_arg, Item **re
tz_tables= thd_arg->lex->time_zone_tables_used;
- if (args[1]->const_item())
- from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
-
- if (args[2]->const_item())
- to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
-
return 0;
}
@@ -1713,13 +1708,19 @@ bool Item_func_convert_tz::get_date(TIME *ltime,
my_time_t my_time_tmp;
bool not_used;
String str;
-
- if (!args[1]->const_item())
+
+ if (!from_tz_cached)
+ {
from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
-
- if (!args[2]->const_item())
+ from_tz_cached= args[1]->const_item();
+ }
+
+ if (!to_tz_cached)
+ {
to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
-
+ to_tz_cached= args[2]->const_item();
+ }
+
if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0))
{
null_value= 1;
@@ -1741,6 +1742,13 @@ bool Item_func_convert_tz::get_date(TIME *ltime,
}
+void Item_func_convert_tz::cleanup()
+{
+ from_tz_cached= to_tz_cached= 0;
+ Item_date_func::cleanup();
+}
+
+
void Item_date_add_interval::fix_length_and_dec()
{
enum_field_types arg0_field_type;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index df0b05d6d42..cc2709bf555 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -545,12 +545,15 @@ class Item_func_convert_tz :public Item_date_func
TABLE_LIST *tz_tables;
/*
If time zone parameters are constants we are caching objects that
- represent them.
+ represent them (we use separate from_tz_cached/to_tz_cached members
+ to indicate this fact, since NULL is legal value for from_tz/to_tz
+ members.
*/
+ bool from_tz_cached, to_tz_cached;
Time_zone *from_tz, *to_tz;
public:
Item_func_convert_tz(Item *a, Item *b, Item *c):
- Item_date_func(a, b, c) {}
+ Item_date_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {}
longlong val_int();
double val() { return (double) val_int(); }
String *val_str(String *str);
@@ -558,6 +561,7 @@ class Item_func_convert_tz :public Item_date_func
bool fix_fields(THD *, struct st_table_list *, Item **);
void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
+ void cleanup();
};
diff --git a/sql/set_var.cc b/sql/set_var.cc
index e44ac742abe..99032a29651 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -2438,8 +2438,15 @@ bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
bool sys_var_thd_time_zone::update(THD *thd, set_var *var)
{
- /* We are using Time_zone object found during check() phase */
- *get_tz_ptr(thd,var->type)= var->save_result.time_zone;
+ /* We are using Time_zone object found during check() phase. */
+ if (var->type == OPT_GLOBAL)
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ global_system_variables.time_zone= var->save_result.time_zone;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ }
+ else
+ thd->variables.time_zone= var->save_result.time_zone;
return 0;
}
@@ -2451,27 +2458,25 @@ byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
We can use ptr() instead of c_ptr() here because String contaning
time zone name is guaranteed to be zero ended.
*/
- return (byte *)((*get_tz_ptr(thd,type))->get_name()->ptr());
-}
-
-
-Time_zone** sys_var_thd_time_zone::get_tz_ptr(THD *thd,
- enum_var_type type)
-{
if (type == OPT_GLOBAL)
- return &global_system_variables.time_zone;
+ return (byte *)(global_system_variables.time_zone->get_name()->ptr());
else
- return &thd->variables.time_zone;
+ return (byte *)(thd->variables.time_zone->get_name()->ptr());
}
void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
{
+ pthread_mutex_lock(&LOCK_global_system_variables);
if (type == OPT_GLOBAL)
{
if (default_tz_name)
{
String str(default_tz_name, &my_charset_latin1);
+ /*
+ We are guaranteed to find this time zone since its existence
+ is checked during start-up.
+ */
global_system_variables.time_zone=
my_tz_find(&str, thd->lex->time_zone_tables_used);
}
@@ -2480,6 +2485,7 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
}
else
thd->variables.time_zone= global_system_variables.time_zone;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
}
/*
diff --git a/sql/set_var.h b/sql/set_var.h
index 4a4e631d88c..df2fd41c7bd 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -736,7 +736,6 @@ public:
bool update(THD *thd, set_var *var);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
virtual void set_default(THD *thd, enum_var_type type);
- Time_zone **get_tz_ptr(THD *thd, enum_var_type type);
};
/****************************************************************************
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d2ac0df1472..b52ebf12dd2 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1719,8 +1719,8 @@ st_lex::st_lex()
global_first Save first global table here
local_first Save first local table here
- NORES
- global_first & local_first are used to save result for link_first_table_back
+ NOTES
+ This function assumes that outer select list is non-empty.
RETURN
global list without first table
@@ -1730,25 +1730,25 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
TABLE_LIST **global_first,
TABLE_LIST **local_first)
{
- *global_first= tables;
- *local_first= (TABLE_LIST*)select_lex.table_list.first;
+ DBUG_ASSERT(select_lex.table_list.first != 0);
/*
- Exclude from global table list
+ Save pointers to first elements of global table list and list
+ of tables used in outer select. It does not harm if these lists
+ are the same.
*/
+ *global_first= tables;
+ *local_first= (TABLE_LIST*)select_lex.table_list.first;
+
+ /* Exclude first elements from these lists */
+ select_lex.table_list.first= (byte*) (*local_first)->next;
tables= tables->next;
- /*
- and from local list if it is not the same
- */
- select_lex.table_list.first= ((&select_lex != all_selects_list) ?
- (byte*) (*local_first)->next :
- (byte*) tables);
(*global_first)->next= 0;
return tables;
}
/*
- Link table back that was unlinked with unlink_first_table()
+ Link table which was unlinked with unlink_first_table() back.
SYNOPSIS
link_first_table_back()
@@ -1764,16 +1764,7 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
TABLE_LIST *local_first)
{
global_first->next= tables;
- if (&select_lex != all_selects_list)
- {
- /*
- we do not touch local table 'next' field => we need just
- put the table in the list
- */
- select_lex.table_list.first= (byte*) local_first;
- }
- else
- select_lex.table_list.first= (byte*) global_first;
+ select_lex.table_list.first= (byte*) local_first;
return global_first;
}
diff --git a/sql/tztime.cc b/sql/tztime.cc
index c2143b0d5dd..fd24e1f15b2 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1773,7 +1773,13 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
0, HA_READ_KEY_EXACT))
{
- sql_print_error("Can't find description of time zone.");
+#ifdef EXTRA_DEBUG
+ /*
+ Most probably user has mistyped time zone name, so no need to bark here
+ unless we need it for debugging.
+ */
+ sql_print_error("Can't find description of time zone '%s'", tz_name_buff);
+#endif
goto end;
}
@@ -1794,7 +1800,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
0, HA_READ_KEY_EXACT))
{
- sql_print_error("Can't find description of time zone.");
+ sql_print_error("Can't find description of time zone '%u'", tzid);
goto end;
}