diff options
author | unknown <dlenev@brandersnatch.localdomain> | 2004-08-10 12:42:31 +0400 |
---|---|---|
committer | unknown <dlenev@brandersnatch.localdomain> | 2004-08-10 12:42:31 +0400 |
commit | 68d7b266988abb4c24a10282a338c305f3395882 (patch) | |
tree | 4016d026abe9ef6a9eb602093cce08af13970126 /sql/tztime.cc | |
parent | e74007fb7c6257aec7b053d87c7108df1abb1710 (diff) | |
download | mariadb-git-68d7b266988abb4c24a10282a338c305f3395882.tar.gz |
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.
Diffstat (limited to 'sql/tztime.cc')
-rw-r--r-- | sql/tztime.cc | 306 |
1 files changed, 170 insertions, 136 deletions
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)); |