Commit 68d7b266 authored by unknown's avatar unknown

Fix for bug #4508 "CONVERT_TZ() function with new time zone as param crashes server".

Instead of trying to open time zone tables during calculation of CONVERT_TZ() function
or setting of @@time_zone variable we should open and lock them with the rest of 
statement's table (so we should add them to global table list) and after that use such 
pre-opened tables for loading info about time zones.


mysql-test/r/timezone2.result:
  Added test for bug #4508
mysql-test/t/timezone2.test:
  Added test for bug #4508
scripts/mysql_create_system_tables.sh:
  Added one more test time zone to time zone tables which is needed for test for bug #4508.
sql/item_create.cc:
  CONVERT_TZ() now is treated as special function.
sql/item_create.h:
  CONVERT_TZ() now is treated as special function.
sql/item_timefunc.cc:
  Item_func_convert_tz now uses list of pre-opened time zone tables instead of trying to
  open them ad-hoc. Also it avoid calling of current_thd.
sql/item_timefunc.h:
  Added comment describing special nature of CONVERT_TZ() function.
  Optimization: Added own fix_fields() method and tz_tables member for caching pointer
  to list of open time zone tables to Item_func_convert_tz class.
sql/lex.h:
  CONVERT_TZ() now is treated as special function.
sql/mysql_priv.h:
  Removed function which is no longer used.
sql/set_var.cc:
  Now my_tz_find() accepts list of pre-opened time zone tables as last argument 
  and no longer needs pointer to current THD.
sql/set_var.h:
  Exported sys_time_zone, which is now used in sql_yacc.yy for quick finding out if we are
  setting @@time_zone variable.
sql/sql_base.cc:
  Moved propagation of pointers to open tables from global list to local select lists to
  open_and_lock_tables(), also added implicit usage of time zone tables as condition for
  such propagation.
sql/sql_lex.cc:
  Added fake_time_zone_tables_list which is used to indicate that time zone tables are
  implicitly used in statement.
  st_select_lex_unit::create_total_list(): if time zone tables are implicitly used in
  statement add them to global tables list.
sql/sql_lex.h:
  Added LEX::time_zone_tables_used member which is used to indicate that time zone tables 
  are implicitly used in this statement (by pointing to fake_time_zone_table_list) and 
  for holding pointer to those tables after they've been opened.
sql/sql_parse.cc:
  We should also create global table list if statement uses time zone tables implicitly.
  Added initialization of LEX::time_zone_tables_used to mysql_query_init().
sql/sql_prepare.cc:
  We should also create global table list if statement uses time zone tables implicitly.
sql/sql_select.cc:
  Removed functions which are no longer used.
sql/sql_yacc.yy:
  CONVERT_TZ() and @@time_zone variable are handled in special way since they implicitly 
  use time zone tables.
sql/tztime.cc:
  Fix for bug #4508 "CONVERT_TZ() function with new time zone as param crashes server".
  If statement uses CONVERT_TZ() function or @@time_zone variable is set then it implicitly
  uses time zone tables. We need to open and lock such tables with all other tables of 
  such statement.
  
  All code responsible for opening table was removed from tz_load_from_db() and function was 
  renamed to tz_load_from_open_tables() (which uses list of pre-opened tables).
  We also have new functions for construction and initialization of table list of time
  zone tables.
  my_tz_find() now always require list of pre-opened time zone tables and no longer needs
  current THD. So we have to pre-open them in my_tz_init().
  Also now we try to open time zone tables only if they were found during startup.
sql/tztime.h:
  New function for construction of table list of time zone tables my_tz_get_table_list().
  Now my_tz_find() requires list of pre-pened time zone tables instead of current thread.
parent e74007fb
......@@ -244,3 +244,10 @@ NULL
select convert_tz( NULL, 'MET', 'UTC');
convert_tz( NULL, 'MET', 'UTC')
NULL
create table t1 (ts timestamp);
set timestamp=1000000000;
insert into t1 (ts) values (now());
select convert_tz(ts, @@time_zone, 'Japan') from t1;
convert_tz(ts, @@time_zone, 'Japan')
2001-09-09 10:46:40
drop table t1;
......@@ -187,3 +187,15 @@ select convert_tz('2003-12-31 04:00:00', 'SomeNotExistingTimeZone', 'UTC');
select convert_tz('2003-12-31 04:00:00', 'MET', 'SomeNotExistingTimeZone');
select convert_tz('2003-12-31 04:00:00', 'MET', NULL);
select convert_tz( NULL, 'MET', 'UTC');
#
# Test for bug #4508 "CONVERT_TZ() function with new time zone as param
# crashes server." (Was caused by improperly worked mechanism of time zone
# dynamical loading).
#
create table t1 (ts timestamp);
set timestamp=1000000000;
insert into t1 (ts) values (now());
select convert_tz(ts, @@time_zone, 'Japan') from t1;
drop table t1;
......@@ -307,7 +307,8 @@ then
then
i_tzn="$i_tzn INSERT INTO time_zone_name (Name, Time_Zone_id) VALUES"
i_tzn="$i_tzn ('MET', 1), ('UTC', 2), ('Universal', 2), "
i_tzn="$i_tzn ('Europe/Moscow',3), ('leap/Europe/Moscow',4);"
i_tzn="$i_tzn ('Europe/Moscow',3), ('leap/Europe/Moscow',4), "
i_tzn="$i_tzn ('Japan', 5);"
fi
fi
......@@ -327,7 +328,7 @@ then
if test "$1" = "test"
then
i_tz="$i_tz INSERT INTO time_zone (Time_zone_id, Use_leap_seconds)"
i_tz="$i_tz VALUES (1,'N'), (2,'N'), (3,'N'), (4,'Y');"
i_tz="$i_tz VALUES (1,'N'), (2,'N'), (3,'N'), (4,'Y'), (5,'N');"
fi
fi
......@@ -546,7 +547,8 @@ then
i_tzt="$i_tzt ,(4, 2045689222, 8) ,(4, 2058390022, 9)"
i_tzt="$i_tzt ,(4, 2077138822, 8) ,(4, 2090444422, 9)"
i_tzt="$i_tzt ,(4, 2108588422, 8) ,(4, 2121894022, 9)"
i_tzt="$i_tzt ,(4, 2140038022, 8);"
i_tzt="$i_tzt ,(4, 2140038022, 8)"
i_tzt="$i_tzt ,(5, -1009875600, 1);"
fi
fi
......@@ -584,7 +586,8 @@ then
i_tztt="$i_tztt ,(4, 4, 10800, 0, 'MSK') ,(4, 5, 14400, 1, 'MSD')"
i_tztt="$i_tztt ,(4, 6, 18000, 1, 'MSD') ,(4, 7, 7200, 0, 'EET')"
i_tztt="$i_tztt ,(4, 8, 10800, 0, 'MSK') ,(4, 9, 14400, 1, 'MSD')"
i_tztt="$i_tztt ,(4, 10, 10800, 1, 'EEST') ,(4, 11, 7200, 0, 'EET');"
i_tztt="$i_tztt ,(4, 10, 10800, 1, 'EEST') ,(4, 11, 7200, 0, 'EET')"
i_tztt="$i_tztt ,(5, 0, 32400, 0, 'CJT') ,(5, 1, 32400, 0, 'JST');"
fi
fi
......
......@@ -89,11 +89,6 @@ Item *create_func_conv(Item* a, Item *b, Item *c)
return new Item_func_conv(a,b,c);
}
Item *create_func_convert_tz(Item* a, Item *b, Item *c)
{
return new Item_func_convert_tz(a,b,c);
}
Item *create_func_cos(Item* a)
{
return new Item_func_cos(a);
......
......@@ -31,7 +31,6 @@ Item *create_func_char_length(Item* a);
Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs);
Item *create_func_connection_id(void);
Item *create_func_conv(Item* a, Item *b, Item *c);
Item *create_func_convert_tz(Item* a, Item *b, Item *c);
Item *create_func_cos(Item* a);
Item *create_func_cot(Item* a);
Item *create_func_crc32(Item* a);
......
......@@ -1648,19 +1648,29 @@ bool Item_func_from_unixtime::get_date(TIME *ltime,
void Item_func_convert_tz::fix_length_and_dec()
{
String str;
thd= current_thd;
{
collation.set(&my_charset_bin);
decimals= 0;
max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}
bool
Item_func_convert_tz::fix_fields(THD *thd_arg, TABLE_LIST *tables_arg, Item **ref)
{
String str;
if (Item_date_func::fix_fields(thd_arg, tables_arg, ref))
return 1;
tz_tables= thd_arg->lex->time_zone_tables_used;
if (args[1]->const_item())
from_tz= my_tz_find(thd, args[1]->val_str(&str));
from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
if (args[2]->const_item())
to_tz= my_tz_find(thd, args[2]->val_str(&str));
to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
return 0;
}
......@@ -1701,10 +1711,10 @@ bool Item_func_convert_tz::get_date(TIME *ltime,
String str;
if (!args[1]->const_item())
from_tz= my_tz_find(thd, args[1]->val_str(&str));
from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
if (!args[2]->const_item())
to_tz= my_tz_find(thd, args[2]->val_str(&str));
to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0))
{
......
......@@ -531,9 +531,22 @@ class Item_func_from_unixtime :public Item_date_func
*/
class Time_zone;
/*
This class represents CONVERT_TZ() function.
The important fact about this function that it is handled in special way.
When such function is met in expression time_zone system tables are added
to global list of tables to open, so later those already opened and locked
tables can be used during this function calculation for loading time zone
descriptions.
*/
class Item_func_convert_tz :public Item_date_func
{
THD *thd;
/* Cached pointer to list of pre-opened time zone tables. */
TABLE_LIST *tz_tables;
/*
If time zone parameters are constants we are caching objects that
represent them.
*/
Time_zone *from_tz, *to_tz;
public:
Item_func_convert_tz(Item *a, Item *b, Item *c):
......@@ -542,6 +555,7 @@ class Item_func_convert_tz :public Item_date_func
double val() { return (double) val_int(); }
String *val_str(String *str);
const char *func_name() const { return "convert_tz"; }
bool fix_fields(THD *, struct st_table_list *, Item **);
void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
};
......
......@@ -499,7 +499,7 @@ static SYMBOL sql_functions[] = {
{ "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)},
{ "CONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)},
{ "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)},
{ "CONVERT_TZ", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_convert_tz)},
{ "CONVERT_TZ", SYM(CONVERT_TZ_SYM)},
{ "COUNT", SYM(COUNT_SYM)},
{ "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)},
{ "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)},
......
......@@ -500,7 +500,6 @@ int mysql_select(THD *thd, Item ***rref_pointer_array,
select_result *result, SELECT_LEX_UNIT *unit,
SELECT_LEX *select_lex);
void free_underlaid_joins(THD *thd, SELECT_LEX *select);
void fix_tables_pointers(SELECT_LEX *select_lex);
int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
select_result *result);
int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
......
......@@ -2372,8 +2372,9 @@ bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
return 1;
}
#endif
if (!(var->save_result.time_zone= my_tz_find(thd, res)))
if (!(var->save_result.time_zone=
my_tz_find(res, thd->lex->time_zone_tables_used)))
{
my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), res ? res->c_ptr() : "NULL");
return 1;
......@@ -2418,7 +2419,8 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
if (default_tz_name)
{
String str(default_tz_name, &my_charset_latin1);
global_system_variables.time_zone= my_tz_find(thd, &str);
global_system_variables.time_zone=
my_tz_find(&str, thd->lex->time_zone_tables_used);
}
else
global_system_variables.time_zone= my_tz_SYSTEM;
......
......@@ -908,6 +908,7 @@ ulong fix_sql_mode(ulong sql_mode);
extern sys_var_str sys_charset_system;
extern sys_var_str sys_init_connect;
extern sys_var_str sys_init_slave;
extern sys_var_thd_time_zone sys_time_zone;
CHARSET_INFO *get_old_charset_by_name(const char *old_name);
gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
NAMED_LIST **found);
......
......@@ -1670,7 +1670,22 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
uint counter;
if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter))
DBUG_RETURN(-1); /* purecov: inspected */
fix_tables_pointers(thd->lex->all_selects_list);
/*
Let us propagate pointers to open tables from global table list
to table lists in particular selects if needed.
*/
if (thd->lex->all_selects_list->next_select_in_list() ||
thd->lex->time_zone_tables_used)
{
for (SELECT_LEX *sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
for (TABLE_LIST *cursor= (TABLE_LIST *) sl->table_list.first;
cursor;
cursor=cursor->next)
if (cursor->table_list)
cursor->table= cursor->table_list->table;
}
DBUG_RETURN(mysql_handle_derived(thd->lex));
}
......
......@@ -22,6 +22,16 @@
#include <m_ctype.h>
#include <hash.h>
/*
Fake table list object, pointer to which is used as special value for
st_lex::time_zone_tables_used indicating that we implicitly use time
zone tables in this statement but real table list was not yet created.
Pointer to it is also returned by my_tz_get_tables_list() as indication
of transient error;
*/
TABLE_LIST fake_time_zone_tables_list;
/* Macros to look like lex */
#define yyGet() *(lex->ptr++)
......@@ -1292,7 +1302,32 @@ bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
TABLE_LIST **result_arg)
{
*result_arg= 0;
res= create_total_list_n_last_return(thd_arg, lex, &result_arg);
if (!(res= create_total_list_n_last_return(thd_arg, lex, &result_arg)))
{
/*
If time zone tables were used implicitly in statement we should add
them to global table list.
*/
if (lex->time_zone_tables_used)
{
/*
Altough we are modifying lex data, it won't raise any problem in
case when this lex belongs to some prepared statement or stored
procedure: such modification does not change any invariants imposed
by requirement to reuse the same lex for multiple executions.
*/
if ((lex->time_zone_tables_used= my_tz_get_table_list(thd)) !=
&fake_time_zone_tables_list)
{
*result_arg= lex->time_zone_tables_used;
}
else
{
send_error(thd, 0);
res= 1;
}
}
}
return res;
}
......
......@@ -633,6 +633,12 @@ typedef struct st_lex
bool prepared_stmt_code_is_varref;
/* Names of user variables holding parameters (in EXECUTE) */
List<LEX_STRING> prepared_stmt_params;
/*
If points to fake_time_zone_tables_list indicates that time zone
tables are implicitly used by statement, also is used for holding
list of those tables after they are opened.
*/
TABLE_LIST *time_zone_tables_used;
st_lex() {}
inline void uncacheable(uint8 cause)
{
......@@ -661,6 +667,7 @@ typedef struct st_lex
TABLE_LIST *local_first);
} LEX;
extern TABLE_LIST fake_time_zone_tables_list;
void lex_init(void);
void lex_free(void);
......
......@@ -1897,7 +1897,8 @@ mysql_execute_command(THD *thd)
#endif
}
#endif /* !HAVE_REPLICATION */
if (&lex->select_lex != lex->all_selects_list &&
if ((&lex->select_lex != lex->all_selects_list ||
lex->time_zone_tables_used) &&
lex->unit.create_total_list(thd, lex, &tables))
DBUG_VOID_RETURN;
......@@ -3875,6 +3876,7 @@ mysql_init_query(THD *thd, uchar *buf, uint length)
lex->lock_option= TL_READ;
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
lex->time_zone_tables_used= 0;
lex_start(thd, buf, length);
thd->select_number= lex->select_lex.select_number= 1;
thd->free_list= 0;
......
......@@ -1407,7 +1407,8 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, stmt->param_count));
if (select_lex != lex->all_selects_list &&
if ((&lex->select_lex != lex->all_selects_list ||
lex->time_zone_tables_used) &&
lex->unit.create_total_list(thd, lex, &tables))
DBUG_RETURN(1);
......
......@@ -213,39 +213,6 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
}
void relink_tables(SELECT_LEX *select_lex)
{
for (TABLE_LIST *cursor= (TABLE_LIST *) select_lex->table_list.first;
cursor;
cursor=cursor->next)
if (cursor->table_list)
cursor->table= cursor->table_list->table;
}
void fix_tables_pointers(SELECT_LEX *select_lex)
{
if (select_lex->next_select_in_list())
{
/* Fix tables 'to-be-unioned-from' list to point at opened tables */
for (SELECT_LEX *sl= select_lex;
sl;
sl= sl->next_select_in_list())
relink_tables(sl);
}
}
void fix_tables_pointers(SELECT_LEX_UNIT *unit)
{
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
{
relink_tables(sl);
for (SELECT_LEX_UNIT *un= sl->first_inner_unit(); un; un= un->next_unit())
fix_tables_pointers(un);
}
}
/*
Function to setup clauses without sum functions
*/
......
......@@ -463,6 +463,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CASE_SYM
%token CONCAT
%token CONCAT_WS
%token CONVERT_TZ_SYM
%token CURDATE
%token CURTIME
%token DATABASE
......@@ -2825,6 +2826,11 @@ simple_expr:
{ $$= new Item_func_concat(* $3); }
| CONCAT_WS '(' expr ',' expr_list ')'
{ $$= new Item_func_concat_ws($3, *$5); }
| CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')'
{
Lex->time_zone_tables_used= &fake_time_zone_tables_list;
$$= new Item_func_convert_tz($3, $5, $7);
}
| CURDATE optional_braces
{ $$= new Item_func_curdate_local(); Lex->safe_to_cache_query=0; }
| CURTIME optional_braces
......@@ -5308,6 +5314,12 @@ internal_variable_name:
$$.var= tmp;
$$.base_name.str=0;
$$.base_name.length=0;
/*
If this is time_zone variable we should open time zone
describing tables
*/
if (tmp == &sys_time_zone)
Lex->time_zone_tables_used= &fake_time_zone_tables_list;
}
| ident '.' ident
{
......
This diff is collapsed.
......@@ -59,7 +59,8 @@ class Time_zone: public Sql_alloc
extern Time_zone * my_tz_UTC;
extern Time_zone * my_tz_SYSTEM;
extern Time_zone * my_tz_find(THD *thd, const String *name);
extern TABLE_LIST * my_tz_get_table_list(THD *thd);
extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables);
extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
extern void my_tz_free();
......
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