diff options
author | aelkin@mysql.com <> | 2006-04-23 12:32:40 +0300 |
---|---|---|
committer | aelkin@mysql.com <> | 2006-04-23 12:32:40 +0300 |
commit | b2b725dba03ecaf3d0f24bb90a5e6addf8da71a7 (patch) | |
tree | 7f035dff2a1603f1d3e606d81f4919fdc7da70b5 | |
parent | c5219d0d670e6f8c28d447ac662e5c5cc9eba3e7 (diff) | |
parent | a4ff3120370f300ff11ce45ec3000cc6b28fbd31 (diff) | |
download | mariadb-git-b2b725dba03ecaf3d0f24bb90a5e6addf8da71a7.tar.gz |
Merge mysql.com:/usr_rh9/home/elkin.rh9/MySQL/BARE/4.1
into mysql.com:/usr_rh9/home/elkin.rh9/MySQL/Merge/5.0
-rw-r--r-- | mysql-test/t/rpl_temporary.test | 46 | ||||
-rw-r--r-- | sql/sql_base.cc | 3790 |
2 files changed, 891 insertions, 2945 deletions
diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 2400eac76ba..71d7b32b7c9 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -82,7 +82,6 @@ drop temporary table t3; select * from t2; --replace_result $VERSION VERSION ---replace_column 2 # 5 # show binlog events; drop table t1, t2; @@ -130,30 +129,31 @@ create temporary table t3 (f int); sync_with_master; # -# Bug#17284 erroneous temp table cleanup on slave +# BUG#17263 incorrect generation DROP temp tables +# Temporary tables of connection are dropped in batches +# where a batch correspond to pseudo_thread_id +# value was set up at the moment of temp table creation # - +connection con1; +set @session.pseudo_thread_id=100; +create temporary table t101 (id int); +create temporary table t102 (id int); +set @session.pseudo_thread_id=200; +create temporary table t201 (id int); +create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); +set @con1_id=connection_id(); +kill @con1_id; + +#now do something to show that slave is ok after DROP temp tables connection master; -create temporary table t4 (f int); -create table t5 (f int); -sync_with_master; -# find dumper's $id -source include/get_binlog_dump_thread_id.inc; -insert into t4 values (1); -# a hint how to do that in 5.1 ---replace_result $id "`select id from information_schema.processlist where command='Binlog Dump'`" -eval kill $id; # to stimulate reconnection by slave w/o timeout -insert into t5 select * from t4; -save_master_pos; - -connection slave; -sync_with_master; -select * from t5 /* must be 1 after reconnection */; +create table t1(f int); +insert into t1 values (1); -connection master; -drop temporary table t4; -drop table t5; +sync_slave_with_master; +#connection slave; +select * from t1 /* must be 1 */; -# The server will now close done +connection master; +drop table t1; -# End of 5.0 tests +# End of 4.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2b5a3d1f38d..756fb8189dc 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -19,36 +19,30 @@ #include "mysql_priv.h" #include "sql_select.h" -#include "sp_head.h" -#include "sp.h" -#include "sql_trigger.h" #include <m_ctype.h> #include <my_dir.h> #include <hash.h> +#include <nisam.h> #ifdef __WIN__ #include <io.h> #endif TABLE *unused_tables; /* Used by mysql_test */ HASH open_cache; /* Used by mysql_test */ +HASH assign_cache; -static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, - const char *name, const char *alias, - TABLE_LIST *table_list, MEM_ROOT *mem_root); +static int open_unireg_entry(THD *thd,TABLE *entry,const char *db, + const char *name, const char *alias); static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); -static bool open_new_frm(THD *thd, const char *path, const char *alias, - const char *db, const char *table_name, - uint db_stat, uint prgflag, - uint ha_open_flags, TABLE *outparam, - TABLE_LIST *table_desc, MEM_ROOT *mem_root); + extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) { TABLE *entry=(TABLE*) record; - *length= entry->s->key_length; - return (byte*) entry->s->table_cache_key; + *length=entry->key_length; + return (byte*) entry->table_cache_key; } bool table_cache_init(void) @@ -129,11 +123,12 @@ static void check_unused(void) # Pointer to list of names of open tables. */ -OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) +OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) { int result = 0; OPEN_TABLE_LIST **start_list, *open_list; TABLE_LIST table_list; + char name[NAME_LEN*2]; DBUG_ENTER("list_open_tables"); VOID(pthread_mutex_lock(&LOCK_open)); @@ -145,19 +140,20 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) { OPEN_TABLE_LIST *table; TABLE *entry=(TABLE*) hash_element(&open_cache,idx); - TABLE_SHARE *share= entry->s; - DBUG_ASSERT(share->table_name != 0); - if ((!share->table_name)) // To be removed + DBUG_ASSERT(entry->real_name); + if ((!entry->real_name)) // To be removed continue; // Shouldn't happen - if (db && my_strcasecmp(system_charset_info, db, share->db)) - continue; - if (wild && wild_compare(share->table_name,wild,0)) - continue; + if (wild) + { + strxmov(name,entry->table_cache_key,".",entry->real_name,NullS); + if (wild_compare(name,wild,0)) + continue; + } /* Check if user has SELECT privilege for any column in the table */ - table_list.db= (char*) share->db; - table_list.table_name= (char*) share->table_name; + table_list.db= (char*) entry->table_cache_key; + table_list.real_name= entry->real_name; table_list.grant.privilege=0; if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) @@ -165,8 +161,8 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) /* need to check if we haven't already listed it */ for (table= open_list ; table ; table=table->next) { - if (!strcmp(table->table,share->table_name) && - !strcmp(table->db,entry->s->db)) + if (!strcmp(table->table,entry->real_name) && + !strcmp(table->db,entry->table_cache_key)) { if (entry->in_use) table->in_use++; @@ -178,15 +174,15 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) if (table) continue; if (!(*start_list = (OPEN_TABLE_LIST *) - sql_alloc(sizeof(**start_list)+share->key_length))) + sql_alloc(sizeof(**start_list)+entry->key_length))) { open_list=0; // Out of memory break; } strmov((*start_list)->table= strmov(((*start_list)->db= (char*) ((*start_list)+1)), - entry->s->db)+1, - entry->s->table_name); + entry->table_cache_key)+1, + entry->real_name); (*start_list)->in_use= entry->in_use ? 1 : 0; (*start_list)->locked= entry->locked_by_name ? 1 : 0; start_list= &(*start_list)->next; @@ -204,7 +200,6 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) void intern_close_table(TABLE *table) { // Free all structures free_io_cache(table); - delete table->triggers; if (table->file) VOID(closefrm(table)); // close file } @@ -287,9 +282,9 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, else { bool found=0; - for (TABLE_LIST *table= tables; table; table= table->next_local) + for (TABLE_LIST *table=tables ; table ; table=table->next) { - if (remove_table_from_cache(thd, table->db, table->table_name, + if (remove_table_from_cache(thd, table->db, table->real_name, RTFC_OWNED_BY_THD_FLAG)) found=1; } @@ -323,7 +318,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, for (uint idx=0 ; idx < open_cache.records ; idx++) { TABLE *table=(TABLE*) hash_element(&open_cache,idx); - if ((table->s->version) < refresh_version && table->db_stat) + if ((table->version) < refresh_version && table->db_stat) { found=1; pthread_cond_wait(&COND_refresh,&LOCK_open); @@ -340,8 +335,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, result=reopen_tables(thd,1,1); thd->in_lock_tables=0; /* Set version for table */ - for (TABLE *table=thd->open_tables; table ; table= table->next) - table->s->version= refresh_version; + for (TABLE *table=thd->open_tables; table ; table=table->next) + table->version=refresh_version; } VOID(pthread_mutex_unlock(&LOCK_open)); if (if_wait_for_refresh) @@ -357,39 +352,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, /* - Mark all tables in the list which were used by current substatement - as free for reuse. - - SYNOPSIS - mark_used_tables_as_free_for_reuse() - thd - thread context - table - head of the list of tables - - DESCRIPTION - Marks all tables in the list which were used by current substatement - (they are marked by its query_id) as free for reuse. - - NOTE - The reason we reset query_id is that it's not enough to just test - if table->query_id != thd->query_id to know if a table is in use. - - For example - SELECT f1_that_uses_t1() FROM t1; - In f1_that_uses_t1() we will see one instance of t1 where query_id is - set to query_id of original query. -*/ - -static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) -{ - for (; table ; table= table->next) - if (table->query_id == thd->query_id) - table->query_id= 0; -} - - -/* - Close all tables used by the current substatement, or all tables - used by this thread if we are on the upper level. + Close all tables used by thread SYNOPSIS close_thread_tables() @@ -398,42 +361,23 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) LOCK_open skip_derived Set to 1 (0 = default) if we should not free derived tables. - stopper When closing tables from thd->open_tables(->next)*, - don't close/remove tables starting from stopper. IMPLEMENTATION Unlocks tables and frees derived tables. Put all normal tables used by thread in free list. - - When in prelocked mode it will only close/mark as free for reuse - tables opened by this substatement, it will also check if we are - closing tables after execution of complete query (i.e. we are on - upper level) and will leave prelocked mode if needed. */ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) { bool found_old_table; - prelocked_mode_type prelocked_mode= thd->prelocked_mode; DBUG_ENTER("close_thread_tables"); - /* - We are assuming here that thd->derived_tables contains ONLY derived - tables for this substatement. i.e. instead of approach which uses - query_id matching for determining which of the derived tables belong - to this substatement we rely on the ability of substatements to - save/restore thd->derived_tables during their execution. - - TODO: Probably even better approach is to simply associate list of - derived tables with (sub-)statement instead of thread and destroy - them at the end of its execution. - */ if (thd->derived_tables && !skip_derived) { TABLE *table, *next; /* - Close all derived tables generated in queries like - SELECT * FROM (SELECT * FROM t1) + Close all derived tables generated from questions like + SELECT * from (select * from t1)) */ for (table= thd->derived_tables ; table ; table= next) { @@ -442,55 +386,10 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) } thd->derived_tables= 0; } - - if (prelocked_mode) + if (thd->locked_tables) { - /* - Mark all temporary tables used by this substatement as free for reuse. - */ - mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables); - } - - if (thd->locked_tables || prelocked_mode) - { - /* - Let us commit transaction for statement. Since in 5.0 we only have - one statement transaction and don't allow several nested statement - transactions this call will do nothing if we are inside of stored - function or trigger (i.e. statement transaction is already active and - does not belong to statement for which we do close_thread_tables()). - TODO: This should be fixed in later releases. - */ - ha_commit_stmt(thd); - - /* We are under simple LOCK TABLES so should not do anything else. */ - if (!prelocked_mode) - DBUG_VOID_RETURN; - - if (!thd->lex->requires_prelocking()) - { - /* - If we are executing one of substatements we have to mark - all tables which it used as free for reuse. - */ - mark_used_tables_as_free_for_reuse(thd, thd->open_tables); - DBUG_VOID_RETURN; - } - - DBUG_ASSERT(prelocked_mode); - /* - We are in prelocked mode, so we have to leave it now with doing - implicit UNLOCK TABLES if need. - */ - DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED")); - thd->prelocked_mode= NON_PRELOCKED; - - if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES) - DBUG_VOID_RETURN; - - thd->lock= thd->locked_tables; - thd->locked_tables= 0; - /* Fallthrough */ + ha_commit_stmt(thd); // If select statement + DBUG_VOID_RETURN; // LOCK TABLES in use } if (thd->lock) @@ -498,24 +397,12 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) mysql_unlock_tables(thd, thd->lock); thd->lock=0; } - /* - assume handlers auto-commit (if some doesn't - transaction handling - in MySQL should be redesigned to support it; it's a big change, - and it's not worth it - better to commit explicitly only writing - transactions, read-only ones should better take care of themselves. - saves some work in 2pc too) - see also sql_parse.cc - dispatch_command() - */ - bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt)); - if (!thd->active_transaction()) - thd->transaction.xid_state.xid.null(); - /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */ if (!lock_in_use) VOID(pthread_mutex_lock(&LOCK_open)); safe_mutex_assert_owner(&LOCK_open); - DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables)); + DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables)); found_old_table= 0; while (thd->open_tables) @@ -534,17 +421,6 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) if (!lock_in_use) VOID(pthread_mutex_unlock(&LOCK_open)); /* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */ - - if (prelocked_mode == PRELOCKED) - { - /* - If we are here then we are leaving normal prelocked mode, so it is - good idea to turn off OPTION_TABLE_LOCK flag. - */ - DBUG_ASSERT(thd->lex->requires_prelocking()); - thd->options&= ~(ulong) (OPTION_TABLE_LOCK); - } - DBUG_VOID_RETURN; } @@ -552,14 +428,14 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) bool close_thread_table(THD *thd, TABLE **table_ptr) { + DBUG_ENTER("close_thread_table"); + bool found_old_table= 0; TABLE *table= *table_ptr; - DBUG_ENTER("close_thread_table"); DBUG_ASSERT(table->key_read == 0); - DBUG_ASSERT(table->file->inited == handler::NONE); *table_ptr=table->next; - if (table->s->version != refresh_version || + if (table->version != refresh_version || thd->version != refresh_version || !table->db_stat) { VOID(hash_delete(&open_cache,(byte*) table)); @@ -567,9 +443,9 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) } else { - if (table->s->flush_version != flush_version) + if (table->flush_version != flush_version) { - table->s->flush_version= flush_version; + table->flush_version=flush_version; table->file->extra(HA_EXTRA_FLUSH); } else @@ -597,8 +473,8 @@ void close_temporary(TABLE *table,bool delete_table) { DBUG_ENTER("close_temporary"); char path[FN_REFLEN]; - db_type table_type=table->s->db_type; - strmov(path,table->s->path); + db_type table_type=table->db_type; + strmov(path,table->path); free_io_cache(table); closefrm(table); my_free((char*) table,MYF(0)); @@ -607,13 +483,23 @@ void close_temporary(TABLE *table,bool delete_table) DBUG_VOID_RETURN; } +/* close_temporary_tables' internal */ +static inline uint tmpkeyval(THD *thd, TABLE *table) +{ + return uint4korr(table->table_cache_key + table->key_length - + sizeof(thd->variables.pseudo_thread_id)); +} + +/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */ void close_temporary_tables(THD *thd) { - TABLE *table,*next; - char *query, *end; - uint query_buf_size; - bool found_user_tables = 0; + TABLE *next, + *prev_table /* prev link is not maintained in TABLE's double-linked list */, + *table; + char *query= (gptr) 0, *end; + uint query_buf_size, max_names_len; + bool found_user_tables; if (!thd->temporary_tables) return; @@ -621,225 +507,176 @@ void close_temporary_tables(THD *thd) LINT_INIT(end); query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS - for (table=thd->temporary_tables ; table ; table=table->next) + /* + insertion sort of temp tables by pseudo_thread_id to build ordered list + of sublists of equal pseudo_thread_id + */ + for (prev_table= thd->temporary_tables, + table= prev_table->next, + found_user_tables= (prev_table->table_name[0] != '#'); + table; + prev_table= table, table= table->next) + { + TABLE *prev_sorted /* same as for prev_table */, + *sorted; /* - We are going to add 4 ` around the db/table names, so 1 does not look - enough; indeed it is enough, because table->key_length is greater (by 8, - because of server_id and thread_id) than db||table. + table not created directly by the user is moved to the tail. + Fixme/todo: nothing (I checked the manual) prevents user to create temp + with `#' */ - query_buf_size+= table->s->key_length+1; - - if ((query = alloc_root(thd->mem_root, query_buf_size))) - // Better add "if exists", in case a RESET MASTER has been done - end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); - - for (table=thd->temporary_tables ; table ; table=next) + if (table->real_name[0] == '#') + continue; + else + { + found_user_tables = 1; + } + for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table; + prev_sorted= sorted, sorted= sorted->next) + { + if (sorted->real_name[0] == '#' || tmpkeyval(thd, sorted) > tmpkeyval(thd, table)) + { + /* move into the sorted part of the list from the unsorted */ + prev_table->next= table->next; + table->next= sorted; + if (prev_sorted) + { + prev_sorted->next= table; + } + else + { + thd->temporary_tables= table; + } + table= prev_table; + break; + } + } + } + /* + calc query_buf_size as max per sublists, one sublist per pseudo thread id. + Also stop at first occurence of `#'-named table that starts + all implicitly created temp tables + */ + for (max_names_len= 0, table=thd->temporary_tables; + table && table->real_name[0] != '#'; + table=table->next) { - if (query) // we might be out of memory, but this is not fatal + uint tmp_names_len; + for (tmp_names_len= table->key_length + 1; + table->next && table->real_name[0] != '#' && + tmpkeyval(thd, table) == tmpkeyval(thd, table->next); + table=table->next) { - // skip temporary tables not created directly by the user - if (table->s->table_name[0] != '#') - found_user_tables = 1; - end = strxmov(end,"`",table->s->db,"`.`", - table->s->table_name,"`,", NullS); + /* + We are going to add 4 ` around the db/table names, so 1 might not look + enough; indeed it is enough, because table->key_length is greater (by 8, + because of server_id and thread_id) than db||table. + */ + tmp_names_len += table->next->key_length + 1; } - next=table->next; - close_temporary(table, 1); + if (tmp_names_len > max_names_len) max_names_len= tmp_names_len; } - if (query && found_user_tables && mysql_bin_log.is_open()) - { - /* The -1 is to remove last ',' */ - thd->clear_error(); - Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE); - /* - Imagine the thread had created a temp table, then was doing a SELECT, and - the SELECT was killed. Then it's not clever to mark the statement above as - "killed", because it's not really a statement updating data, and there - are 99.99% chances it will succeed on slave. - If a real update (one updating a persistent table) was killed on the - master, then this real update will be logged with error_code=killed, - rightfully causing the slave to stop. - */ - qinfo.error_code= 0; - mysql_bin_log.write(&qinfo); + + /* allocate */ + if (found_user_tables && mysql_bin_log.is_open() && + (query = alloc_root(thd->mem_root, query_buf_size+= max_names_len))) + // Better add "if exists", in case a RESET MASTER has been done + end= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); + + /* scan sorted tmps to generate sequence of DROP */ + for (table=thd->temporary_tables; table; table= next) + { + if (query // we might be out of memory, but this is not fatal + && table->real_name[0] != '#') + { + char *end_cur; + /* Set pseudo_thread_id to be that of the processed table */ + thd->variables.pseudo_thread_id= tmpkeyval(thd, table); + /* Loop forward through all tables within the sublist of + common pseudo_thread_id to create single DROP query */ + for (end_cur= end; + table && table->real_name[0] != '#' && + tmpkeyval(thd, table) == thd->variables.pseudo_thread_id; + table= next) + { + end_cur= strxmov(end_cur, "`", table->table_cache_key, "`.`", + table->real_name, "`,", NullS); + next= table->next; + close_temporary(table, 1); + } + thd->clear_error(); + /* The -1 is to remove last ',' */ + Query_log_event qinfo(thd, query, (ulong)(end_cur - query) - 1, 0, FALSE); + /* + Imagine the thread had created a temp table, then was doing a SELECT, and + the SELECT was killed. Then it's not clever to mark the statement above as + "killed", because it's not really a statement updating data, and there + are 99.99% chances it will succeed on slave. + If a real update (one updating a persistent table) was killed on the + master, then this real update will be logged with error_code=killed, + rightfully causing the slave to stop. + */ + qinfo.error_code= 0; + mysql_bin_log.write(&qinfo); + } + else + { + next= table->next; + close_temporary(table, 1); + } } thd->temporary_tables=0; } - /* - Find table in list. + Find first suitable table by alias in given list. SYNOPSIS find_table_in_list() - table Pointer to table list - offset Offset to which list in table structure to use - db_name Data base name - table_name Table name - - NOTES: - This is called by find_table_in_local_list() and - find_table_in_global_list(). + table - pointer to table list + db_name - data base name or 0 for any + table_name - table name or 0 for any RETURN VALUES NULL Table not found # Pointer to found table. */ -TABLE_LIST *find_table_in_list(TABLE_LIST *table, - st_table_list *TABLE_LIST::*link, - const char *db_name, - const char *table_name) +TABLE_LIST * find_table_in_list(TABLE_LIST *table, + const char *db_name, const char *table_name) { - for (; table; table= table->*link ) - { - if ((table->table == 0 || table->table->s->tmp_table == NO_TMP_TABLE) && - strcmp(table->db, db_name) == 0 && - strcmp(table->table_name, table_name) == 0) + for (; table; table= table->next) + if ((!db_name || !strcmp(table->db, db_name)) && + (!table_name || !my_strcasecmp(table_alias_charset, + table->alias, table_name))) break; - } return table; } - /* - Test that table is unique (It's only exists once in the table list) + Find real table in given list. SYNOPSIS - unique_table() - thd thread handle - table table which should be checked - table_list list of tables - - NOTE: to exclude derived tables from check we use following mechanism: - a) during derived table processing set THD::derived_tables_processing - b) JOIN::prepare set SELECT::exclude_from_table_unique_test if - THD::derived_tables_processing set. (we can't use JOIN::execute - because for PS we perform only JOIN::prepare, but we can't set this - flag in JOIN::prepare if we are not sure that we are in derived table - processing loop, because multi-update call fix_fields() for some its - items (which mean JOIN::prepare for subqueries) before unique_table - call to detect which tables should be locked for write). - c) unique_table skip all tables which belong to SELECT with - SELECT::exclude_from_table_unique_test set. - Also SELECT::exclude_from_table_unique_test used to exclude from check - tables of main SELECT of multi-delete and multi-update - - TODO: when we will have table/view change detection we can do this check - only once for PS/SP + find_real_table_in_list() + table - pointer to table list + db_name - data base name + table_name - table name - RETURN - found duplicate - 0 if table is unique + RETURN VALUES + NULL Table not found + # Pointer to found table. */ -TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) +TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) { - TABLE_LIST *res; - const char *d_name, *t_name; - DBUG_ENTER("unique_table"); - DBUG_PRINT("enter", ("table alias: %s", table->alias)); - - /* - If this function called for query which update table (INSERT/UPDATE/...) - then we have in table->table pointer to TABLE object which we are - updating even if it is VIEW so we need TABLE_LIST of this TABLE object - to get right names (even if lower_case_table_names used). - - If this function called for CREATE command that we have not opened table - (table->table equal to 0) and right names is in current TABLE_LIST - object. - */ - if (table->table) - { - /* temporary table is always unique */ - if (table->table && table->table->s->tmp_table != NO_TMP_TABLE) - DBUG_RETURN(0); - table= table->find_underlying_table(table->table); - /* - as far as we have table->table we have to find real TABLE_LIST of - it in underlying tables - */ - DBUG_ASSERT(table); - } - d_name= table->db; - t_name= table->table_name; - - DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name)); - for (;;) - { - if (((! (res= find_table_in_global_list(table_list, d_name, t_name))) && - (! (res= mysql_lock_have_duplicate(thd, table, table_list)))) || - ((!res->table || res->table != table->table) && - res->select_lex && !res->select_lex->exclude_from_table_unique_test)) + for (; table; table= table->next) + if (!strcmp(table->db, db_name) && + !strcmp(table->real_name, table_name)) break; - /* - If we found entry of this table or or table of SELECT which already - processed in derived table or top select of multi-update/multi-delete - (exclude_from_table_unique_test). - */ - table_list= res->next_global; - DBUG_PRINT("info", - ("found same copy of table or table which we should skip")); - } - DBUG_RETURN(res); -} - - -/* - Issue correct error message in case we found 2 duplicate tables which - prevent some update operation - - SYNOPSIS - update_non_unique_table_error() - update table which we try to update - operation name of update operation - duplicate duplicate table which we found - - NOTE: - here we hide view underlying tables if we have them -*/ - -void update_non_unique_table_error(TABLE_LIST *update, - const char *operation, - TABLE_LIST *duplicate) -{ - update= update->top_table(); - duplicate= duplicate->top_table(); - if (!update->view || !duplicate->view || - update->view == duplicate->view || - update->view_name.length != duplicate->view_name.length || - update->view_db.length != duplicate->view_db.length || - my_strcasecmp(table_alias_charset, - update->view_name.str, duplicate->view_name.str) != 0 || - my_strcasecmp(table_alias_charset, - update->view_db.str, duplicate->view_db.str) != 0) - { - /* - it is not the same view repeated (but it can be parts of the same copy - of view), so we have to hide underlying tables. - */ - if (update->view) - { - if (update->view == duplicate->view) - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), update->alias, operation); - else - my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), - (duplicate->view ? duplicate->alias : update->alias), - operation, update->alias); - return; - } - if (duplicate->view) - { - my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias, operation, - update->alias); - return; - } - } - my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias); + return table; } - TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; @@ -854,8 +691,8 @@ TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) prev= &thd->temporary_tables; for (table=thd->temporary_tables ; table ; table=table->next) { - if (table->s->key_length == key_length && - !memcmp(table->s->table_cache_key,key,key_length)) + if (table->key_length == key_length && + !memcmp(table->table_cache_key,key,key_length)) return prev; prev= &table->next; } @@ -870,7 +707,7 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) return 1; table= *prev; *prev= table->next; - close_temporary(table, 1); + close_temporary(table); if (thd->slave_thread) --slave_open_temp_tables; return 0; @@ -883,26 +720,22 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) Prepares a table cache key, which is the concatenation of db, table_name and thd->slave_proxy_id, separated by '\0'. */ - bool rename_temporary_table(THD* thd, TABLE *table, const char *db, const char *table_name) { char *key; - TABLE_SHARE *share= table->s; - if (!(key=(char*) alloc_root(&table->mem_root, (uint) strlen(db)+ (uint) strlen(table_name)+6+4))) return 1; /* purecov: inspected */ - share->key_length= (uint) - (strmov((char*) (share->table_name= strmov(share->table_cache_key= key, - db)+1), - table_name) - share->table_cache_key)+1; - share->db= share->table_cache_key; - int4store(key+share->key_length, thd->server_id); - share->key_length+= 4; - int4store(key+share->key_length, thd->variables.pseudo_thread_id); - share->key_length+= 4; + table->key_length=(uint) + (strmov((table->real_name=strmov(table->table_cache_key=key, + db)+1), + table_name) - table->table_cache_key)+1; + int4store(key+table->key_length,thd->server_id); + table->key_length += 4; + int4store(key+table->key_length,thd->variables.pseudo_thread_id); + table->key_length += 4; return 0; } @@ -933,16 +766,15 @@ static void relink_unused(TABLE *table) TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find) { char key[MAX_DBKEY_LENGTH]; - uint key_length= find->s->key_length; + uint key_length=find->key_length; TABLE *start=list,**prev,*next; prev= &start; - - memcpy(key, find->s->table_cache_key, key_length); + memcpy(key,find->table_cache_key,key_length); for (; list ; list=next) { next=list->next; - if (list->s->key_length == key_length && - !memcmp(list->s->table_cache_key, key, key_length)) + if (list->key_length == key_length && + !memcmp(list->table_cache_key,key,key_length)) { if (thd->locked_tables) mysql_lock_remove(thd, thd->locked_tables,list); @@ -988,254 +820,113 @@ void wait_for_refresh(THD *thd) } -/* - Open table which is already name-locked by this thread. - - SYNOPSIS - reopen_name_locked_table() - thd Thread handle - table_list TABLE_LIST object for table to be open, TABLE_LIST::table - member should point to TABLE object which was used for - name-locking. - - NOTE - This function assumes that its caller already acquired LOCK_open mutex. - - RETURN VALUE - FALSE - Success - TRUE - Error -*/ - -bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) +TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) { - TABLE *table= table_list->table; - TABLE_SHARE *share; - char *db= table_list->db; - char *table_name= table_list->table_name; - char key[MAX_DBKEY_LENGTH]; - uint key_length; - TABLE orig_table; DBUG_ENTER("reopen_name_locked_table"); + if (thd->killed) + DBUG_RETURN(0); + TABLE* table; + if (!(table = table_list->table)) + DBUG_RETURN(0); - safe_mutex_assert_owner(&LOCK_open); - - if (thd->killed || !table) - DBUG_RETURN(TRUE); - - orig_table= *table; + char* db = thd->db ? thd->db : table_list->db; + char* table_name = table_list->real_name; + char key[MAX_DBKEY_LENGTH]; + uint key_length; key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - if (open_unireg_entry(thd, table, db, table_name, table_name, 0, - thd->mem_root) || - !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key, - key_length))) + pthread_mutex_lock(&LOCK_open); + if (open_unireg_entry(thd, table, db, table_name, table_name) || + !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, + key_length))) { - intern_close_table(table); - /* - If there was an error during opening of table (for example if it - does not exist) '*table' object can be wiped out. To be able - properly release name-lock in this case we should restore this - object to its original state. - */ - *table= orig_table; - DBUG_RETURN(TRUE); + closefrm(table); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); } - share= table->s; - share->db= share->table_cache_key; - share->key_length=key_length; - share->version=0; - share->flush_version=0; + table->key_length=key_length; + table->version=0; + table->flush_version=0; table->in_use = thd; check_unused(); + pthread_mutex_unlock(&LOCK_open); table->next = thd->open_tables; thd->open_tables = table; table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->null_row= table->maybe_null= table->force_index= 0; + table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query= share->keys_in_use; - table->used_keys= share->keys_for_keyread; - DBUG_RETURN(FALSE); + table->keys_in_use_for_query= table->keys_in_use; + table->used_keys= table->keys_for_keyread; + DBUG_RETURN(table); } -/* - Open a table. - - SYNOPSIS - open_table() - thd Thread context. - table_list Open first table in list. - refresh INOUT Pointer to memory that will be set to 1 if - we need to close all tables and reopen them. - If this is a NULL pointer, then the table is not - put in the thread-open-list. - flags Bitmap of flags to modify how open works: - MYSQL_LOCK_IGNORE_FLUSH - Open table even if - someone has done a flush or namelock on it. - No version number checking is done. - - IMPLEMENTATION - Uses a cache of open tables to find a table not in use. - - RETURN - NULL Open failed. If refresh is set then one should close - all other tables and retry the open. - # Success. Pointer to TABLE object for open table. -*/ +/****************************************************************************** +** open a table +** Uses a cache of open tables to find a table not in use. +** If refresh is a NULL pointer, then the is no version number checking and +** the table is not put in the thread-open-list +** If the return value is NULL and refresh is set then one must close +** all tables and retry the open +******************************************************************************/ -TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, - bool *refresh, uint flags) +TABLE *open_table(THD *thd,const char *db,const char *table_name, + const char *alias,bool *refresh) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; uint key_length; - char *alias= table_list->alias; HASH_SEARCH_STATE state; DBUG_ENTER("open_table"); /* find a unused table in the open table cache */ if (refresh) *refresh=0; - - /* an open table operation needs a lot of the stack space */ - if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (char *)&alias)) - return 0; - if (thd->killed) DBUG_RETURN(0); - key_length= (uint) (strmov(strmov(key, table_list->db)+1, - table_list->table_name)-key)+1; + key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1; int4store(key + key_length, thd->server_id); int4store(key + key_length + 4, thd->variables.pseudo_thread_id); - if (!table_list->skip_temporary) + for (table=thd->temporary_tables; table ; table=table->next) { - for (table= thd->temporary_tables; table ; table=table->next) + if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA && + !memcmp(table->table_cache_key, key, + key_length + TMP_TABLE_KEY_EXTRA)) { - if (table->s->key_length == key_length + TMP_TABLE_KEY_EXTRA && - !memcmp(table->s->table_cache_key, key, - key_length + TMP_TABLE_KEY_EXTRA)) + if (table->query_id == thd->query_id) { - if (table->query_id == thd->query_id || - thd->prelocked_mode && table->query_id) - { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); - DBUG_RETURN(0); - } - table->query_id= thd->query_id; - table->clear_query_id= 1; - thd->tmp_table_used= 1; - DBUG_PRINT("info",("Using temporary table")); - goto reset; + my_printf_error(ER_CANT_REOPEN_TABLE, + ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name); + DBUG_RETURN(0); } + table->query_id=thd->query_id; + table->clear_query_id=1; + thd->tmp_table_used= 1; + DBUG_PRINT("info",("Using temporary table")); + goto reset; } } - if (thd->locked_tables || thd->prelocked_mode) + if (thd->locked_tables) { // Using table locks - TABLE *best_table= 0; - int best_distance= INT_MIN; - bool check_if_used= thd->prelocked_mode && - ((int) table_list->lock_type >= - (int) TL_WRITE_ALLOW_WRITE); for (table=thd->open_tables; table ; table=table->next) { - if (table->s->key_length == key_length && - !memcmp(table->s->table_cache_key, key, key_length)) + if (table->key_length == key_length && + !memcmp(table->table_cache_key,key,key_length) && + !my_strcasecmp(system_charset_info, table->table_name, alias) && + table->query_id != thd->query_id) { - if (check_if_used && table->query_id && - table->query_id != thd->query_id) - { - /* - If we are in stored function or trigger we should ensure that - we won't change table that is already used by calling statement. - So if we are opening table for writing, we should check that it - is not already open by some calling stamement. - */ - my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0), - table->s->table_name); - DBUG_RETURN(0); - } - if (!my_strcasecmp(system_charset_info, table->alias, alias) && - table->query_id != thd->query_id && /* skip tables already used */ - !(thd->prelocked_mode && table->query_id)) - { - int distance= ((int) table->reginfo.lock_type - - (int) table_list->lock_type); - /* - Find a table that either has the exact lock type requested, - or has the best suitable lock. In case there is no locked - table that has an equal or higher lock than requested, - we us the closest matching lock to be able to produce an error - message about wrong lock mode on the table. The best_table - is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd. - - distance < 0 - No suitable lock found - distance > 0 - we have lock mode higher then we require - distance == 0 - we have lock mode exactly which we need - */ - if (best_distance < 0 && distance > best_distance || - distance >= 0 && distance < best_distance) - { - best_distance= distance; - best_table= table; - if (best_distance == 0 && !check_if_used) - { - /* - If we have found perfect match and we don't need to check that - table is not used by one of calling statements (assuming that - we are inside of function or trigger) we can finish iterating - through open tables list. - */ - break; - } - } - } + table->query_id=thd->query_id; + DBUG_PRINT("info",("Using locked table")); + goto reset; } } - if (best_table) - { - table= best_table; - table->query_id= thd->query_id; - DBUG_PRINT("info",("Using locked table")); - goto reset; - } - /* - is it view? - (it is work around to allow to open view with locked tables, - real fix will be made after definition cache will be made) - */ - { - char path[FN_REFLEN]; - db_type not_used; - strxnmov(path, FN_REFLEN, mysql_data_home, "/", table_list->db, "/", - table_list->table_name, reg_ext, NullS); - (void) unpack_filename(path, path); - if (mysql_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW) - { - /* - Will not be used (because it's VIEW) but has to be passed. - Also we will not free it (because it is a stack variable). - */ - TABLE tab; - table= &tab; - VOID(pthread_mutex_lock(&LOCK_open)); - if (!open_unireg_entry(thd, table, table_list->db, - table_list->table_name, - alias, table_list, mem_root)) - { - DBUG_ASSERT(table_list->view != 0); - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(0); // VIEW - } - VOID(pthread_mutex_unlock(&LOCK_open)); - } - } - my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); + my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias); DBUG_RETURN(0); } @@ -1243,19 +934,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (!thd->open_tables) thd->version=refresh_version; - else if ((thd->version != refresh_version) && - ! (flags & MYSQL_LOCK_IGNORE_FLUSH)) + else if (thd->version != refresh_version && refresh) { /* Someone did a refresh while thread was opening tables */ - if (refresh) - *refresh=1; + *refresh=1; VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(0); } /* close handler tables which are marked for flush */ - if (thd->handler_tables) - mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE); + mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE); for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length, &state); @@ -1263,26 +951,24 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table= (TABLE*) hash_next(&open_cache, (byte*) key, key_length, &state)) { - if (table->s->version != refresh_version) + if (table->version != refresh_version) { - if (flags & MYSQL_LOCK_IGNORE_FLUSH) + if (! refresh) { - /* Force close at once after usage */ - thd->version= table->s->version; + /* Ignore flush for now, but force close after usage. */ + thd->version= table->version; continue; } /* - There is a refresh in progress for this table - Wait until the table is freed or the thread is killed. + ** There is a refresh in progress for this table + ** Wait until the table is freed or the thread is killed. */ close_old_data_files(thd,thd->open_tables,0,0); if (table->in_use != thd) wait_for_refresh(thd); else - { VOID(pthread_mutex_unlock(&LOCK_open)); - } if (refresh) *refresh=1; DBUG_RETURN(0); @@ -1298,11 +984,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } table->prev->next=table->next; /* Remove from unused list */ table->next->prev=table->prev; - table->in_use= thd; + } else { - TABLE_SHARE *share; /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */ @@ -1313,35 +998,25 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (open_unireg_entry(thd, table, table_list->db, table_list->table_name, - alias, table_list, mem_root) || - (!table_list->view && - !(table->s->table_cache_key= memdup_root(&table->mem_root, - (char*) key, - key_length)))) + if (open_unireg_entry(thd, table,db,table_name,alias) || + !(table->table_cache_key=memdup_root(&table->mem_root,(char*) key, + key_length))) { table->next=table->prev=table; free_cache_entry(table); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (table_list->view) - { - my_free((gptr)table, MYF(0)); - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(0); // VIEW - } - share= table->s; - share->db= share->table_cache_key; - share->key_length= key_length; - share->version= refresh_version; - share->flush_version= flush_version; + table->key_length=key_length; + table->version=refresh_version; + table->flush_version=flush_version; DBUG_PRINT("info", ("inserting table %p into the cache", table)); VOID(my_hash_insert(&open_cache,(byte*) table)); } + table->in_use=thd; check_unused(); // Debugging call - + VOID(pthread_mutex_unlock(&LOCK_open)); if (refresh) { @@ -1351,32 +1026,55 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->reginfo.lock_type=TL_READ; /* Assume read */ reset: - if (thd->lex->need_correct_ident()) - table->alias_name_used= my_strcasecmp(table_alias_charset, - table->s->table_name, alias); /* Fix alias if table name changes */ - if (strcmp(table->alias, alias)) + if (strcmp(table->table_name,alias)) { uint length=(uint) strlen(alias)+1; - table->alias= (char*) my_realloc((char*) table->alias, length, - MYF(MY_WME)); - memcpy((char*) table->alias, alias, length); + table->table_name= (char*) my_realloc(table->table_name,length, + MYF(MY_WME)); + memcpy(table->table_name,alias,length); + for (uint i=0 ; i < table->fields ; i++) + table->field[i]->table_name=table->table_name; + } +#if MYSQL_VERSION_ID < 40100 + /* + If per-connection "new" variable (represented by variables.new_mode) + is set then we should pretend that the length of TIMESTAMP field is 19. + The cheapest (from perfomance viewpoint) way to achieve that is to set + field_length of all Field_timestamp objects in a table after opening + it (to 19 if new_mode is true or to original field length otherwise). + We save value of new_mode variable in TABLE::timestamp_mode to + not perform this setup if new_mode value is the same between sequential + table opens. + */ + my_bool new_mode= thd->variables.new_mode; + if (table->timestamp_mode != new_mode) + { + for (uint i=0 ; i < table->fields ; i++) + { + Field *field= table->field[i]; + + if (field->type() == FIELD_TYPE_TIMESTAMP) + field->field_length= new_mode ? 19 : + ((Field_timestamp *)(field))->orig_field_length; + } + table->timestamp_mode= new_mode; } +#endif /* These variables are also set in reopen_table() */ table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->null_row= table->maybe_null= table->force_index= 0; + table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query= table->s->keys_in_use; - table->insert_values= 0; - table->used_keys= table->s->keys_for_keyread; - table->fulltext_searched= 0; - table->file->ft_handler= 0; + table->keys_in_use_for_query= table->keys_in_use; + table->used_keys= table->keys_for_keyread; + table->file->ft_handler=0; + table->fulltext_searched=0; if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); - table_list->updatable= 1; // It is not derived table nor non-updatable VIEW DBUG_ASSERT(table->key_read == 0); + DBUG_ASSERT(table->insert_values == 0); DBUG_RETURN(table); } @@ -1388,8 +1086,8 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name) for (TABLE *table=thd->open_tables; table ; table=table->next) { - if (table->s->key_length == key_length && - !memcmp(table->s->table_cache_key,key,key_length)) + if (table->key_length == key_length && + !memcmp(table->table_cache_key,key,key_length)) return table; } return(0); @@ -1416,9 +1114,9 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name) bool reopen_table(TABLE *table,bool locked) { TABLE tmp; - char *db= table->s->table_cache_key; - const char *table_name= table->s->table_name; - bool error= 1; + char *db=table->table_cache_key; + char *table_name=table->real_name; + bool error=1; Field **field; uint key,part; DBUG_ENTER("reopen_table"); @@ -1426,70 +1124,64 @@ bool reopen_table(TABLE *table,bool locked) #ifdef EXTRA_DEBUG if (table->db_stat) sql_print_error("Table %s had a open data handler in reopen_table", - table->alias); + table->table_name); #endif if (!locked) VOID(pthread_mutex_lock(&LOCK_open)); safe_mutex_assert_owner(&LOCK_open); - if (open_unireg_entry(table->in_use, &tmp, db, table_name, - table->alias, 0, table->in_use->mem_root)) + if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name)) goto end; free_io_cache(table); - if (!(tmp.s->table_cache_key= memdup_root(&tmp.mem_root,db, - table->s->key_length))) + if (!(tmp.table_cache_key= memdup_root(&tmp.mem_root,db, + table->key_length))) { - delete tmp.triggers; closefrm(&tmp); // End of memory goto end; } - tmp.s->db= tmp.s->table_cache_key; /* This list copies variables set by open_table */ tmp.tablenr= table->tablenr; tmp.used_fields= table->used_fields; tmp.const_table= table->const_table; + tmp.outer_join= table->outer_join; tmp.null_row= table->null_row; tmp.maybe_null= table->maybe_null; tmp.status= table->status; - tmp.keys_in_use_for_query= tmp.s->keys_in_use; - tmp.used_keys= tmp.s->keys_for_keyread; + tmp.keys_in_use_for_query= tmp.keys_in_use; + tmp.used_keys= tmp.keys_for_keyread; + tmp.force_index= tmp.force_index; /* Get state */ - tmp.s->key_length= table->s->key_length; + tmp.key_length= table->key_length; tmp.in_use= table->in_use; tmp.reginfo.lock_type=table->reginfo.lock_type; - tmp.s->version= refresh_version; - tmp.s->tmp_table= table->s->tmp_table; + tmp.version= refresh_version; + tmp.tmp_table= table->tmp_table; tmp.grant= table->grant; /* Replace table in open list */ tmp.next= table->next; tmp.prev= table->prev; - delete table->triggers; if (table->file) VOID(closefrm(table)); // close file, free everything - *table= tmp; - table->s= &table->share_not_to_be_used; + *table=tmp; table->file->change_table_ptr(table); - DBUG_ASSERT(table->alias != 0); + DBUG_ASSERT(table->table_name); for (field=table->field ; *field ; field++) { (*field)->table= (*field)->orig_table= table; - (*field)->table_name= &table->alias; + (*field)->table_name=table->table_name; } - for (key=0 ; key < table->s->keys ; key++) + for (key=0 ; key < table->keys ; key++) { for (part=0 ; part < table->key_info[key].usable_key_parts ; part++) table->key_info[key].key_part[part].field->table= table; } - if (table->triggers) - table->triggers->set_table(table); - VOID(pthread_cond_broadcast(&COND_refresh)); error=0; @@ -1511,8 +1203,8 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name) TABLE *table; for (table=thd->open_tables; table ; table=table->next) { - if (!strcmp(table->s->table_name, table_name) && - !strcmp(table->s->db, db)) + if (!strcmp(table->real_name,table_name) && + !strcmp(table->table_cache_key,db)) { mysql_lock_remove(thd, thd->locked_tables,table); table->file->close(); @@ -1538,7 +1230,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) TABLE *table,*next,**prev; TABLE **tables,**tables_ptr; // For locks - bool error=0, not_used; + bool error=0; if (get_locks) { /* The ptr is checked later */ @@ -1557,7 +1249,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) next=table->next; if (!tables || (!db_stat && reopen_table(table,1))) { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); + my_error(ER_CANT_REOPEN_TABLE,MYF(0),table->table_name); VOID(hash_delete(&open_cache,(byte*) table)); error=1; } @@ -1569,7 +1261,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) *tables_ptr++= table; // need new lock on this if (in_refresh) { - table->s->version=0; + table->version=0; table->locked_by_flush=0; } } @@ -1579,8 +1271,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) MYSQL_LOCK *lock; /* We should always get these locks */ thd->some_tables_deleted=0; - if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), - 0, ¬_used))) + if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr-tables), 0))) { thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock); } @@ -1609,11 +1300,11 @@ void close_old_data_files(THD *thd, TABLE *table, bool abort_locks, bool found=send_refresh; for (; table ; table=table->next) { - if (table->s->version != refresh_version) + if (table->version != refresh_version) { found=1; if (!abort_locks) // If not from flush tables - table->s->version= refresh_version; // Let other threads use table + table->version = refresh_version; // Let other threads use table if (table->db_stat) { if (abort_locks) @@ -1643,18 +1334,18 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) { do { - char *key= table->s->table_cache_key; - uint key_length= table->s->key_length; HASH_SEARCH_STATE state; + char *key= table->table_cache_key; + uint key_length=table->key_length; for (TABLE *search= (TABLE*) hash_first(&open_cache, (byte*) key, - key_length, &state); + key_length, &state); search ; search= (TABLE*) hash_next(&open_cache, (byte*) key, key_length, &state)) { if (search->locked_by_flush || search->locked_by_name && wait_for_name_lock || - search->db_stat && search->s->version < refresh_version) + search->db_stat && search->version < refresh_version) return 1; // Table is used } } while ((table=table->next)); @@ -1702,11 +1393,11 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name) TABLE *table,*next,**prev; bool found=0; prev= &thd->open_tables; - for (table= thd->open_tables; table ; table=next) + for (table=thd->open_tables; table ; table=next) { next=table->next; - if (!strcmp(table->s->table_name, table_name) && - !strcmp(table->s->db, db)) + if (!strcmp(table->real_name,table_name) && + !strcmp(table->table_cache_key,db)) { mysql_lock_remove(thd, thd->locked_tables,table); VOID(hash_delete(&open_cache,(byte*) table)); @@ -1741,8 +1432,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) TABLE *table; for (table= thd->open_tables; table ; table= table->next) { - if (!strcmp(table->s->table_name,table_name) && - !strcmp(table->s->db, db)) + if (!strcmp(table->real_name,table_name) && + !strcmp(table->table_cache_key,db)) { mysql_lock_abort(thd,table); break; @@ -1761,8 +1452,6 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) db Database name name Table name alias Alias name - table_desc TABLE_LIST descriptor (used with views) - mem_root temporary mem_root for parsing NOTES Extra argument for open is taken from thd->open_options @@ -1771,9 +1460,9 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) 0 ok # Error */ + static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, - const char *name, const char *alias, - TABLE_LIST *table_desc, MEM_ROOT *mem_root) + const char *name, const char *alias) { char path[FN_REFLEN]; int error; @@ -1781,28 +1470,19 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, DBUG_ENTER("open_unireg_entry"); strxmov(path, mysql_data_home, "/", db, "/", name, NullS); - while ((error= openfrm(thd, path, alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | HA_TRY_READ_ONLY | - NO_ERR_ON_NEW_FRM), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - thd->open_options, entry)) && - (error != 5 || - (fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME), - open_new_frm(thd, path, alias, db, name, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - thd->open_options, entry, table_desc, mem_root)))) - + while (openfrm(path,alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | + HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + thd->open_options, entry)) { - if (!entry->s || !entry->s->crashed) + if (!entry->crashed) { /* - Frm file could not be found on disk - Since it does not exist, no one can be using it - LOCK_open has been locked to protect from someone else - trying to discover the table at the same time. + Frm file could not be found on disk + Since it does not exist, no one can be using it + LOCK_open has been locked to protect from someone else + trying to discover the table at the same time. */ if (discover_retry_count++ != 0) goto err; @@ -1810,7 +1490,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, { /* Give right error message */ thd->clear_error(); - DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name)); + DBUG_PRINT("error", ("Dicovery of %s/%s failed", db, name)); my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while " "unpacking from engine", @@ -1819,8 +1499,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, goto err; } - mysql_reset_errors(thd, 1); // Clear warnings - thd->clear_error(); // Clear error message + thd->clear_error(); // Clear error message continue; } @@ -1828,7 +1507,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, TABLE_LIST table_list; bzero((char*) &table_list, sizeof(table_list)); // just for safe table_list.db=(char*) db; - table_list.table_name=(char*) name; + table_list.real_name=(char*) name; safe_mutex_assert_owner(&LOCK_open); @@ -1847,7 +1526,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, pthread_mutex_unlock(&LOCK_open); thd->clear_error(); // Clear error message error= 0; - if (openfrm(thd, path, alias, + if (openfrm(path,alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, @@ -1872,22 +1551,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, goto err; break; } - - if (error == 5) - DBUG_RETURN(0); // we have just opened VIEW - - /* - We can't mark all tables in 'mysql' database as system since we don't - allow to lock such tables for writing with any other tables (even with - other system tables) and some privilege tables need this. - */ - if (!my_strcasecmp(system_charset_info, db, "mysql") && - !my_strcasecmp(system_charset_info, name, "proc")) - entry->s->system_table= 1; - - if (Table_triggers_list::check_n_load(thd, db, name, entry, 0)) - goto err; - /* If we are here, there was no fatal error (but error may be still unitialized). @@ -1916,7 +1579,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, */ sql_print_error("When opening HEAP table, could not allocate \ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); - delete entry->triggers; if (entry->file) closefrm(entry); goto err; @@ -1925,251 +1587,91 @@ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); } DBUG_RETURN(0); err: - /* Hide "Table doesn't exist" errors if table belong to view */ - if (thd->net.last_errno == ER_NO_SUCH_TABLE && - table_desc && table_desc->belong_to_view) - { - TABLE_LIST *view= table_desc->belong_to_view; - thd->clear_error(); - my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str); - } DBUG_RETURN(1); } - /* Open all tables in list SYNOPSIS open_tables() thd - thread handler - start - list of tables in/out + start - list of tables counter - number of opened tables will be return using this parameter - flags - bitmap of flags to modify how the tables will be open: - MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has - done a flush or namelock on it. - - NOTE - Unless we are already in prelocked mode, this function will also precache - all SP/SFs explicitly or implicitly (via views and triggers) used by the - query and add tables needed for their execution to table list. If resulting - tables list will be non empty it will mark query as requiring precaching. - Prelocked mode will be enabled for such query during lock_tables() call. - - If query for which we are opening tables is already marked as requiring - prelocking it won't do such precaching and will simply reuse table list - which is already built. RETURN 0 - OK -1 - error */ -int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) +int open_tables(THD *thd, TABLE_LIST *start, uint *counter) { TABLE_LIST *tables; bool refresh; int result=0; - MEM_ROOT new_frm_mem; - /* Also used for indicating that prelocking is need */ - TABLE_LIST **query_tables_last_own; DBUG_ENTER("open_tables"); - /* - temporary mem_root for new .frm parsing. - TODO: variables for size - */ - init_alloc_root(&new_frm_mem, 8024, 8024); thd->current_tablenr= 0; restart: *counter= 0; - query_tables_last_own= 0; thd->proc_info="Opening tables"; - - /* - If we are not already executing prelocked statement and don't have - statement for which table list for prelocking is already built, let - us cache routines and try to build such table list. - - NOTE: We will mark statement as requiring prelocking only if we will - have non empty table list. But this does not guarantee that in prelocked - mode we will have some locked tables, because queries which use only - derived/information schema tables and views possible. Thus "counter" - may be still zero for prelocked statement... - */ - - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && - thd->lex->sroutines_list.elements) - { - bool first_no_prelocking, need_prelocking, tabs_changed; - TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; - - DBUG_ASSERT(thd->lex->query_tables == *start); - sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking); - - if (sp_cache_routines_and_add_tables(thd, thd->lex, - first_no_prelocking, - &tabs_changed)) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - result= -1; - goto err; - } - else if ((tabs_changed || *start) && need_prelocking) - { - query_tables_last_own= save_query_tables_last; - *start= thd->lex->query_tables; - } - } - - for (tables= *start; tables ;tables= tables->next_global) + for (tables=start ; tables ; tables=tables->next) { /* Ignore placeholders for derived tables. After derived tables processing, link to created temporary table will be put here. - If this is derived table for view then we still want to process - routines used by this view. */ if (tables->derived) - { - if (tables->view) - goto process_view_routines; continue; - } - if (tables->schema_table) - { - if (!mysql_schema_table(thd, thd->lex, tables)) - continue; - DBUG_RETURN(-1); - } (*counter)++; - if (!tables->table && - !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags))) + !(tables->table= open_table(thd, + tables->db, + tables->real_name, + tables->alias, &refresh))) { - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); - - if (tables->view) - { - /* VIEW placeholder */ - (*counter)--; - - /* - tables->next_global list consists of two parts: - 1) Query tables and underlying tables of views. - 2) Tables used by all stored routines that this statement invokes on - execution. - We need to know where the bound between these two parts is. If we've - just opened a view, which was the last table in part #1, and it - has added its base tables after itself, adjust the boundary pointer - accordingly. - */ - if (query_tables_last_own == &(tables->next_global) && - tables->view->query_tables) - query_tables_last_own= tables->view->query_tables_last; - /* - Let us free memory used by 'sroutines' hash here since we never - call destructor for this LEX. - */ - hash_free(&tables->view->sroutines); - goto process_view_routines; - } - if (refresh) // Refresh in progress { - /* - We have met name-locked or old version of table. Now we have - to close all tables which are not up to date. We also have to - throw away set of prelocked tables (and thus close tables from - this set that were open by now) since it possible that one of - tables which determined its content was changed. - - Instead of implementing complex/non-robust logic mentioned - above we simply close and then reopen all tables. - - In order to prepare for recalculation of set of prelocked tables - we pretend that we have finished calculation which we were doing - currently. - */ - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - close_tables_for_reopen(thd, start); + /* close all 'old' tables used by this thread */ + pthread_mutex_lock(&LOCK_open); + // if query_id is not reset, we will get an error + // re-opening a temp table + thd->version=refresh_version; + TABLE **prev_table= &thd->open_tables; + bool found=0; + for (TABLE_LIST *tmp=start ; tmp ; tmp=tmp->next) + { + /* Close normal (not temporary) changed tables */ + if (tmp->table && ! tmp->table->tmp_table) + { + if (tmp->table->version != refresh_version || + ! tmp->table->db_stat) + { + VOID(hash_delete(&open_cache,(byte*) tmp->table)); + tmp->table=0; + found=1; + } + else + { + *prev_table= tmp->table; // Relink open list + prev_table= &tmp->table->next; + } + } + } + *prev_table=0; + pthread_mutex_unlock(&LOCK_open); + if (found) + VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh goto restart; } result= -1; // Fatal error break; } - else - { - /* - If we are not already in prelocked mode and extended table list is not - yet built and we have trigger for table being opened then we should - cache all routines used by its triggers and add their tables to - prelocking list. - If we lock table for reading we won't update it so there is no need to - process its triggers since they never will be activated. - */ - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && - tables->table->triggers && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) - { - if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, - tables)) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - result= -1; - goto err; - } - } - free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); - } - if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) tables->table->reginfo.lock_type=tables->lock_type; tables->table->grant= tables->grant; - -process_view_routines: - /* - Again we may need cache all routines used by this view and add - tables used by them to table list. - */ - if (tables->view && !thd->prelocked_mode && - !thd->lex->requires_prelocking() && - tables->view->sroutines_list.elements) - { - /* We have at least one table in TL here. */ - if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables)) - { - /* - Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has - been emitted; we must abort. - */ - result= -1; - goto err; - } - } } - - err: thd->proc_info=0; - free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block - - if (query_tables_last_own) - thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - DBUG_RETURN(result); } @@ -2197,10 +1699,12 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ && (int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ) { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias); + my_printf_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, + ER(ER_TABLE_NOT_LOCKED_FOR_WRITE), + MYF(0),table->table_name); DBUG_RETURN(1); } - if ((error=table->file->start_stmt(thd, lock_type))) + if ((error=table->file->start_stmt(thd))) { table->file->print_error(error,MYF(0)); DBUG_RETURN(1); @@ -2218,11 +1722,6 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, table_list Table to open is first table in this list lock_type Lock to use for open - NOTE - This function don't do anything like SP/SF/views/triggers analysis done - in open_tables(). It is intended for opening of only one concrete table. - And used only in special contexts. - RETURN VALUES table Opened table 0 Error @@ -2240,11 +1739,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) thd->proc_info="Opening table"; thd->current_tablenr= 0; - /* open_ltable can be used only for BASIC TABLEs */ - table_list->required_type= FRMTYPE_TABLE; - while (!(table= open_table(thd, table_list, thd->mem_root, &refresh, 0)) && - refresh) - ; + while (!(table=open_table(thd,table_list->db, + table_list->real_name,table_list->alias, + &refresh)) && refresh) ; if (table) { @@ -2267,8 +1764,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) - if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0, - &refresh))) + if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0))) table= 0; } } @@ -2291,25 +1787,15 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) -1 - error NOTE - The lock will automaticaly be freed by close_thread_tables() + The lock will automaticly be freed by close_thread_tables() */ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { - uint counter; - bool need_reopen; DBUG_ENTER("simple_open_n_lock_tables"); - - for ( ; ; ) - { - if (open_tables(thd, &tables, &counter, 0)) - DBUG_RETURN(-1); - if (!lock_tables(thd, tables, counter, &need_reopen)) - break; - if (!need_reopen) - DBUG_RETURN(-1); - close_tables_for_reopen(thd, &tables); - } + uint counter; + if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter)) + DBUG_RETURN(-1); /* purecov: inspected */ DBUG_RETURN(0); } @@ -2324,34 +1810,21 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) tables - list of tables for open&locking RETURN - FALSE - ok - TRUE - error + 0 - ok + -1 - error NOTE - The lock will automaticaly be freed by close_thread_tables() + The lock will automaticly be freed by close_thread_tables() */ -bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) +int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { - uint counter; - bool need_reopen; DBUG_ENTER("open_and_lock_tables"); - - for ( ; ; ) - { - if (open_tables(thd, &tables, &counter, 0)) - DBUG_RETURN(-1); - if (!lock_tables(thd, tables, counter, &need_reopen)) - break; - if (!need_reopen) - DBUG_RETURN(-1); - close_tables_for_reopen(thd, &tables); - } - if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || - (thd->fill_derived_tables() && - mysql_handle_derived(thd->lex, &mysql_derived_filling))) - DBUG_RETURN(TRUE); /* purecov: inspected */ - DBUG_RETURN(0); + uint counter; + if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter)) + DBUG_RETURN(-1); /* purecov: inspected */ + relink_tables_for_derived(thd); + DBUG_RETURN(mysql_handle_derived(thd->lex)); } @@ -2362,9 +1835,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) open_normal_and_derived_tables thd - thread handler tables - list of tables for open - flags - bitmap of flags to modify how the tables will be open: - MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has - done a flush or namelock on it. RETURN FALSE - ok @@ -2375,36 +1845,36 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) data from the tables. */ -bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) +int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) { uint counter; DBUG_ENTER("open_normal_and_derived_tables"); - DBUG_ASSERT(!thd->fill_derived_tables()); - if (open_tables(thd, &tables, &counter, flags) || - mysql_handle_derived(thd->lex, &mysql_derived_prepare)) - DBUG_RETURN(TRUE); /* purecov: inspected */ - DBUG_RETURN(0); + if (open_tables(thd, tables, &counter)) + DBUG_RETURN(-1); /* purecov: inspected */ + relink_tables_for_derived(thd); + DBUG_RETURN(mysql_handle_derived(thd->lex)); } /* - Mark all real tables in the list as free for reuse. - - SYNOPSIS - mark_real_tables_as_free_for_reuse() - thd - thread context - table - head of the list of tables - - DESCRIPTION - Marks all real tables in the list (i.e. not views, derived - or schema tables) as free for reuse. + Let us propagate pointers to open tables from global table list + to table lists in particular selects if needed. */ -static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) +void relink_tables_for_derived(THD *thd) { - for (; table; table= table->next_global) - if (!table->placeholder() && !table->schema_table) - table->table->query_id= 0; + 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; + } } @@ -2415,183 +1885,51 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) lock_tables() thd Thread handler tables Tables to lock - count Number of opened tables - need_reopen Out parameter which if TRUE indicates that some - tables were dropped or altered during this call - and therefore invoker should reopen tables and - try to lock them once again (in this case - lock_tables() will also return error). + count umber of opened tables NOTES You can't call lock_tables twice, as this would break the dead-lock-free handling thr_lock gives us. You most always get all needed locks at once. - If query for which we are calling this function marked as requring - prelocking, this function will do implicit LOCK TABLES and change - thd::prelocked_mode accordingly. - RETURN VALUES 0 ok -1 Error */ -int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) +int lock_tables(THD *thd, TABLE_LIST *tables, uint count) { TABLE_LIST *table; - - DBUG_ENTER("lock_tables"); - /* - We can't meet statement requiring prelocking if we already - in prelocked mode. - */ - DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking()); - /* - If statement requires prelocking then it has non-empty table list. - So it is safe to shortcut. - */ - DBUG_ASSERT(!thd->lex->requires_prelocking() || tables); - - *need_reopen= FALSE; - if (!tables) - DBUG_RETURN(0); + return 0; - /* - We need this extra check for thd->prelocked_mode because we want to avoid - attempts to lock tables in substatements. Checking for thd->locked_tables - is not enough in some situations. For example for SP containing - "drop table t3; create temporary t3 ..; insert into t3 ...;" - thd->locked_tables may be 0 after drop tables, and without this extra - check insert will try to lock temporary table t3, that will lead - to memory leak... - */ - if (!thd->locked_tables && !thd->prelocked_mode) + if (!thd->locked_tables) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; - - if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) - DBUG_RETURN(-1); - for (table= tables; table; table= table->next_global) + if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count))) + return -1; + for (table = tables ; table ; table=table->next) { - if (!table->placeholder() && !table->schema_table) + if (!table->derived) *(ptr++)= table->table; } - - /* We have to emulate LOCK TABLES if we are statement needs prelocking. */ - if (thd->lex->requires_prelocking()) - { - thd->in_lock_tables=1; - thd->options|= OPTION_TABLE_LOCK; - } - - if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, - need_reopen))) - { - if (thd->lex->requires_prelocking()) - { - thd->options&= ~(ulong) (OPTION_TABLE_LOCK); - thd->in_lock_tables=0; - } - DBUG_RETURN(-1); - } - if (thd->lex->requires_prelocking() && - thd->lex->sql_command != SQLCOM_LOCK_TABLES) - { - TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); - /* - We just have done implicit LOCK TABLES, and now we have - to emulate first open_and_lock_tables() after it. - - Note that "LOCK TABLES" can also be marked as requiring prelocking - (e.g. if one locks view which uses functions). We should not emulate - such open_and_lock_tables() in this case. We also should not set - THD::prelocked_mode or first close_thread_tables() call will do - "UNLOCK TABLES". - */ - thd->locked_tables= thd->lock; - thd->lock= 0; - thd->in_lock_tables=0; - - for (table= tables; table != first_not_own; table= table->next_global) - { - if (!table->placeholder() && !table->schema_table) - { - table->table->query_id= thd->query_id; - if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) - { - ha_rollback_stmt(thd); - mysql_unlock_tables(thd, thd->locked_tables); - thd->locked_tables= 0; - thd->options&= ~(ulong) (OPTION_TABLE_LOCK); - DBUG_RETURN(-1); - } - } - } - /* - Let us mark all tables which don't belong to the statement itself, - and was marked as occupied during open_tables() as free for reuse. - */ - mark_real_tables_as_free_for_reuse(first_not_own); - DBUG_PRINT("info",("prelocked_mode= PRELOCKED")); - thd->prelocked_mode= PRELOCKED; - } + if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), 0))) + return -1; /* purecov: inspected */ } else { - TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); - for (table= tables; table != first_not_own; table= table->next_global) + for (table = tables ; table ; table=table->next) { - if (!table->placeholder() && !table->schema_table && + if (!table->derived && check_lock_and_start_stmt(thd, table->table, table->lock_type)) { ha_rollback_stmt(thd); - DBUG_RETURN(-1); + return -1; } } - /* - If we are under explicit LOCK TABLES and our statement requires - prelocking, we should mark all "additional" tables as free for use - and enter prelocked mode. - */ - if (thd->lex->requires_prelocking()) - { - mark_real_tables_as_free_for_reuse(first_not_own); - DBUG_PRINT("info", ("thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES")); - thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES; - } } - DBUG_RETURN(0); -} - - -/* - Prepare statement for reopening of tables and recalculation of set of - prelocked tables. - - SYNOPSIS - close_tables_for_reopen() - thd in Thread context - tables in/out List of tables which we were trying to open and lock - -*/ - -void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) -{ - /* - If table list consists only from tables from prelocking set, table list - for new attempt should be empty, so we have to update list's root pointer. - */ - if (thd->lex->first_not_own_table() == *tables) - *tables= 0; - thd->lex->chop_off_not_own_tables(); - sp_remove_not_own_routines(thd->lex); - for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) - tmp->table= 0; - mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables); - close_thread_tables(thd); + return 0; } @@ -2605,7 +1943,6 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list) { TABLE *tmp_table; - TABLE_SHARE *share; DBUG_ENTER("open_temporary_table"); /* @@ -2620,7 +1957,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, MYF(MY_WME)))) DBUG_RETURN(0); /* purecov: inspected */ - if (openfrm(thd, path, table_name, + if (openfrm(path, table_name, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, ha_open_options, @@ -2630,22 +1967,21 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, DBUG_RETURN(0); } - share= tmp_table->s; tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked - share->tmp_table= (tmp_table->file->has_transactions() ? - TRANSACTIONAL_TMP_TABLE : TMP_TABLE); - share->table_cache_key= (char*) (tmp_table+1); - share->db= share->table_cache_key; - share->key_length= (uint) (strmov(((char*) (share->table_name= - strmov(share->table_cache_key, - db)+1)), - table_name) - - share->table_cache_key) +1; - int4store(share->table_cache_key + share->key_length, thd->server_id); - share->key_length+= 4; - int4store(share->table_cache_key + share->key_length, + tmp_table->in_use= thd; + tmp_table->tmp_table = (tmp_table->file->has_transactions() ? + TRANSACTIONAL_TMP_TABLE : TMP_TABLE); + tmp_table->table_cache_key=(char*) (tmp_table+1); + tmp_table->key_length= (uint) (strmov((tmp_table->real_name= + strmov(tmp_table->table_cache_key,db) + +1), table_name) + - tmp_table->table_cache_key)+1; + int4store(tmp_table->table_cache_key + tmp_table->key_length, + thd->server_id); + tmp_table->key_length += 4; + int4store(tmp_table->table_cache_key + tmp_table->key_length, thd->variables.pseudo_thread_id); - share->key_length+= 4; + tmp_table->key_length += 4; if (link_in_list) { @@ -2668,7 +2004,7 @@ bool rm_temporary_table(enum db_type base, char *path) if (my_delete(path,MYF(0))) error=1; /* purecov: inspected */ *fn_ext(path)='\0'; // remove extension - handler *file= get_new_handler((TABLE*) 0, current_thd->mem_root, base); + handler *file=get_new_handler((TABLE*) 0, base); if (file && file->delete_table(path)) { error=1; @@ -2681,255 +2017,31 @@ bool rm_temporary_table(enum db_type base, char *path) /***************************************************************************** -* The following find_field_in_XXX procedures implement the core of the -* name resolution functionality. The entry point to resolve a column name in a -* list of tables is 'find_field_in_tables'. It calls 'find_field_in_table_ref' -* for each table reference. In turn, depending on the type of table reference, -* 'find_field_in_table_ref' calls one of the 'find_field_in_XXX' procedures -* below specific for the type of table reference. +** find field in list or tables. if field is unqualifed and unique, +** return unique field ******************************************************************************/ -/* Special Field pointers as return values of find_field_in_XXX functions. */ -Field *not_found_field= (Field*) 0x1; -Field *view_ref_found= (Field*) 0x2; - #define WRONG_GRANT (Field*) -1 -static void update_field_dependencies(THD *thd, Field *field, TABLE *table) -{ - if (thd->set_query_id) - { - if (field->query_id != thd->query_id) - { - field->query_id= thd->query_id; - table->used_fields++; - table->used_keys.intersect(field->part_of_key); - } - else - thd->dupp_field= field; - } -} - - -/* - Find a field by name in a view that uses merge algorithm. - - SYNOPSIS - find_field_in_view() - thd thread handler - table_list view to search for 'name' - name name of field - length length of name - item_name name of item if it will be created (VIEW) - ref expression substituted in VIEW should be passed - using this reference (return view_ref_found) - register_tree_change TRUE if ref is not stack variable and we - need register changes in item tree - - RETURN - 0 field is not found - view_ref_found found value in VIEW (real result is in *ref) - # pointer to field - only for schema table fields -*/ - -static Field * -find_field_in_view(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, - const char *item_name, Item **ref, - bool register_tree_change) -{ - DBUG_ENTER("find_field_in_view"); - DBUG_PRINT("enter", - ("view: '%s', field name: '%s', item name: '%s', ref 0x%lx", - table_list->alias, name, item_name, (ulong) ref)); - Field_iterator_view field_it; - field_it.set(table_list); - Query_arena *arena, backup; - - DBUG_ASSERT(table_list->schema_table_reformed || - (ref != 0 && table_list->view != 0)); - for (; !field_it.end_of_fields(); field_it.next()) - { - if (!my_strcasecmp(system_charset_info, field_it.name(), name)) - { - // in PS use own arena or data will be freed after prepare - if (register_tree_change) - arena= thd->activate_stmt_arena_if_needed(&backup); - /* - create_item() may, or may not create a new Item, depending on - the column reference. See create_view_field() for details. - */ - Item *item= field_it.create_item(thd); - if (register_tree_change && arena) - thd->restore_active_arena(arena, &backup); - - if (!item) - DBUG_RETURN(0); - /* - *ref != NULL means that *ref contains the item that we need to - replace. If the item was aliased by the user, set the alias to - the replacing item. - We need to set alias on both ref itself and on ref real item. - */ - if (*ref && !(*ref)->is_autogenerated_name) - { - item->set_name((*ref)->name, (*ref)->name_length, - system_charset_info); - item->real_item()->set_name((*ref)->name, (*ref)->name_length, - system_charset_info); - } - if (register_tree_change) - thd->change_item_tree(ref, item); - else - *ref= item; - DBUG_RETURN((Field*) view_ref_found); - } - } - DBUG_RETURN(0); -} - - -/* - Find field by name in a NATURAL/USING join table reference. - - SYNOPSIS - find_field_in_natural_join() - thd [in] thread handler - table_ref [in] table reference to search - name [in] name of field - length [in] length of name - ref [in/out] if 'name' is resolved to a view field, ref is - set to point to the found view field - register_tree_change [in] TRUE if ref is not stack variable and we - need register changes in item tree - actual_table [out] the original table reference where the field - belongs - differs from 'table_list' only for - NATURAL/USING joins - - DESCRIPTION - Search for a field among the result fields of a NATURAL/USING join. - Notice that this procedure is called only for non-qualified field - names. In the case of qualified fields, we search directly the base - tables of a natural join. - - RETURN - NULL if the field was not found - WRONG_GRANT if no access rights to the found field - # Pointer to the found Field -*/ - -static Field * -find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, - uint length, Item **ref, bool register_tree_change, - TABLE_LIST **actual_table) -{ - List_iterator_fast<Natural_join_column> - field_it(*(table_ref->join_columns)); - Natural_join_column *nj_col; - Field *found_field; - Query_arena *arena, backup; - DBUG_ENTER("find_field_in_natural_join"); - DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx", - name, (ulong) ref)); - DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns); - DBUG_ASSERT(*actual_table == NULL); - - LINT_INIT(found_field); - - for (;;) - { - if (!(nj_col= field_it++)) - DBUG_RETURN(NULL); - - if (!my_strcasecmp(system_charset_info, nj_col->name(), name)) - break; - } - - if (nj_col->view_field) - { - Item *item; - if (register_tree_change) - arena= thd->activate_stmt_arena_if_needed(&backup); - /* - create_item() may, or may not create a new Item, depending on the - column reference. See create_view_field() for details. - */ - item= nj_col->create_item(thd); - if (register_tree_change && arena) - thd->restore_active_arena(arena, &backup); - - if (!item) - DBUG_RETURN(NULL); - DBUG_ASSERT(nj_col->table_field == NULL); - if (nj_col->table_ref->schema_table_reformed) - { - /* - Translation table items are always Item_fields and fixed - already('mysql_schema_table' function). So we can return - ->field. It is used only for 'show & where' commands. - */ - DBUG_RETURN(((Item_field*) (nj_col->view_field->item))->field); - } - if (register_tree_change) - thd->change_item_tree(ref, item); - else - *ref= item; - found_field= (Field*) view_ref_found; - } - else - { - /* This is a base table. */ - DBUG_ASSERT(nj_col->view_field == NULL); - DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->table); - found_field= nj_col->table_field; - update_field_dependencies(thd, found_field, nj_col->table_ref->table); - } - - *actual_table= nj_col->table_ref; - - DBUG_RETURN(found_field); -} - - -/* - Find field by name in a base table or a view with temp table algorithm. - - SYNOPSIS - find_field_in_table() - thd thread handler - table table where to search for the field - name name of field - length length of name - allow_rowid do allow finding of "_rowid" field? - cached_field_index_ptr cached position in field list (used to speedup - lookup for fields in prepared tables) - - RETURN - 0 field is not found - # pointer to field -*/ - -Field * -find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, - bool allow_rowid, uint *cached_field_index_ptr) +Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, + bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr) { Field **field_ptr, *field; uint cached_field_index= *cached_field_index_ptr; - DBUG_ENTER("find_field_in_table"); - DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias, name)); /* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */ - if (cached_field_index < table->s->fields && - !my_strcasecmp(system_charset_info, + if (cached_field_index < table->fields && + !my_strcasecmp(system_charset_info, table->field[cached_field_index]->field_name, name)) field_ptr= table->field + cached_field_index; - else if (table->s->name_hash.records) - field_ptr= (Field**) hash_search(&table->s->name_hash, (byte*) name, - length); + else if (table->name_hash.records) + field_ptr= (Field**)hash_search(&table->name_hash,(byte*) name, + length); else { if (!(field_ptr= table->field)) - DBUG_RETURN((Field *)0); + return (Field *)0; for (; *field_ptr; ++field_ptr) if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) break; @@ -2945,163 +2057,25 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, if (!allow_rowid || my_strcasecmp(system_charset_info, name, "_rowid") || !(field=table->rowid_field)) - DBUG_RETURN((Field*) 0); + return (Field*) 0; } - update_field_dependencies(thd, field, table); - - DBUG_RETURN(field); -} - - -/* - Find field in a table reference. - - SYNOPSIS - find_field_in_table_ref() - thd [in] thread handler - table_list [in] table reference to search - name [in] name of field - length [in] field length of name - item_name [in] name of item if it will be created (VIEW) - db_name [in] optional database name that qualifies the - table_name [in] optional table name that qualifies the field - ref [in/out] if 'name' is resolved to a view field, ref - is set to point to the found view field - check_privileges [in] check privileges - allow_rowid [in] do allow finding of "_rowid" field? - cached_field_index_ptr [in] cached position in field list (used to - speedup lookup for fields in prepared tables) - register_tree_change [in] TRUE if ref is not stack variable and we - need register changes in item tree - actual_table [out] the original table reference where the field - belongs - differs from 'table_list' only for - NATURAL_USING joins. - - DESCRIPTION - Find a field in a table reference depending on the type of table - reference. There are three types of table references with respect - to the representation of their result columns: - - an array of Field_translator objects for MERGE views and some - information_schema tables, - - an array of Field objects (and possibly a name hash) for stored - tables, - - a list of Natural_join_column objects for NATURAL/USING joins. - This procedure detects the type of the table reference 'table_list' - and calls the corresponding search routine. - - RETURN - 0 field is not found - view_ref_found found value in VIEW (real result is in *ref) - # pointer to field -*/ - -Field * -find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, - const char *item_name, const char *db_name, - const char *table_name, Item **ref, - bool check_privileges, bool allow_rowid, - uint *cached_field_index_ptr, - bool register_tree_change, TABLE_LIST **actual_table) -{ - Field *fld; - DBUG_ENTER("find_field_in_table_ref"); - DBUG_PRINT("enter", - ("table: '%s' field name: '%s' item name: '%s' ref 0x%lx", - table_list->alias, name, item_name, (ulong) ref)); - - /* - Check that the table and database that qualify the current field name - are the same as the table reference we are going to search for the field. - - Exclude from the test below nested joins because the columns in a - nested join generally originate from different tables. Nested joins - also have no table name, except when a nested join is a merge view - or an information schema table. - - We include explicitly table references with a 'field_translation' table, - because if there are views over natural joins we don't want to search - inside the view, but we want to search directly in the view columns - which are represented as a 'field_translation'. - - TODO: Ensure that table_name, db_name and tables->db always points to - something ! - */ - if (/* Exclude nested joins. */ - (!table_list->nested_join || - /* Include merge views and information schema tables. */ - table_list->field_translation) && - /* - Test if the field qualifiers match the table reference we plan - to search. - */ - table_name && table_name[0] && - (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || - (db_name && db_name[0] && table_list->db && table_list->db[0] && - strcmp(db_name, table_list->db)))) - DBUG_RETURN(0); - - *actual_table= NULL; - - if (table_list->field_translation) - { - /* 'table_list' is a view or an information schema table. */ - if ((fld= find_field_in_view(thd, table_list, name, length, item_name, ref, - register_tree_change))) - *actual_table= table_list; - } - else if (!table_list->nested_join) - { - /* 'table_list' is a stored table. */ - DBUG_ASSERT(table_list->table); - if ((fld= find_field_in_table(thd, table_list->table, name, length, - allow_rowid, - cached_field_index_ptr))) - *actual_table= table_list; - } - else + if (thd->set_query_id) { - /* - 'table_list' is a NATURAL/USING join, or an operand of such join that - is a nested join itself. - - If the field name we search for is qualified, then search for the field - in the table references used by NATURAL/USING the join. - */ - if (table_name && table_name[0]) + if (field->query_id != thd->query_id) { - List_iterator<TABLE_LIST> it(table_list->nested_join->join_list); - TABLE_LIST *table; - while ((table= it++)) - { - if ((fld= find_field_in_table_ref(thd, table, name, length, item_name, - db_name, table_name, ref, - check_privileges, allow_rowid, - cached_field_index_ptr, - register_tree_change, actual_table))) - DBUG_RETURN(fld); - } - DBUG_RETURN(0); + field->query_id=thd->query_id; + table->used_fields++; + table->used_keys.intersect(field->part_of_key); } - /* - Non-qualified field, search directly in the result columns of the - natural join. The condition of the outer IF is true for the top-most - natural join, thus if the field is not qualified, we will search - directly the top-most NATURAL/USING join. - */ - fld= find_field_in_natural_join(thd, table_list, name, length, ref, - register_tree_change, actual_table); + else + thd->dupp_field=field; } - #ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check if there are sufficient access rights to the found field. */ - if (fld && check_privileges && - check_column_grant_in_table_ref(thd, *actual_table, name, length)) - fld= WRONG_GRANT; + if (check_grants && check_grant_column(thd,table,name,length)) + return WRONG_GRANT; #endif - - DBUG_RETURN(fld); + return field; } @@ -3110,100 +2084,58 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, SYNOPSIS find_field_in_tables() - thd pointer to current thread structure - item field item that should be found - first_table list of tables to be searched for item - last_table end of the list of tables to search for item. If NULL - then search to the end of the list 'first_table'. - ref if 'item' is resolved to a view field, ref is set to - point to the found view field - report_error Degree of error reporting: - - IGNORE_ERRORS then do not report any error - - IGNORE_EXCEPT_NON_UNIQUE report only non-unique - fields, suppress all other errors - - REPORT_EXCEPT_NON_UNIQUE report all other errors - except when non-unique fields were found - - REPORT_ALL_ERRORS - check_privileges need to check privileges - register_tree_change TRUE if ref is not a stack variable and we - to need register changes in item tree + thd Pointer to current thread structure + item Field item that should be found + tables Tables for scanning + where Table where field found will be returned via + this parameter + report_error If FALSE then do not report error if item not found + and return not_found_field RETURN VALUES - 0 If error: the found field is not unique, or there are - no sufficient access priviliges for the found field, - or the field is qualified with non-existing table. - not_found_field The function was called with report_error == - (IGNORE_ERRORS || IGNORE_EXCEPT_NON_UNIQUE) and a - field was not found. - view_ref_found View field is found, item passed through ref parameter - found field If a item was resolved to some field + 0 Field is not found or field is not unique- error + message is reported + not_found_field Function was called with report_error == FALSE and + field was not found. no error message reported. + found field */ +// Special Field pointer for find_field_in_tables returning +const Field *not_found_field= (Field*) 0x1; + Field * -find_field_in_tables(THD *thd, Item_ident *item, - TABLE_LIST *first_table, TABLE_LIST *last_table, - Item **ref, find_item_error_report_type report_error, - bool check_privileges, bool register_tree_change) +find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, + TABLE_LIST **where, bool report_error) { Field *found=0; - const char *db= item->db_name; - const char *table_name= item->table_name; - const char *name= item->field_name; + const char *db=item->db_name; + const char *table_name=item->table_name; + const char *name=item->field_name; uint length=(uint) strlen(name); char name_buff[NAME_LEN+1]; - TABLE_LIST *cur_table= first_table; - TABLE_LIST *actual_table; bool allow_rowid; - if (!table_name || !table_name[0]) - { - table_name= 0; // For easier test - db= 0; - } - - allow_rowid= table_name || (cur_table && !cur_table->next_local); - if (item->cached_table) { /* - This shortcut is used by prepared statements. We assume that - TABLE_LIST *first_table is not changed during query execution (which - is true for all queries except RENAME but luckily RENAME doesn't + This shortcut is used by prepared statements. We assuming that + TABLE_LIST *tables is not changed during query execution (which + is true for all queries except RENAME but luckily RENAME doesn't use fields...) so we can rely on reusing pointer to its member. - With this optimization we also miss case when addition of one more - field makes some prepared query ambiguous and so erroneous, but we + With this optimisation we also miss case when addition of one more + field makes some prepared query ambiguous and so erronous, but we accept this trade off. */ - TABLE_LIST *table_ref= item->cached_table; - /* - The condition (table_ref->view == NULL) ensures that we will call - find_field_in_table even in the case of information schema tables - when table_ref->field_translation != NULL. - */ - if (table_ref->table && !table_ref->view) - found= find_field_in_table(thd, table_ref->table, name, length, - TRUE, &(item->cached_field_index)); - else - found= find_field_in_table_ref(thd, table_ref, name, length, item->name, - NULL, NULL, ref, check_privileges, - TRUE, &(item->cached_field_index), - register_tree_change, - &actual_table); + found= find_field_in_table(thd, item->cached_table->table, name, length, + test(item->cached_table-> + table->grant.want_privilege), + 1, &(item->cached_field_index)); + if (found) { + (*where)= tables; if (found == WRONG_GRANT) - return (Field*) 0; - { - SELECT_LEX *current_sel= thd->lex->current_select; - SELECT_LEX *last_select= table_ref->select_lex; - /* - If the field was an outer referencee, mark all selects using this - sub query as dependent on the outer query - */ - if (current_sel != last_select) - mark_select_range_as_dependent(thd, last_select, current_sel, - found, *ref, item); - } + return (Field*) 0; return found; } } @@ -3211,7 +2143,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (db && lower_case_table_names) { /* - convert database to lower case for comparison. + convert database to lower case for comparision. We can't do this in Item_field as this would change the 'name' of the item which may be used in the select list */ @@ -3220,81 +2152,99 @@ find_field_in_tables(THD *thd, Item_ident *item, db= name_buff; } - if (last_table) - last_table= last_table->next_name_resolution_table; - - for (; cur_table != last_table ; - cur_table= cur_table->next_name_resolution_table) + if (table_name && table_name[0]) + { /* Qualified field */ + bool found_table=0; + for (; tables ; tables=tables->next) + { + if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) && + (!db || !tables->db || !tables->db[0] || !strcmp(db,tables->db))) + { + found_table=1; + Field *find=find_field_in_table(thd,tables->table,name,length, + test(tables->table->grant. + want_privilege), + 1, &(item->cached_field_index)); + if (find) + { + (*where)= item->cached_table= tables; + if (!tables->cacheable_table) + item->cached_table= 0; + if (find == WRONG_GRANT) + return (Field*) 0; + if (db || !thd->where) + return find; + if (found) + { + my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), + item->full_name(),thd->where); + return (Field*) 0; + } + found=find; + } + } + } + if (found) + return found; + if (!found_table && report_error) + { + char buff[NAME_LEN*2+1]; + if (db && db[0]) + { + strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS); + table_name=buff; + } + my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), + table_name, thd->where); + } + else + if (report_error) + my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), + item->full_name(),thd->where); + else + return (Field*) not_found_field; + return (Field*) 0; + } + allow_rowid= tables && !tables->next; // Only one table + for (; tables ; tables=tables->next) { - Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length, - item->name, db, table_name, ref, - check_privileges, allow_rowid, - &(item->cached_field_index), - register_tree_change, - &actual_table); - if (cur_field) + if (!tables->table) { - if (cur_field == WRONG_GRANT) - return (Field*) 0; - - /* - Store the original table of the field, which may be different from - cur_table in the case of NATURAL/USING join. - */ - item->cached_table= (!actual_table->cacheable_table || found) ? - 0 : actual_table; - - DBUG_ASSERT(thd->where); - /* - If we found a fully qualified field we return it directly as it can't - have duplicates. - */ - if (db) - return cur_field; + if (report_error) + my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), + item->full_name(),thd->where); + return (Field*) not_found_field; + } + Field *field=find_field_in_table(thd,tables->table,name,length, + test(tables->table->grant.want_privilege), + allow_rowid, &(item->cached_field_index)); + if (field) + { + if (field == WRONG_GRANT) + return (Field*) 0; + (*where)= item->cached_table= tables; + if (!tables->cacheable_table) + item->cached_table= 0; if (found) { - if (report_error == REPORT_ALL_ERRORS || - report_error == IGNORE_EXCEPT_NON_UNIQUE) - my_error(ER_NON_UNIQ_ERROR, MYF(0), - table_name ? item->full_name() : name, thd->where); + if (!thd->where) // Returns first found + break; + my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), + name,thd->where); return (Field*) 0; } - found= cur_field; + found= field; } } - if (found) return found; - - /* - If the field was qualified and there were no tables to search, issue - an error that an unknown table was given. The situation is detected - as follows: if there were no tables we wouldn't go through the loop - and cur_table wouldn't be updated by the loop increment part, so it - will be equal to the first table. - */ - if (table_name && (cur_table == first_table) && - (report_error == REPORT_ALL_ERRORS || - report_error == REPORT_EXCEPT_NON_UNIQUE)) - { - char buff[NAME_LEN*2+1]; - if (db && db[0]) - { - strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS); - table_name=buff; - } - my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd->where); - } + if (report_error) + my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), + MYF(0), item->full_name(), thd->where); else - { - if (report_error == REPORT_ALL_ERRORS || - report_error == REPORT_EXCEPT_NON_UNIQUE) - my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd->where); - else - found= not_found_field; - } - return found; + return (Field*) not_found_field; + return (Field*) 0; } @@ -3328,8 +2278,8 @@ find_field_in_tables(THD *thd, Item_ident *item, found field */ -/* Special Item pointer to serve as a return value from find_item_in_list(). */ -Item **not_found_item= (Item**) 0x1; +// Special Item pointer for find_item_in_list returning +const Item **not_found_item= (const Item**) 0x1; Item ** @@ -3344,8 +2294,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, bool found_unaliased_non_uniq= 0; uint unaliased_counter; - LINT_INIT(unaliased_counter); // Dependent on found_unaliased - + LINT_INIT(unaliased_counter); *unaliased= FALSE; if (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM) @@ -3357,9 +2306,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, for (uint i= 0; (item=li++); i++) { - if (field_name && item->real_item()->type() == Item::FIELD_ITEM) + if (field_name && item->type() == Item::FIELD_ITEM) { - Item_ident *item_field= (Item_ident*) item; + Item_field *item_field= (Item_field*) item; /* In case of group_concat() with ORDER BY condition in the QUERY @@ -3405,8 +2354,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, unaliased names only and will have duplicate error anyway. */ if (report_error != IGNORE_ERRORS) - my_error(ER_NON_UNIQ_ERROR, MYF(0), - find->full_name(), current_thd->where); + my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), + MYF(0), find->full_name(), current_thd->where); return (Item**) 0; } found_unaliased= li.ref(); @@ -3430,8 +2379,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, if ((*found)->eq(item, 0)) continue; // Same field twice if (report_error != IGNORE_ERRORS) - my_error(ER_NON_UNIQ_ERROR, MYF(0), - find->full_name(), current_thd->where); + my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), + MYF(0), find->full_name(), current_thd->where); return (Item**) 0; } found= li.ref(); @@ -3459,7 +2408,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } } } - else if (!table_name && (find->eq(item,0) || + else if (!table_name && (item->eq(find,0) || find->name && item->name && !my_strcasecmp(system_charset_info, item->name,find->name))) @@ -3474,8 +2423,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, if (found_unaliased_non_uniq) { if (report_error != IGNORE_ERRORS) - my_error(ER_NON_UNIQ_ERROR, MYF(0), - find->full_name(), current_thd->where); + my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), MYF(0), + find->full_name(), current_thd->where); return (Item **) 0; } if (found_unaliased) @@ -3490,655 +2439,14 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, if (report_error != REPORT_EXCEPT_NOT_FOUND) { if (report_error == REPORT_ALL_ERRORS) - my_error(ER_BAD_FIELD_ERROR, MYF(0), - find->full_name(), current_thd->where); + my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), + find->full_name(), current_thd->where); return (Item **) 0; } else return (Item **) not_found_item; } - -/* - Test if a string is a member of a list of strings. - - SYNOPSIS - test_if_string_in_list() - find the string to look for - str_list a list of strings to be searched - - DESCRIPTION - Sequentially search a list of strings for a string, and test whether - the list contains the same string. - - RETURN - TRUE if find is in str_list - FALSE otherwise -*/ - -static bool -test_if_string_in_list(const char *find, List<String> *str_list) -{ - List_iterator<String> str_list_it(*str_list); - String *curr_str; - size_t find_length= strlen(find); - while ((curr_str= str_list_it++)) - { - if (find_length != curr_str->length()) - continue; - if (!my_strcasecmp(system_charset_info, find, curr_str->ptr())) - return TRUE; - } - return FALSE; -} - - -/* - Create a new name resolution context for an item so that it is - being resolved in a specific table reference. - - SYNOPSIS - set_new_item_local_context() - thd pointer to current thread - item item for which new context is created and set - table_ref table ref where an item showld be resolved - - DESCRIPTION - Create a new name resolution context for an item, so that the item - is resolved only the supplied 'table_ref'. - - RETURN - FALSE if all OK - TRUE otherwise -*/ - -static bool -set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref) -{ - Name_resolution_context *context; - if (!(context= new (thd->mem_root) Name_resolution_context)) - return TRUE; - context->init(); - context->first_name_resolution_table= - context->last_name_resolution_table= table_ref; - item->context= context; - return FALSE; -} - - -/* - Find and mark the common columns of two table references. - - SYNOPSIS - mark_common_columns() - thd [in] current thread - table_ref_1 [in] the first (left) join operand - table_ref_2 [in] the second (right) join operand - using_fields [in] if the join is JOIN...USING - the join columns, - if NATURAL join, then NULL - found_using_fields [out] number of fields from the USING clause that were - found among the common fields - - DESCRIPTION - The procedure finds the common columns of two relations (either - tables or intermediate join results), and adds an equi-join condition - to the ON clause of 'table_ref_2' for each pair of matching columns. - If some of table_ref_XXX represents a base table or view, then we - create new 'Natural_join_column' instances for each column - reference and store them in the 'join_columns' of the table - reference. - - IMPLEMENTATION - The procedure assumes that store_natural_using_join_columns() was - called for the previous level of NATURAL/USING joins. - - RETURN - TRUE error when some common column is non-unique, or out of memory - FALSE OK -*/ - -static bool -mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, - List<String> *using_fields, uint *found_using_fields) -{ - Field_iterator_table_ref it_1, it_2; - Natural_join_column *nj_col_1, *nj_col_2; - Query_arena *arena, backup; - bool result= TRUE; - bool first_outer_loop= TRUE; - /* - Leaf table references to which new natural join columns are added - if the leaves are != NULL. - */ - TABLE_LIST *leaf_1= (table_ref_1->nested_join && - !table_ref_1->is_natural_join) ? - NULL : table_ref_1; - TABLE_LIST *leaf_2= (table_ref_2->nested_join && - !table_ref_2->is_natural_join) ? - NULL : table_ref_2; - - DBUG_ENTER("mark_common_columns"); - DBUG_PRINT("info", ("operand_1: %s operand_2: %s", - table_ref_1->alias, table_ref_2->alias)); - - *found_using_fields= 0; - arena= thd->activate_stmt_arena_if_needed(&backup); - - for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) - { - bool found= FALSE; - const char *field_name_1; - if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1))) - goto err; - field_name_1= nj_col_1->name(); - - /* - Find a field with the same name in table_ref_2. - - Note that for the second loop, it_2.set() will iterate over - table_ref_2->join_columns and not generate any new elements or - lists. - */ - nj_col_2= NULL; - for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next()) - { - Natural_join_column *cur_nj_col_2; - const char *cur_field_name_2; - if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2))) - goto err; - cur_field_name_2= cur_nj_col_2->name(); - - /* - Compare the two columns and check for duplicate common fields. - A common field is duplicate either if it was already found in - table_ref_2 (then found == TRUE), or if a field in table_ref_2 - was already matched by some previous field in table_ref_1 - (then cur_nj_col_2->is_common == TRUE). - */ - if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) - { - if (found || cur_nj_col_2->is_common) - { - my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); - goto err; - } - nj_col_2= cur_nj_col_2; - found= TRUE; - } - } - if (first_outer_loop && leaf_2) - { - /* - Make sure that the next inner loop "knows" that all columns - are materialized already. - */ - leaf_2->is_join_columns_complete= TRUE; - first_outer_loop= FALSE; - } - if (!found) - continue; // No matching field - - /* - field_1 and field_2 have the same names. Check if they are in the USING - clause (if present), mark them as common fields, and add a new - equi-join condition to the ON clause. - */ - if (nj_col_2 && - (!using_fields || - test_if_string_in_list(field_name_1, using_fields))) - { - Item *item_1= nj_col_1->create_item(thd); - Item *item_2= nj_col_2->create_item(thd); - Field *field_1= nj_col_1->field(); - Field *field_2= nj_col_2->field(); - Item_ident *item_ident_1, *item_ident_2; - Item_func_eq *eq_cond; - - if (!item_1 || !item_2) - goto err; // out of memory - - /* - The following assert checks that the two created items are of - type Item_ident. - */ - DBUG_ASSERT(!thd->lex->current_select->no_wrap_view_item); - /* - In the case of no_wrap_view_item == 0, the created items must be - of sub-classes of Item_ident. - */ - DBUG_ASSERT(item_1->type() == Item::FIELD_ITEM || - item_1->type() == Item::REF_ITEM); - DBUG_ASSERT(item_2->type() == Item::FIELD_ITEM || - item_2->type() == Item::REF_ITEM); - - /* - We need to cast item_1,2 to Item_ident, because we need to hook name - resolution contexts specific to each item. - */ - item_ident_1= (Item_ident*) item_1; - item_ident_2= (Item_ident*) item_2; - /* - Create and hook special name resolution contexts to each item in the - new join condition . We need this to both speed-up subsequent name - resolution of these items, and to enable proper name resolution of - the items during the execute phase of PS. - */ - if (set_new_item_local_context(thd, item_ident_1, nj_col_1->table_ref) || - set_new_item_local_context(thd, item_ident_2, nj_col_2->table_ref)) - goto err; - - if (!(eq_cond= new Item_func_eq(item_ident_1, item_ident_2))) - goto err; /* Out of memory. */ - - /* - Add the new equi-join condition to the ON clause. Notice that - fix_fields() is applied to all ON conditions in setup_conds() - so we don't do it here. - */ - add_join_on((table_ref_1->outer_join & JOIN_TYPE_RIGHT ? - table_ref_1 : table_ref_2), - eq_cond); - - nj_col_1->is_common= nj_col_2->is_common= TRUE; - - if (field_1) - { - /* Mark field_1 used for table cache. */ - field_1->query_id= thd->query_id; - nj_col_1->table_ref->table->used_keys.intersect(field_1->part_of_key); - } - if (field_2) - { - /* Mark field_2 used for table cache. */ - field_2->query_id= thd->query_id; - nj_col_2->table_ref->table->used_keys.intersect(field_2->part_of_key); - } - - if (using_fields != NULL) - ++(*found_using_fields); - } - } - if (leaf_1) - leaf_1->is_join_columns_complete= TRUE; - - /* - Everything is OK. - Notice that at this point there may be some column names in the USING - clause that are not among the common columns. This is an SQL error and - we check for this error in store_natural_using_join_columns() when - (found_using_fields < length(join_using_fields)). - */ - result= FALSE; - -err: - if (arena) - thd->restore_active_arena(arena, &backup); - DBUG_RETURN(result); -} - - - -/* - Materialize and store the row type of NATURAL/USING join. - - SYNOPSIS - store_natural_using_join_columns() - thd current thread - natural_using_join the table reference of the NATURAL/USING join - table_ref_1 the first (left) operand (of a NATURAL/USING join). - table_ref_2 the second (right) operand (of a NATURAL/USING join). - using_fields if the join is JOIN...USING - the join columns, - if NATURAL join, then NULL - found_using_fields number of fields from the USING clause that were - found among the common fields - - DESCRIPTION - Iterate over the columns of both join operands and sort and store - all columns into the 'join_columns' list of natural_using_join - where the list is formed by three parts: - part1: The coalesced columns of table_ref_1 and table_ref_2, - sorted according to the column order of the first table. - part2: The other columns of the first table, in the order in - which they were defined in CREATE TABLE. - part3: The other columns of the second table, in the order in - which they were defined in CREATE TABLE. - Time complexity - O(N1+N2), where Ni = length(table_ref_i). - - IMPLEMENTATION - The procedure assumes that mark_common_columns() has been called - for the join that is being processed. - - RETURN - TRUE error: Some common column is ambiguous - FALSE OK -*/ - -static bool -store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, - TABLE_LIST *table_ref_1, - TABLE_LIST *table_ref_2, - List<String> *using_fields, - uint found_using_fields) -{ - Field_iterator_table_ref it_1, it_2; - Natural_join_column *nj_col_1, *nj_col_2; - Query_arena *arena, backup; - bool result= TRUE; - List<Natural_join_column> *non_join_columns; - DBUG_ENTER("store_natural_using_join_columns"); - - DBUG_ASSERT(!natural_using_join->join_columns); - - arena= thd->activate_stmt_arena_if_needed(&backup); - - if (!(non_join_columns= new List<Natural_join_column>) || - !(natural_using_join->join_columns= new List<Natural_join_column>)) - goto err; - - /* Append the columns of the first join operand. */ - for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) - { - nj_col_1= it_1.get_natural_column_ref(); - if (nj_col_1->is_common) - { - natural_using_join->join_columns->push_back(nj_col_1); - /* Reset the common columns for the next call to mark_common_columns. */ - nj_col_1->is_common= FALSE; - } - else - non_join_columns->push_back(nj_col_1); - } - - /* - Check that all columns in the USING clause are among the common - columns. If this is not the case, report the first one that was - not found in an error. - */ - if (using_fields && found_using_fields < using_fields->elements) - { - String *using_field_name; - List_iterator_fast<String> using_fields_it(*using_fields); - while ((using_field_name= using_fields_it++)) - { - const char *using_field_name_ptr= using_field_name->c_ptr(); - List_iterator_fast<Natural_join_column> - it(*(natural_using_join->join_columns)); - Natural_join_column *common_field; - - for (;;) - { - /* If reached the end of fields, and none was found, report error. */ - if (!(common_field= it++)) - { - my_error(ER_BAD_FIELD_ERROR, MYF(0), using_field_name_ptr, - current_thd->where); - goto err; - } - if (!my_strcasecmp(system_charset_info, - common_field->name(), using_field_name_ptr)) - break; // Found match - } - } - } - - /* Append the non-equi-join columns of the second join operand. */ - for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next()) - { - nj_col_2= it_2.get_natural_column_ref(); - if (!nj_col_2->is_common) - non_join_columns->push_back(nj_col_2); - else - { - /* Reset the common columns for the next call to mark_common_columns. */ - nj_col_2->is_common= FALSE; - } - } - - if (non_join_columns->elements > 0) - natural_using_join->join_columns->concat(non_join_columns); - natural_using_join->is_join_columns_complete= TRUE; - - result= FALSE; - -err: - if (arena) - thd->restore_active_arena(arena, &backup); - DBUG_RETURN(result); -} - - -/* - Precompute and store the row types of the top-most NATURAL/USING joins. - - SYNOPSIS - store_top_level_join_columns() - thd current thread - table_ref nested join or table in a FROM clause - left_neighbor neighbor table reference to the left of table_ref at the - same level in the join tree - right_neighbor neighbor table reference to the right of table_ref at the - same level in the join tree - - DESCRIPTION - The procedure performs a post-order traversal of a nested join tree - and materializes the row types of NATURAL/USING joins in a - bottom-up manner until it reaches the TABLE_LIST elements that - represent the top-most NATURAL/USING joins. The procedure should be - applied to each element of SELECT_LEX::top_join_list (i.e. to each - top-level element of the FROM clause). - - IMPLEMENTATION - Notice that the table references in the list nested_join->join_list - are in reverse order, thus when we iterate over it, we are moving - from the right to the left in the FROM clause. - - RETURN - TRUE Error - FALSE OK -*/ - -static bool -store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref, - TABLE_LIST *left_neighbor, - TABLE_LIST *right_neighbor) -{ - Query_arena *arena, backup; - bool result= TRUE; - - DBUG_ENTER("store_top_level_join_columns"); - - arena= thd->activate_stmt_arena_if_needed(&backup); - - /* Call the procedure recursively for each nested table reference. */ - if (table_ref->nested_join) - { - List_iterator_fast<TABLE_LIST> nested_it(table_ref->nested_join->join_list); - TABLE_LIST *cur_left_neighbor= nested_it++; - TABLE_LIST *cur_right_neighbor= NULL; - - while (cur_left_neighbor) - { - TABLE_LIST *cur_table_ref= cur_left_neighbor; - cur_left_neighbor= nested_it++; - /* - The order of RIGHT JOIN operands is reversed in 'join list' to - transform it into a LEFT JOIN. However, in this procedure we need - the join operands in their lexical order, so below we reverse the - join operands. Notice that this happens only in the first loop, and - not in the second one, as in the second loop cur_left_neighbor == NULL. - This is the correct behavior, because the second loop - sets cur_table_ref reference correctly after the join operands are - swapped in the first loop. - */ - if (cur_left_neighbor && - cur_table_ref->outer_join & JOIN_TYPE_RIGHT) - { - /* This can happen only for JOIN ... ON. */ - DBUG_ASSERT(table_ref->nested_join->join_list.elements == 2); - swap_variables(TABLE_LIST*, cur_left_neighbor, cur_table_ref); - } - - if (cur_table_ref->nested_join && - store_top_level_join_columns(thd, cur_table_ref, - cur_left_neighbor, cur_right_neighbor)) - goto err; - cur_right_neighbor= cur_table_ref; - } - } - - /* - If this is a NATURAL/USING join, materialize its result columns and - convert to a JOIN ... ON. - */ - if (table_ref->is_natural_join) - { - DBUG_ASSERT(table_ref->nested_join && - table_ref->nested_join->join_list.elements == 2); - List_iterator_fast<TABLE_LIST> operand_it(table_ref->nested_join->join_list); - /* - Notice that the order of join operands depends on whether table_ref - represents a LEFT or a RIGHT join. In a RIGHT join, the operands are - in inverted order. - */ - TABLE_LIST *table_ref_2= operand_it++; /* Second NATURAL join operand.*/ - TABLE_LIST *table_ref_1= operand_it++; /* First NATURAL join operand. */ - List<String> *using_fields= table_ref->join_using_fields; - uint found_using_fields; - - /* - The two join operands were interchanged in the parser, change the order - back for 'mark_common_columns'. - */ - if (table_ref_2->outer_join & JOIN_TYPE_RIGHT) - swap_variables(TABLE_LIST*, table_ref_1, table_ref_2); - if (mark_common_columns(thd, table_ref_1, table_ref_2, - using_fields, &found_using_fields)) - goto err; - - /* - Swap the join operands back, so that we pick the columns of the second - one as the coalesced columns. In this way the coalesced columns are the - same as of an equivalent LEFT JOIN. - */ - if (table_ref_1->outer_join & JOIN_TYPE_RIGHT) - swap_variables(TABLE_LIST*, table_ref_1, table_ref_2); - if (store_natural_using_join_columns(thd, table_ref, table_ref_1, - table_ref_2, using_fields, - found_using_fields)) - goto err; - - /* - Change NATURAL JOIN to JOIN ... ON. We do this for both operands - because either one of them or the other is the one with the - natural join flag because RIGHT joins are transformed into LEFT, - and the two tables may be reordered. - */ - table_ref_1->natural_join= table_ref_2->natural_join= NULL; - - /* Add a TRUE condition to outer joins that have no common columns. */ - if (table_ref_2->outer_join && - !table_ref_1->on_expr && !table_ref_2->on_expr) - table_ref_2->on_expr= new Item_int((longlong) 1,1); /* Always true. */ - - /* Change this table reference to become a leaf for name resolution. */ - if (left_neighbor) - { - TABLE_LIST *last_leaf_on_the_left; - last_leaf_on_the_left= left_neighbor->last_leaf_for_name_resolution(); - last_leaf_on_the_left->next_name_resolution_table= table_ref; - } - if (right_neighbor) - { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= right_neighbor->first_leaf_for_name_resolution(); - table_ref->next_name_resolution_table= first_leaf_on_the_right; - } - else - table_ref->next_name_resolution_table= NULL; - } - result= FALSE; /* All is OK. */ - -err: - if (arena) - thd->restore_active_arena(arena, &backup); - DBUG_RETURN(result); -} - - -/* - Compute and store the row types of the top-most NATURAL/USING joins - in a FROM clause. - - SYNOPSIS - setup_natural_join_row_types() - thd current thread - from_clause list of top-level table references in a FROM clause - - DESCRIPTION - Apply the procedure 'store_top_level_join_columns' to each of the - top-level table referencs of the FROM clause. Adjust the list of tables - for name resolution - context->first_name_resolution_table to the - top-most, lef-most NATURAL/USING join. - - IMPLEMENTATION - Notice that the table references in 'from_clause' are in reverse - order, thus when we iterate over it, we are moving from the right - to the left in the FROM clause. - - RETURN - TRUE Error - FALSE OK -*/ -static bool setup_natural_join_row_types(THD *thd, - List<TABLE_LIST> *from_clause, - Name_resolution_context *context) -{ - thd->where= "from clause"; - if (from_clause->elements == 0) - return FALSE; /* We come here in the case of UNIONs. */ - - /* For stored procedures do not redo work if already done. */ - if (!context->select_lex->first_execution) - return FALSE; - - List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause); - TABLE_LIST *table_ref; /* Current table reference. */ - /* Table reference to the left of the current. */ - TABLE_LIST *left_neighbor; - /* Table reference to the right of the current. */ - TABLE_LIST *right_neighbor= NULL; - - /* Note that tables in the list are in reversed order */ - for (left_neighbor= table_ref_it++; left_neighbor ; ) - { - table_ref= left_neighbor; - left_neighbor= table_ref_it++; - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - return TRUE; - if (left_neighbor) - { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; - } - right_neighbor= table_ref; - } - - /* - Store the top-most, left-most NATURAL/USING join, so that we start - the search from that one instead of context->table_list. At this point - right_neighbor points to the left-most top-level table reference in the - FROM clause. - */ - DBUG_ASSERT(right_neighbor); - context->first_name_resolution_table= - right_neighbor->first_leaf_for_name_resolution(); - - return FALSE; -} - - /**************************************************************************** ** Expand all '*' in given fields ****************************************************************************/ @@ -4147,29 +2455,27 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num) { + DBUG_ENTER("setup_wild"); if (!wild_num) - return(0); + DBUG_RETURN(0); - Item *item; + reg2 Item *item; List_iterator<Item> it(fields); - Query_arena *arena, backup; - DBUG_ENTER("setup_wild"); - + Item_arena *arena, backup; /* - Don't use arena if we are not in prepared statements or stored procedures - For PS/SP we have to use arena to remember the changes + If we are in preparing prepared statement phase then we have change + temporary mem_root to statement mem root to save changes of SELECT list */ - arena= thd->activate_stmt_arena_if_needed(&backup); + arena= thd->change_arena_if_needed(&backup); while (wild_num && (item= it++)) - { + { if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name && ((Item_field*) item)->field_name[0] == '*' && !((Item_field*) item)->field) { uint elem= fields.elements; - bool any_privileges= ((Item_field *) item)->any_privileges; Item_subselect *subsel= thd->lex->current_select->master_unit()->item; if (subsel && subsel->substype() == Item_subselect::EXISTS_SUBS) @@ -4181,13 +2487,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, */ it.replace(new Item_int("Not_used", (longlong) 1, 21)); } - else if (insert_fields(thd, ((Item_field*) item)->context, - ((Item_field*) item)->db_name, - ((Item_field*) item)->table_name, &it, - any_privileges)) + else if (insert_fields(thd,tables,((Item_field*) item)->db_name, + ((Item_field*) item)->table_name, &it)) { - if (arena) - thd->restore_active_arena(arena, &backup); + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(-1); } if (sum_func_list) @@ -4203,14 +2507,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, } } if (arena) - { - /* make * substituting permanent */ - SELECT_LEX *select_lex= thd->lex->current_select; - select_lex->with_wild= 0; - select_lex->item_list= fields; - - thd->restore_active_arena(arena, &backup); - } + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(0); } @@ -4218,20 +2515,17 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ** Check that all given fields exists and fill struct with current data ****************************************************************************/ -bool setup_fields(THD *thd, Item **ref_pointer_array, - List<Item> &fields, bool set_query_id, - List<Item> *sum_func_list, bool allow_sum_func) +int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, + List<Item> &fields, bool set_query_id, + List<Item> *sum_func_list, bool allow_sum_func) { reg2 Item *item; - bool save_set_query_id= thd->set_query_id; - nesting_map save_allow_sum_func= thd->lex->allow_sum_func; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; - if (allow_sum_func) - thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; - thd->where= THD::DEFAULT_WHERE; + thd->allow_sum_func= allow_sum_func; + thd->where="field list"; /* To prevent fail on forward lookup we fill it with zerows, @@ -4250,119 +2544,51 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, Item **ref= ref_pointer_array; while ((item= it++)) { - if (!item->fixed && item->fix_fields(thd, it.ref()) || + if (!item->fixed && item->fix_fields(thd, tables, it.ref()) || (item= *(it.ref()))->check_cols(1)) - { - thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; - DBUG_RETURN(TRUE); /* purecov: inspected */ - } + DBUG_RETURN(-1); /* purecov: inspected */ if (ref) *(ref++)= item; if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && sum_func_list) item->split_sum_func(thd, ref_pointer_array, *sum_func_list); - thd->used_tables|= item->used_tables(); + thd->used_tables|=item->used_tables(); } - thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; DBUG_RETURN(test(thd->net.report_error)); } /* - make list of leaves of join table tree - - SYNOPSIS - make_leaves_list() - list pointer to pointer on list first element - tables table list - - RETURN pointer on pointer to next_leaf of last element -*/ - -TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) -{ - for (TABLE_LIST *table= tables; table; table= table->next_local) - { - if (table->merge_underlying_list) - { - DBUG_ASSERT(table->view && - table->effective_algorithm == VIEW_ALGORITHM_MERGE); - list= make_leaves_list(list, table->merge_underlying_list); - } - else - { - *list= table; - list= &table->next_leaf; - } - } - return list; -} - -/* prepare tables SYNOPSIS setup_tables() - thd Thread handler - context name resolution contest to setup table list there - from_clause Top-level list of table references in the FROM clause - tables Table list (select_lex->table_list) - conds Condition of current SELECT (can be changed by VIEW) - leaves List of join table leaves list (select_lex->leaf_tables) - refresh It is onle refresh for subquery - select_insert It is SELECT ... INSERT command + tables table list - NOTE - Check also that the 'used keys' and 'ignored keys' exists and set up the - table structure accordingly. - Create a list of leaf tables. For queries with NATURAL/USING JOINs, - compute the row types of the top most natural/using join table references - and link these into a list of table references for name resolution. - This has to be called for all tables that are used by items, as otherwise - table->map is not set and all Item_field will be regarded as const items. + NOTE + Remap table numbers if INSERT ... SELECT + Check also that the 'used keys' and 'ignored keys' exists and set up the + table structure accordingly - RETURN - FALSE ok; In this case *map will includes the chosen index - TRUE error + This has to be called for all tables that are used by items, as otherwise + table->map is not set and all Item_field will be regarded as const items. + + RETURN + 0 ok; In this case *map will includes the choosed index + 1 error */ -bool setup_tables(THD *thd, Name_resolution_context *context, - List<TABLE_LIST> *from_clause, TABLE_LIST *tables, - Item **conds, TABLE_LIST **leaves, bool select_insert) +bool setup_tables(TABLE_LIST *tables) { - uint tablenr= 0; DBUG_ENTER("setup_tables"); - - context->table_list= context->first_name_resolution_table= tables; - - /* - this is used for INSERT ... SELECT. - For select we setup tables except first (and its underlying tables) - */ - TABLE_LIST *first_select_table= (select_insert ? - tables->next_local: - 0); - if (!(*leaves)) - make_leaves_list(leaves, tables); - - TABLE_LIST *table_list; - for (table_list= *leaves; - table_list; - table_list= table_list->next_leaf, tablenr++) + uint tablenr=0; + for (TABLE_LIST *table_list=tables ; table_list ; + table_list=table_list->next,tablenr++) { TABLE *table= table_list->table; - if (first_select_table && - table_list->top_table() == first_select_table) - { - /* new counting for SELECT of INSERT ... SELECT command */ - first_select_table= 0; - tablenr= 0; - } setup_table_map(table, table_list, tablenr); - table->used_keys= table->s->keys_for_keyread; + table->used_keys= table->keys_for_keyread; if (table_list->use_index) { key_map map; @@ -4386,32 +2612,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context, my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); DBUG_RETURN(1); } - for (table_list= tables; - table_list; - table_list= table_list->next_local) - { - if (table_list->merge_underlying_list) - { - DBUG_ASSERT(table_list->view && - table_list->effective_algorithm == VIEW_ALGORITHM_MERGE); - Query_arena *arena= thd->stmt_arena, backup; - bool res; - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_active_arena(arena, &backup); - res= table_list->setup_underlying(thd); - if (arena) - thd->restore_active_arena(arena, &backup); - if (res) - DBUG_RETURN(1); - } - } - - /* Precompute and store the row types of NATURAL/USING joins. */ - if (setup_natural_join_row_types(thd, from_clause, context)) - DBUG_RETURN(1); - DBUG_RETURN(0); } @@ -4440,13 +2640,11 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, map->clear_all(); while ((name=it++)) { - if (table->s->keynames.type_names == 0 || - (pos= find_type(&table->s->keynames, name->ptr(), - name->length(), 1)) <= - 0) + if ((pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <= + 0) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->s->table_name); + table->real_name); map->set_all(); return 1; } @@ -4456,34 +2654,18 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, } -/* - Drops in all fields instead of current '*' field - - SYNOPSIS - insert_fields() - thd Thread handler - context Context for name resolution - db_name Database name in case of 'database_name.table_name.*' - table_name Table name in case of 'table_name.*' - it Pointer to '*' - any_privileges 0 If we should ensure that we have SELECT privileges - for all columns - 1 If any privilege is ok - RETURN - 0 ok 'it' is updated to point at last inserted - 1 error. Error message is generated but not sent to client -*/ +/**************************************************************************** + This just drops in all fields instead of current '*' field + Returns pointer to last inserted field if ok +****************************************************************************/ bool -insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, - const char *table_name, List_iterator<Item> *it, - bool any_privileges) +insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, + const char *table_name, List_iterator<Item> *it) { - Field_iterator_table_ref field_iterator; - bool found; char name_buff[NAME_LEN+1]; + uint found; DBUG_ENTER("insert_fields"); - DBUG_PRINT("arena", ("stmt arena: 0x%lx", (ulong)thd->stmt_arena)); if (db_name && lower_case_table_names) { @@ -4497,285 +2679,216 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, db_name= name_buff; } - found= FALSE; - /* - If table names are qualified, then loop over all tables used in the query, - else treat natural joins as leaves and do not iterate over their underlying - tables. - */ - for (TABLE_LIST *tables= (table_name ? context->table_list : - context->first_name_resolution_table); - tables; - tables= (table_name ? tables->next_local : - tables->next_name_resolution_table) - ) + found=0; + for (; tables ; tables=tables->next) { - Field *field; - TABLE *table= tables->table; - - DBUG_ASSERT(tables->is_leaf_for_name_resolution()); - - if (table_name && my_strcasecmp(table_alias_charset, table_name, - tables->alias) || - (db_name && strcmp(tables->db,db_name))) - continue; - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Ensure that we have access rights to all fields to be inserted. */ - if (!((table && (table->grant.privilege & SELECT_ACL) || - tables->view && (tables->grant.privilege & SELECT_ACL))) && - !any_privileges) - { - field_iterator.set(tables); - if (check_grant_all_columns(thd, SELECT_ACL, field_iterator.grant(), - field_iterator.db_name(), - field_iterator.table_name(), - &field_iterator)) - DBUG_RETURN(TRUE); - } -#endif - - - /* - Update the tables used in the query based on the referenced fields. For - views and natural joins this update is performed inside the loop below. - */ - if (table) - thd->used_tables|= table->map; - - /* - Initialize a generic field iterator for the current table reference. - Notice that it is guaranteed that this iterator will iterate over the - fields of a single table reference, because 'tables' is a leaf (for - name resolution purposes). - */ - field_iterator.set(tables); - - for (; !field_iterator.end_of_fields(); field_iterator.next()) + TABLE *table=tables->table; + if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, + tables->alias) && + (!db_name || !strcmp(tables->db,db_name)))) { - Item *item; - - if (!(item= field_iterator.create_item(thd))) - DBUG_RETURN(TRUE); - - if (!found) - { - found= TRUE; - it->replace(item); /* Replace '*' with the first found item. */ - } - else - it->after(item); /* Add 'item' to the SELECT list. */ - #ifndef NO_EMBEDDED_ACCESS_CHECKS - /* - Set privilege information for the fields of newly created views. - We have that (any_priviliges == TRUE) if and only if we are creating - a view. In the time of view creation we can't use the MERGE algorithm, - therefore if 'tables' is itself a view, it is represented by a - temporary table. Thus in this case we can be sure that 'item' is an - Item_field. - */ - if (any_privileges) - { - DBUG_ASSERT(tables->field_translation == NULL && table || - tables->is_natural_join); - DBUG_ASSERT(item->type() == Item::FIELD_ITEM); - Item_field *fld= (Item_field*) item; - const char *field_table_name= field_iterator.table_name(); - - if (!tables->schema_table && - !(fld->have_privileges= - (get_column_grant(thd, field_iterator.grant(), - field_iterator.db_name(), - field_table_name, fld->field_name) & - VIEW_ANY_ACL))) - { - my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY", - thd->security_ctx->priv_user, - thd->security_ctx->host_or_ip, - fld->field_name, field_table_name); - DBUG_RETURN(TRUE); - } - } + /* Ensure that we have access right to all columns */ + if (!(table->grant.privilege & SELECT_ACL) && + check_grant_all_columns(thd,SELECT_ACL,table)) + DBUG_RETURN(-1); #endif + Field **ptr=table->field,*field; + TABLE *natural_join_table= 0; - if ((field= field_iterator.field())) - { - /* - Mark if field used before in this select. - Used by 'insert' to verify if a field name is used twice. - */ - if (field->query_id == thd->query_id) - thd->dupp_field= field; - field->query_id= thd->query_id; - - if (table) - table->used_keys.intersect(field->part_of_key); + thd->used_tables|=table->map; + if (!table->outer_join && + tables->natural_join && + !tables->natural_join->table->outer_join) + natural_join_table= tables->natural_join->table; - if (tables->is_natural_join) + while ((field = *ptr++)) + { + uint not_used_field_index= NO_CACHED_FIELD_INDEX; + /* Skip duplicate field names if NATURAL JOIN is used */ + if (!natural_join_table || + !find_field_in_table(thd, natural_join_table, field->field_name, + strlen(field->field_name), 0, 0, + ¬_used_field_index)) { - TABLE *field_table; - /* - In this case we are sure that the column ref will not be created - because it was already created and stored with the natural join. - */ - Natural_join_column *nj_col; - if (!(nj_col= field_iterator.get_natural_column_ref())) - DBUG_RETURN(TRUE); - DBUG_ASSERT(nj_col->table_field); - field_table= nj_col->table_ref->table; - if (field_table) - { - thd->used_tables|= field_table->map; - field_table->used_keys.intersect(field->part_of_key); - field_table->used_fields++; - } + Item_field *item= new Item_field(thd, field); + if (!found++) + (void) it->replace(item); // Replace '*' + else + it->after(item); } + /* + Mark if field used before in this select. + Used by 'insert' to verify if a field name is used twice + */ + if (field->query_id == thd->query_id) + thd->dupp_field=field; + field->query_id=thd->query_id; + table->used_keys.intersect(field->part_of_key); } - else - { - thd->used_tables|= item->used_tables(); - item->walk(&Item::reset_query_id_processor, - (byte *)(&thd->query_id)); - } + /* All fields are used */ + table->used_fields=table->fields; } - /* - In case of stored tables, all fields are considered as used, - while in the case of views, the fields considered as used are the - ones marked in setup_tables during fix_fields of view columns. - For NATURAL joins, used_tables is updated in the IF above. - */ - if (table) - table->used_fields= table->s->fields; } - if (found) - DBUG_RETURN(FALSE); - - /* - TODO: in the case when we skipped all columns because there was a - qualified '*', and all columns were coalesced, we have to give a more - meaningful message than ER_BAD_TABLE_ERROR. - */ - if (!table_name) - my_message(ER_NO_TABLES_USED, ER(ER_NO_TABLES_USED), MYF(0)); - else - my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name); - - DBUG_RETURN(TRUE); + if (!found) + { + if (!table_name) + my_error(ER_NO_TABLES_USED,MYF(0)); + else + my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name); + } + DBUG_RETURN(!found); } /* - Fix all conditions and outer join expressions. - - SYNOPSIS - setup_conds() - thd thread handler - tables list of tables for name resolving (select_lex->table_list) - leaves list of leaves of join table tree (select_lex->leaf_tables) - conds WHERE clause - - DESCRIPTION - TODO - - RETURN - TRUE if some error occured (e.g. out of memory) - FALSE if all is OK +** Fix all conditions and outer join expressions */ -int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, - COND **conds) +int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { - SELECT_LEX *select_lex= thd->lex->current_select; - Query_arena *arena= thd->stmt_arena, backup; - TABLE_LIST *table= NULL; // For HP compilers - /* - it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX - which belong to LEX, i.e. most up SELECT) will be updated by - INSERT/UPDATE/LOAD - NOTE: using this condition helps to prevent call of prepare_check_option() - from subquery of VIEW, because tables of subquery belongs to VIEW - (see condition before prepare_check_option() call) - */ - bool it_is_update= (select_lex == &thd->lex->select_lex) && - thd->lex->which_check_option_applicable(); + table_map not_null_tables= 0; + Item_arena *arena= 0, backup; DBUG_ENTER("setup_conds"); - if (select_lex->conds_processed_with_permanent_arena || - arena->is_conventional()) - arena= 0; // For easier test - thd->set_query_id=1; - select_lex->cond_count= 0; - - for (table= tables; table; table= table->next_local) - { - if (table->prepare_where(thd, conds, FALSE)) - goto err_no_arena; - } - + thd->lex->current_select->cond_count= 0; if (*conds) { thd->where="where clause"; - if (!(*conds)->fixed && (*conds)->fix_fields(thd, conds) || + if (!(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds) || (*conds)->check_cols(1)) - goto err_no_arena; + DBUG_RETURN(1); + not_null_tables= (*conds)->not_null_tables(); } - /* - Apply fix_fields() to all ON clauses at all levels of nesting, - including the ones inside view definitions. - */ - for (table= leaves; table; table= table->next_leaf) + + /* Check if we are using outer joins */ + for (TABLE_LIST *table=tables ; table ; table=table->next) { - TABLE_LIST *embedded; /* The table at the current level of nesting. */ - TABLE_LIST *embedding= table; /* The parent nested table reference. */ - do + if (table->on_expr) { - embedded= embedding; - if (embedded->on_expr) + /* Make a join an a expression */ + thd->where="on clause"; + + if (!table->on_expr->fixed && + table->on_expr->fix_fields(thd, tables, &table->on_expr) || + table->on_expr->check_cols(1)) + DBUG_RETURN(1); + thd->lex->current_select->cond_count++; + + /* + If it's a normal join or a LEFT JOIN which can be optimized away + add the ON/USING expression to the WHERE + */ + if (!table->outer_join || + ((table->table->map & not_null_tables) && + !(specialflag & SPECIAL_NO_NEW_FUNC))) { - /* Make a join an a expression */ - thd->where="on clause"; - if (!embedded->on_expr->fixed && - embedded->on_expr->fix_fields(thd, &embedded->on_expr) || - embedded->on_expr->check_cols(1)) - goto err_no_arena; - select_lex->cond_count++; + table->outer_join= 0; + arena= thd->change_arena_if_needed(&backup); + *conds= and_conds(*conds, table->on_expr); + table->on_expr=0; + if (arena) + { + thd->restore_backup_item_arena(arena, &backup); + arena= 0; // Safety if goto err + } + if ((*conds) && !(*conds)->fixed && + (*conds)->fix_fields(thd, tables, conds)) + DBUG_RETURN(1); } - embedding= embedded->embedding; } - while (embedding && - embedding->nested_join->join_list.head() == embedded); - - /* process CHECK OPTION */ - if (it_is_update) + if (table->natural_join) { - TABLE_LIST *view= table->top_table(); - if (view->effective_with_check) + arena= thd->change_arena_if_needed(&backup); + /* Make a join of all fields with have the same name */ + TABLE *t1= table->table; + TABLE *t2= table->natural_join->table; + Item_cond_and *cond_and= new Item_cond_and(); + if (!cond_and) // If not out of memory + goto err; + cond_and->top_level_item(); + + Field **t1_field, *t2_field; + for (t1_field= t1->field; (*t1_field); t1_field++) { - if (view->prepare_check_option(thd)) - goto err_no_arena; - thd->change_item_tree(&table->check_option, view->check_option); + const char *t1_field_name= (*t1_field)->field_name; + uint not_used_field_index= NO_CACHED_FIELD_INDEX; + + if ((t2_field= find_field_in_table(thd, t2, t1_field_name, + strlen(t1_field_name), 0, 0, + ¬_used_field_index))) + { + Item_func_eq *tmp=new Item_func_eq(new Item_field(thd, *t1_field), + new Item_field(thd, t2_field)); + if (!tmp) + goto err; + /* Mark field used for table cache */ + (*t1_field)->query_id= t2_field->query_id= thd->query_id; + cond_and->list.push_back(tmp); + t1->used_keys.intersect((*t1_field)->part_of_key); + t2->used_keys.intersect(t2_field->part_of_key); + } + } + thd->lex->current_select->cond_count+= cond_and->list.elements; + + // to prevent natural join processing during PS re-execution + table->natural_join= 0; + + if (cond_and->list.elements) + { + if (!table->outer_join) // Not left join + { + *conds= and_conds(*conds, cond_and); + // fix_fields() should be made with temporary memory pool + if (arena) + thd->restore_backup_item_arena(arena, &backup); + if (*conds && !(*conds)->fixed) + { + if (!(*conds)->fixed && + (*conds)->fix_fields(thd, tables, conds)) + DBUG_RETURN(1); + } + } + else + { + table->on_expr= and_conds(table->on_expr, cond_and); + // fix_fields() should be made with temporary memory pool + if (arena) + thd->restore_backup_item_arena(arena, &backup); + if (table->on_expr && !table->on_expr->fixed) + { + if (!table->on_expr->fixed && + table->on_expr->fix_fields(thd, tables, &table->on_expr)) + DBUG_RETURN(1); + } + } + } + else if (arena) + { + thd->restore_backup_item_arena(arena, &backup); + arena= 0; // Safety if goto err } } } - if (!thd->stmt_arena->is_conventional()) + if (thd->current_arena->is_stmt_prepare()) { /* We are in prepared statement preparation code => we should store WHERE clause changing for next executions. - We do this ON -> WHERE transformation only once per PS/SP statement. + We do this ON -> WHERE transformation only once per PS statement. */ - select_lex->where= *conds; - select_lex->conds_processed_with_permanent_arena= 1; + thd->lex->current_select->where= *conds; } DBUG_RETURN(test(thd->net.report_error)); -err_no_arena: +err: + if (arena) + thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(1); } @@ -4785,25 +2898,8 @@ err_no_arena: ** Returns : 1 if some field has wrong type ******************************************************************************/ - -/* - Fill fields with given items. - - SYNOPSIS - fill_record() - thd thread handler - fields Item_fields list to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors - - RETURN - FALSE OK - TRUE error occured -*/ - -static bool -fill_record(THD * thd, List<Item> &fields, List<Item> &values, - bool ignore_errors) +int +fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> f(fields),v(values); Item *value; @@ -4818,67 +2914,14 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, if (rfield == table->next_number_field) table->auto_increment_field_not_null= TRUE; if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) - { - my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); - DBUG_RETURN(TRUE); - } + DBUG_RETURN(1); } - DBUG_RETURN(thd->net.report_error); + DBUG_RETURN(0); } -/* - Fill fields in list with values from the list of items and invoke - before triggers. - - SYNOPSIS - fill_record_n_invoke_before_triggers() - thd thread context - fields Item_fields list to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors - triggers object holding list of triggers to be invoked - event event type for triggers to be invoked - - NOTE - This function assumes that fields which values will be set and triggers - to be invoked belong to the same table, and that TABLE::record[0] and - record[1] buffers correspond to new and old versions of row respectively. - - RETURN - FALSE OK - TRUE error occured -*/ - -bool -fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, - List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, - enum trg_event_type event) -{ - return (fill_record(thd, fields, values, ignore_errors) || - triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE)); -} - - -/* - Fill field buffer with values from Field list - - SYNOPSIS - fill_record() - thd thread handler - ptr pointer on pointer to record - values list of fields - ignore_errors TRUE if we should ignore errors - - RETURN - FALSE OK - TRUE error occured -*/ - -bool -fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) +int +fill_record(Field **ptr,List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> v(values); Item *value; @@ -4891,45 +2934,10 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) TABLE *table= field->table; if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; - if (value->save_in_field(field, 0) == -1) - DBUG_RETURN(TRUE); + if ((value->save_in_field(field, 0) < 0) && !ignore_errors) + DBUG_RETURN(1); } - DBUG_RETURN(thd->net.report_error); -} - - -/* - Fill fields in array with values from the list of items and invoke - before triggers. - - SYNOPSIS - fill_record_n_invoke_before_triggers() - thd thread context - ptr NULL-ended array of fields to be filled - values values to fill with - ignore_errors TRUE if we should ignore errors - triggers object holding list of triggers to be invoked - event event type for triggers to be invoked - - NOTE - This function assumes that fields which values will be set and triggers - to be invoked belong to the same table, and that TABLE::record[0] and - record[1] buffers correspond to new and old versions of row respectively. - - RETURN - FALSE OK - TRUE error occured -*/ - -bool -fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, - List<Item> &values, bool ignore_errors, - Table_triggers_list *triggers, - enum trg_event_type event) -{ - return (fill_record(thd, ptr, values, ignore_errors) || - triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE)); + DBUG_RETURN(0); } @@ -4977,31 +2985,34 @@ static void mysql_rm_tmp_tables(void) *****************************************************************************/ /* - Invalidate any cache entries that are for some DB - - SYNOPSIS - remove_db_from_cache() - db Database name. This will be in lower case if - lower_case_table_name is set - - NOTE: - We can't use hash_delete when looping hash_elements. We mark them first - and afterwards delete those marked unused. +** Invalidate any cache entries that are for some DB +** We can't use hash_delete when looping hash_elements. We mark them first +** and afterwards delete those marked unused. */ void remove_db_from_cache(const char *db) { + char name_buff[NAME_LEN+1]; + if (db && lower_case_table_names) + { + /* + convert database to lower case for comparision. + */ + strmake(name_buff, db, sizeof(name_buff)-1); + my_casedn_str(files_charset_info, name_buff); + db= name_buff; + } for (uint idx=0 ; idx < open_cache.records ; idx++) { TABLE *table=(TABLE*) hash_element(&open_cache,idx); - if (!strcmp(table->s->db, db)) + if (!strcmp(table->table_cache_key,db)) { - table->s->version= 0L; /* Free when thread is ready */ + table->version=0L; /* Free when thread is ready */ if (!table->in_use) relink_unused(table); } } - while (unused_tables && !unused_tables->s->version) + while (unused_tables && !unused_tables->version) VOID(hash_delete(&open_cache,(byte*) unused_tables)); } @@ -5043,6 +3054,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, bool result=0, signalled= 0; DBUG_ENTER("remove_table_from_cache"); + key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) { @@ -5056,7 +3068,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, &state)) { THD *in_use; - table->s->version=0L; /* Free when thread is ready */ + table->version=0L; /* Free when thread is ready */ if (!(in_use=table->in_use)) { DBUG_PRINT("info",("Table was not in use")); @@ -5071,7 +3083,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { - in_use->killed= THD::KILL_CONNECTION; + in_use->killed=1; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -5097,7 +3109,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, else result= result || (flags & RTFC_OWNED_BY_THD_FLAG); } - while (unused_tables && !unused_tables->s->version) + while (unused_tables && !unused_tables->version) VOID(hash_delete(&open_cache,(byte*) unused_tables)); if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) { @@ -5167,69 +3179,3 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) } return 0; } - - -/* - open new .frm format table - - SYNOPSIS - open_new_frm() - THD thread handler - path path to .frm - alias alias for table - db database - table_name name of table - db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..) - can be 0 (example in ha_example_table) - prgflag READ_ALL etc.. - ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. - outparam result table - table_desc TABLE_LIST descriptor - mem_root temporary MEM_ROOT for parsing -*/ - -static bool -open_new_frm(THD *thd, const char *path, const char *alias, - const char *db, const char *table_name, - uint db_stat, uint prgflag, - uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, - MEM_ROOT *mem_root) -{ - LEX_STRING pathstr; - File_parser *parser; - DBUG_ENTER("open_new_frm"); - - pathstr.str= (char*) path; - pathstr.length= strlen(path); - - if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) - { - if (is_equal(&view_type, parser->type())) - { - if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE) - { - my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE"); - goto err; - } - if (mysql_make_view(thd, parser, table_desc)) - goto err; - } - else - { - /* only VIEWs are supported now */ - my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str); - goto err; - } - DBUG_RETURN(0); - } - -err: - bzero(outparam, sizeof(TABLE)); // do not run repair - DBUG_RETURN(1); -} - - -bool is_equal(const LEX_STRING *a, const LEX_STRING *b) -{ - return a->length == b->length && !strncmp(a->str, b->str, a->length); -} |