diff options
-rw-r--r-- | mysql-test/r/timezone2.result | 7 | ||||
-rw-r--r-- | mysql-test/t/timezone2.test | 12 | ||||
-rw-r--r-- | scripts/mysql_create_system_tables.sh | 11 | ||||
-rw-r--r-- | sql/item_create.cc | 5 | ||||
-rw-r--r-- | sql/item_create.h | 1 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 28 | ||||
-rw-r--r-- | sql/item_timefunc.h | 16 | ||||
-rw-r--r-- | sql/lex.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/set_var.cc | 8 | ||||
-rw-r--r-- | sql/set_var.h | 1 | ||||
-rw-r--r-- | sql/sql_base.cc | 17 | ||||
-rw-r--r-- | sql/sql_lex.cc | 37 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 | ||||
-rw-r--r-- | sql/sql_parse.cc | 4 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 3 | ||||
-rw-r--r-- | sql/sql_select.cc | 33 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 12 | ||||
-rw-r--r-- | sql/tztime.cc | 306 | ||||
-rw-r--r-- | sql/tztime.h | 3 |
20 files changed, 315 insertions, 199 deletions
diff --git a/mysql-test/r/timezone2.result b/mysql-test/r/timezone2.result index 5361ff4ffe6..02406b77a65 100644 --- a/mysql-test/r/timezone2.result +++ b/mysql-test/r/timezone2.result @@ -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; diff --git a/mysql-test/t/timezone2.test b/mysql-test/t/timezone2.test index 49579421570..15ac3416b29 100644 --- a/mysql-test/t/timezone2.test +++ b/mysql-test/t/timezone2.test @@ -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; + diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index e45c0ec5571..7a30bcdbeca 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -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 diff --git a/sql/item_create.cc b/sql/item_create.cc index 4290a25e348..c98c7892c26 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -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); diff --git a/sql/item_create.h b/sql/item_create.h index 19f0c9133f2..7577627ef04 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -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); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index cc320addd47..73aec7e8bdd 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -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)) { diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a7ff2924786..2254fc830c9 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -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); }; diff --git a/sql/lex.h b/sql/lex.h index 218a1762a5c..c64a7069c32 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -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)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2f0e2085430..b3b79c16787 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -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, diff --git a/sql/set_var.cc b/sql/set_var.cc index bcebb62ae4d..fc1332695d6 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -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; diff --git a/sql/set_var.h b/sql/set_var.h index a51e44285d6..4a4e631d88c 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -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); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index dd8283e057a..4efdd3edbcd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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)); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 949eaba7311..2b6a307092c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -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; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5348d5e5646..053c85166f6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -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); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b69d582f30b..1182f018ea4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -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; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index d8deba2c939..db904d24bf7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -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); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3b3d8303210..f8bc6210a2f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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 */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ccbaf7c0112..5d6ca5d5de5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -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 { diff --git a/sql/tztime.cc b/sql/tztime.cc index 2ed55f2fa4e..757272d332f 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1359,6 +1359,13 @@ static bool tz_inited= 0; static uint tz_leapcnt= 0; static LS_INFO *tz_lsis= 0; +/* + Shows whenever we have found time zone tables during start-up. + Used for avoiding of putting those tables to global table list + for queries that use time zone info. +*/ +static bool time_zone_tables_exist= 1; + typedef struct st_tz_names_entry: public Sql_alloc { @@ -1388,6 +1395,68 @@ extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, /* + Prepare table list with time zone related tables from preallocated array. + + SYNOPSIS + tz_init_table_list() + tz_tabs - pointer to preallocated array of 4 TABLE_LIST objects. + + DESCRIPTION + This function prepares list of TABLE_LIST objects which can be used + for opening of time zone tables from preallocated array. +*/ + +void +tz_init_table_list(TABLE_LIST *tz_tabs) +{ + bzero(tz_tabs, sizeof(TABLE_LIST) * 4); + tz_tabs[0].alias= tz_tabs[0].real_name= (char*)"time_zone_name"; + tz_tabs[1].alias= tz_tabs[1].real_name= (char*)"time_zone"; + tz_tabs[2].alias= tz_tabs[2].real_name= (char*)"time_zone_transition_type"; + tz_tabs[3].alias= tz_tabs[3].real_name= (char*)"time_zone_transition"; + tz_tabs[0].next= tz_tabs+1; + tz_tabs[1].next= tz_tabs+2; + tz_tabs[2].next= tz_tabs+3; + tz_tabs[0].lock_type= tz_tabs[1].lock_type= tz_tabs[2].lock_type= + tz_tabs[3].lock_type= TL_READ; + tz_tabs[0].db= tz_tabs[1].db= tz_tabs[2].db= tz_tabs[3].db= (char *)"mysql"; +} + + +/* + Create table list with time zone related tables. + + SYNOPSIS + my_tz_get_table_list() + thd - current thread object + + DESCRIPTION + This function creates list of TABLE_LIST objects allocated in thd's + memroot, which can be used for opening of time zone tables. + + RETURN VALUES + Returns pointer to first TABLE_LIST object, (could be 0 if time zone + tables don't exist) and &fake_time_zone_tables_list in case of error. +*/ + +TABLE_LIST * +my_tz_get_table_list(THD *thd) +{ + TABLE_LIST *tz_tabs; + + if (!time_zone_tables_exist) + return 0; + + if (!(tz_tabs= (TABLE_LIST *)thd->alloc(sizeof(TABLE_LIST) * 4))) + return &fake_time_zone_tables_list; + + tz_init_table_list(tz_tabs); + + return tz_tabs; +} + + +/* Initialize time zone support infrastructure. SYNOPSIS @@ -1399,13 +1468,13 @@ extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, DESCRIPTION This function will init memory structures needed for time zone support, it will register mandatory SYSTEM time zone in them. It will try to open - mysql.time_zone_leap_seconds table and and load information which further - will be shared among all time zones loaded. It will also try to load - information about default time zone. If system tables with time zone - descriptions don't exist it won't fail (unless default_tzname is time zone - from tables). If bootstrap parameter is true then this routine assumes that - we are in bootstrap mode and won't load time zone descriptions unless someone - specifies default time zone which is supposedly stored in those tables. + mysql.time_zone* tables and load information about default time zone and + information which further will be shared among all time zones loaded. + If system tables with time zone descriptions don't exist it won't fail + (unless default_tzname is time zone from tables). If bootstrap parameter + is true then this routine assumes that we are in bootstrap mode and won't + load time zone descriptions unless someone specifies default time zone + which is supposedly stored in those tables. It'll also set default time zone if it is specified. RETURN VALUES @@ -1416,14 +1485,13 @@ my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { THD *thd; - TABLE_LIST tables; + TABLE_LIST *tables= 0; + TABLE_LIST tables_buff[5]; TABLE *table; - TABLE *lock_ptr; - MYSQL_LOCK *lock; TZ_NAMES_ENTRY *tmp_tzname; my_bool return_val= 1; int res; - uint not_used; + uint counter; DBUG_ENTER("my_tz_init"); /* @@ -1468,7 +1536,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) if (bootstrap) { /* If we are in bootstrap mode we should not load time zone tables */ - return_val= 0; + return_val= time_zone_tables_exist= 0; goto end_with_setting_default_tz; } @@ -1480,28 +1548,25 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) thd->db= my_strdup("mysql",MYF(0)); thd->db_length= 5; // Safety - bzero((char*) &tables,sizeof(tables)); - tables.alias= tables.real_name= (char*)"time_zone_leap_second"; - tables.lock_type= TL_READ; - tables.db= thd->db; - - if (open_tables(thd, &tables, ¬_used)) + bzero((char*) &tables_buff, sizeof(TABLE_LIST)); + tables_buff[0].alias= tables_buff[0].real_name= + (char*)"time_zone_leap_second"; + tables_buff[0].lock_type= TL_READ; + tables_buff[0].db= thd->db; + tables_buff[0].next= tables_buff + 1; + /* Fill TABLE_LIST for rest of the time zone describing tables */ + tz_init_table_list(tables_buff + 1); + + if (open_tables(thd, tables_buff, &counter) || + lock_tables(thd, tables_buff, counter)) { - sql_print_error("Warning: Can't open time zone table: %s " + sql_print_error("Warning: Can't open and lock time zone table: %s " "trying to live without them", thd->net.last_error); /* We will try emulate that everything is ok */ - return_val= 0; + return_val= time_zone_tables_exist= 0; goto end_with_setting_default_tz; } - - lock_ptr= tables.table; - if (!(lock= mysql_lock_tables(thd, &lock_ptr, 1))) - { - sql_print_error("Fatal error: Can't lock time zone table: %s", - thd->net.last_error); - goto end_with_close; - } - + tables= tables_buff + 1; /* Now we are going to load leap seconds descriptions that are shared @@ -1514,11 +1579,16 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { sql_print_error("Fatal error: Out of memory while loading " "mysql.time_zone_leap_second table"); - goto end_with_unlock; + goto end_with_close; } - table= tables.table; - table->file->ha_index_init(0); + table= tables_buff[0].table; + /* + It is OK to ignore ha_index_init()/ha_index_end() return values since + mysql.time_zone* tables are MyISAM and these operations always succeed + for MyISAM. + */ + (void)table->file->ha_index_init(0); tz_leapcnt= 0; res= table->file->index_first(table->record[0]); @@ -1530,7 +1600,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) sql_print_error("Fatal error: While loading mysql.time_zone_leap_second" " table: too much leaps"); table->file->ha_index_end(); - goto end_with_unlock; + goto end_with_close; } tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int(); @@ -1546,13 +1616,13 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) res= table->file->index_next(table->record[0]); } - table->file->ha_index_end(); + (void)table->file->ha_index_end(); if (res != HA_ERR_END_OF_FILE) { sql_print_error("Fatal error: Error while loading " "mysql.time_zone_leap_second table"); - goto end_with_unlock; + goto end_with_close; } /* @@ -1562,19 +1632,12 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) return_val= 0; -end_with_unlock: - mysql_unlock_tables(thd, lock); - -end_with_close: - close_thread_tables(thd); - thd->version--; /* Force close to free memory */ - end_with_setting_default_tz: - /* If not an error and have default time zone try to load it */ - if (!return_val && default_tzname) + /* If we have default time zone try to load it */ + if (default_tzname) { String tzname(default_tzname, &my_charset_latin1); - if (!(global_system_variables.time_zone= my_tz_find(thd, &tzname))) + if (!(global_system_variables.time_zone= my_tz_find(&tzname, tables))) { sql_print_error("Fatal error: Illegal or unknown default time zone '%s'", default_tzname); @@ -1582,6 +1645,10 @@ end_with_setting_default_tz: } } +end_with_close: + thd->version--; /* Force close to free memory */ + close_thread_tables(thd); + end_with_cleanup: /* if there were error free time zone describing structs */ @@ -1625,29 +1692,27 @@ void my_tz_free() Load time zone description from system tables. SYNOPSIS - tz_load_from_db() - thd - current thread object - tz_name - name of time zone that should be loaded. + tz_load_from_open_tables() + tz_name - name of time zone that should be loaded. + tz_tables - list of tables from which time zone description + should be loaded DESCRIPTION - This function will try to open system tables describing time zones - and to load information about time zone specified. It will also update - information in hash used for time zones lookup. + This function will try to load information about time zone specified + from the list of the already opened and locked tables (first table in + tz_tables should be time_zone_name, next time_zone, then + time_zone_transition_type and time_zone_transition should be last). + It will also update information in hash used for time zones lookup. RETURN VALUES Returns pointer to newly created Time_zone object or 0 in case of error. */ + static Time_zone* -tz_load_from_db(THD *thd, const String *tz_name) +tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) { - TABLE_LIST tables[4]; TABLE *table= 0; - TABLE *lock_ptr[4]; - MYSQL_LOCK *lock; - char system_db_name[]= "mysql"; - char *db_save; - uint db_length_save; TIME_ZONE_INFO *tz_info; TZ_NAMES_ENTRY *tmp_tzname; Time_zone *return_val= 0; @@ -1667,9 +1732,8 @@ tz_load_from_db(THD *thd, const String *tz_name) #ifdef ABBR_ARE_USED char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; #endif - uint not_used; - DBUG_ENTER("tz_load_from_db"); + DBUG_ENTER("tz_load_from_open_tables"); /* Prepare tz_info for loading also let us make copy of time zone name */ @@ -1690,76 +1754,45 @@ tz_load_from_db(THD *thd, const String *tz_name) strmake(tz_name_buff, tz_name->ptr(), tz_name->length()); /* - Open and lock time zone description tables - */ - db_save= thd->db; - db_length_save= thd->db_length; - thd->db= system_db_name; - thd->db_length= 5; - - bzero((char*) &tables,sizeof(tables)); - tables[0].alias= tables[0].real_name= (char*)"time_zone_name"; - tables[1].alias= tables[1].real_name= (char*)"time_zone"; - tables[2].alias= tables[2].real_name= (char*)"time_zone_transition"; - tables[3].alias= tables[3].real_name= (char*)"time_zone_transition_type"; - tables[0].next= tables+1; - tables[1].next= tables+2; - tables[2].next= tables+3; - tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= - tables[3].lock_type= TL_READ; - tables[0].db= tables[1].db= tables[2].db= tables[3].db= thd->db; - if (open_tables(thd, tables, ¬_used)) - { - sql_print_error("Error: Can't open time zone tables: %s", - thd->net.last_error); - goto end; - } - - lock_ptr[0]= tables[0].table; - lock_ptr[1]= tables[1].table; - lock_ptr[2]= tables[2].table; - lock_ptr[3]= tables[3].table; - if (!(lock= mysql_lock_tables(thd, lock_ptr, 4))) - { - sql_print_error("Error: Can't lock time zone tables: %s", - thd->net.last_error); - goto end_with_close; - } - - /* Let us find out time zone id by its name (there is only one index and it is specifically for this purpose). */ - table= tables[0].table; - + table= tz_tables->table; + tz_tables= tz_tables->next; table->field[0]->store(tz_name->ptr(), tz_name->length(), &my_charset_latin1); - table->file->ha_index_init(0); + /* + It is OK to ignore ha_index_init()/ha_index_end() return values since + mysql.time_zone* tables are MyISAM and these operations always succeed + for MyISAM. + */ + (void)table->file->ha_index_init(0); if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 0, HA_READ_KEY_EXACT)) { sql_print_error("Error: Can't find description of time zone."); - goto end_with_unlock; + goto end; } tzid= (uint)table->field[1]->val_int(); - table->file->ha_index_end(); + (void)table->file->ha_index_end(); /* Now we need to lookup record in mysql.time_zone table in order to understand whenever this timezone uses leap seconds (again we are using the only index in this table). */ - table= tables[1].table; + table= tz_tables->table; + tz_tables= tz_tables->next; table->field[0]->store((longlong)tzid); - table->file->ha_index_init(0); + (void)table->file->ha_index_init(0); if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 0, HA_READ_KEY_EXACT)) { sql_print_error("Error: Can't find description of time zone."); - goto end_with_unlock; + goto end; } /* If Uses_leap_seconds == 'Y' */ @@ -1769,7 +1802,7 @@ tz_load_from_db(THD *thd, const String *tz_name) tz_info->lsis= tz_lsis; } - table->file->ha_index_end(); + (void)table->file->ha_index_end(); /* Now we will iterate through records for out time zone in @@ -1777,9 +1810,10 @@ tz_load_from_db(THD *thd, const String *tz_name) only for our time zone guess what are we doing? Right - using special index. */ - table= tables[3].table; + table= tz_tables->table; + tz_tables= tz_tables->next; table->field[0]->store((longlong)tzid); - table->file->ha_index_init(0); + (void)table->file->ha_index_init(0); // FIXME Is there any better approach than explicitly specifying 4 ??? res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, @@ -1793,7 +1827,7 @@ tz_load_from_db(THD *thd, const String *tz_name) sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition_type table: too big " "transition type id"); - goto end_with_unlock; + goto end; } ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int(); @@ -1807,7 +1841,7 @@ tz_load_from_db(THD *thd, const String *tz_name) sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition_type table: not enough " "room for abbreviations"); - goto end_with_unlock; + goto end; } ttis[ttid].tt_abbrind= tz_info->charcnt; memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length()); @@ -1838,10 +1872,10 @@ tz_load_from_db(THD *thd, const String *tz_name) { sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition_type table"); - goto end_with_unlock; + goto end; } - table->file->ha_index_end(); + (void)table->file->ha_index_end(); /* @@ -1849,9 +1883,9 @@ tz_load_from_db(THD *thd, const String *tz_name) mysql.time_zone_transition table. Here we additionaly need records in ascending order by index scan also satisfies us. */ - table= tables[2].table; + table= tz_tables->table; table->field[0]->store((longlong)tzid); - table->file->ha_index_init(0); + (void)table->file->ha_index_init(0); // FIXME Is there any better approach than explicitly specifying 4 ??? res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, @@ -1866,14 +1900,14 @@ tz_load_from_db(THD *thd, const String *tz_name) sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition table: " "too much transitions"); - goto end_with_unlock; + goto end; } if (ttid + 1 > tz_info->typecnt) { sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition table: " "bad transition type id"); - goto end_with_unlock; + goto end; } ats[tz_info->timecnt]= ttime; @@ -1896,10 +1930,10 @@ tz_load_from_db(THD *thd, const String *tz_name) { sql_print_error("Error while loading time zone description from " "mysql.time_zone_transition table"); - goto end_with_unlock; + goto end; } - table->file->ha_index_end(); + (void)table->file->ha_index_end(); table= 0; /* @@ -1916,7 +1950,7 @@ tz_load_from_db(THD *thd, const String *tz_name) { sql_print_error("Error: Out of memory while loading time zone " "description"); - goto end_with_unlock; + goto end; } @@ -1941,12 +1975,12 @@ tz_load_from_db(THD *thd, const String *tz_name) if (tz_info->typecnt < 1) { sql_print_error("Error: loading time zone without transition types"); - goto end_with_unlock; + goto end; } if (prepare_tz_info(tz_info, &tz_storage)) { sql_print_error("Error: Unable to build mktime map for time zone"); - goto end_with_unlock; + goto end; } @@ -1958,7 +1992,7 @@ tz_load_from_db(THD *thd, const String *tz_name) my_hash_insert(&tz_names, (const byte *)tmp_tzname))) { sql_print_error("Error: Out of memory while loading time zone"); - goto end_with_unlock; + goto end; } /* @@ -1966,19 +2000,11 @@ tz_load_from_db(THD *thd, const String *tz_name) */ return_val= tmp_tzname->tz; -end_with_unlock: +end: if (table) - table->file->ha_index_end(); - - mysql_unlock_tables(thd, lock); + (void)table->file->ha_index_end(); -end_with_close: - close_thread_tables(thd); - -end: - thd->db= db_save; - thd->db_length= db_length_save; DBUG_RETURN(return_val); } @@ -2068,8 +2094,8 @@ str_to_offset(const char *str, uint length, long *offset) SYNOPSIS my_tz_find() - thd - current thread name - time zone specification + tz_tables - list of opened'n'locked time zone describing tables DESCRIPTION This function checks if name is one of time zones described in db, @@ -2091,7 +2117,11 @@ str_to_offset(const char *str, uint length, long *offset) values as parameter without additional external check and this property is used by @@time_zone variable handling code). - It will perform lookup in system tables (mysql.time_zone*) if needed. + It will perform lookup in system tables (mysql.time_zone*) if needed + using tz_tables as list of already opened tables (for info about this + list look at tz_load_from_open_tables() description). It won't perform + such lookup if no time zone describing tables were found during server + start up. RETURN VALUE Pointer to corresponding Time_zone object. 0 - in case of bad time zone @@ -2099,7 +2129,7 @@ str_to_offset(const char *str, uint length, long *offset) */ Time_zone * -my_tz_find(THD *thd, const String * name) +my_tz_find(const String * name, TABLE_LIST *tz_tables) { TZ_NAMES_ENTRY *tmp_tzname; Time_zone *result_tz= 0; @@ -2109,6 +2139,8 @@ my_tz_find(THD *thd, const String * name) DBUG_PRINT("enter", ("time zone name='%s'", name ? ((String *)name)->c_ptr() : "NULL")); + DBUG_ASSERT(!time_zone_tables_exist || tz_tables); + if (!name) DBUG_RETURN(0); @@ -2136,8 +2168,10 @@ my_tz_find(THD *thd, const String * name) (const byte *)name->ptr(), name->length()))) result_tz= tmp_tzname->tz; + else if(time_zone_tables_exist) + result_tz= tz_load_from_open_tables(name, tz_tables); else - result_tz= tz_load_from_db(thd, name); + result_tz= 0; } VOID(pthread_mutex_unlock(&tz_LOCK)); diff --git a/sql/tztime.h b/sql/tztime.h index 69ff176326e..aabec260ec7 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -59,7 +59,8 @@ public: 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(); |