diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 1786 |
1 files changed, 1273 insertions, 513 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index fe1f268e277..5f9f743932d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -19,6 +19,8 @@ #include "mysql_priv.h" #include "sql_select.h" +#include "sp_head.h" +#include "sql_trigger.h" #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -31,18 +33,24 @@ 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); +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 void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); - +static my_bool open_new_frm(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); +static void relink_tables_for_multidelete(THD *thd); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) { TABLE *entry=(TABLE*) record; - *length=entry->key_length; - return (byte*) entry->table_cache_key; + *length= entry->s->key_length; + return (byte*) entry->s->table_cache_key; } bool table_cache_init(void) @@ -140,20 +148,21 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) { OPEN_TABLE_LIST *table; TABLE *entry=(TABLE*) hash_element(&open_cache,idx); + TABLE_SHARE *share= entry->s; - DBUG_ASSERT(entry->real_name); - if ((!entry->real_name)) // To be removed + DBUG_ASSERT(share->table_name); + if ((!share->table_name)) // To be removed continue; // Shouldn't happen if (wild) { - strxmov(name,entry->table_cache_key,".",entry->real_name,NullS); + strxmov(name,share->table_cache_key,".",share->table_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*) entry->table_cache_key; - table_list.real_name= entry->real_name; + table_list.db= (char*) share->db; + table_list.table_name= (char*) share->table_name; table_list.grant.privilege=0; if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) @@ -161,8 +170,8 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, 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,entry->real_name) && - !strcmp(table->db,entry->table_cache_key)) + if (!strcmp(table->table,share->table_name) && + !strcmp(table->db,entry->s->db)) { if (entry->in_use) table->in_use++; @@ -174,15 +183,15 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) if (table) continue; if (!(*start_list = (OPEN_TABLE_LIST *) - sql_alloc(sizeof(**start_list)+entry->key_length))) + sql_alloc(sizeof(**start_list)+share->key_length))) { open_list=0; // Out of memory break; } strmov((*start_list)->table= strmov(((*start_list)->db= (char*) ((*start_list)+1)), - entry->table_cache_key)+1, - entry->real_name); + entry->s->db)+1, + entry->s->table_name); (*start_list)->in_use= entry->in_use ? 1 : 0; (*start_list)->locked= entry->locked_by_name ? 1 : 0; start_list= &(*start_list)->next; @@ -200,6 +209,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, 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 } @@ -282,9 +292,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) + for (TABLE_LIST *table= tables; table; table= table->next_local) { - if (remove_table_from_cache(thd, table->db, table->real_name, 1)) + if (remove_table_from_cache(thd, table->db, table->table_name, 1)) found=1; } if (!found) @@ -316,7 +326,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->version) < refresh_version && table->db_stat) + if ((table->s->version) < refresh_version && table->db_stat) { found=1; pthread_cond_wait(&COND_refresh,&LOCK_open); @@ -333,8 +343,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->version=refresh_version; + for (TABLE *table=thd->open_tables; table ; table= table->next) + table->s->version= refresh_version; } VOID(pthread_mutex_unlock(&LOCK_open)); if (if_wait_for_refresh) @@ -365,7 +375,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, Put all normal tables used by thread in free list. */ -void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) +void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, + TABLE *stopper) { bool found_old_table; DBUG_ENTER("close_thread_tables"); @@ -400,10 +411,10 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) 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) + while (thd->open_tables != stopper) found_old_table|=close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; @@ -431,9 +442,10 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) bool found_old_table= 0; TABLE *table= *table_ptr; DBUG_ASSERT(table->key_read == 0); + DBUG_ASSERT(table->file->inited == handler::NONE); *table_ptr=table->next; - if (table->version != refresh_version || + if (table->s->version != refresh_version || thd->version != refresh_version || !table->db_stat) { VOID(hash_delete(&open_cache,(byte*) table)); @@ -441,9 +453,9 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) } else { - if (table->flush_version != flush_version) + if (table->s->flush_version != flush_version) { - table->flush_version=flush_version; + table->s->flush_version= flush_version; table->file->extra(HA_EXTRA_FLUSH); } else @@ -471,8 +483,8 @@ void close_temporary(TABLE *table,bool delete_table) { DBUG_ENTER("close_temporary"); char path[FN_REFLEN]; - db_type table_type=table->db_type; - strmov(path,table->path); + db_type table_type=table->s->db_type; + strmov(path,table->s->path); free_io_cache(table); closefrm(table); my_free((char*) table,MYF(0)); @@ -500,7 +512,7 @@ void close_temporary_tables(THD *thd) /* We'll re-use always same buffer so make it big enough for longest name */ for (table=thd->temporary_tables ; table ; table=table->next) - greatest_key_length= max(greatest_key_length, table->key_length); + greatest_key_length= max(greatest_key_length, table->s->key_length); if ((query = alloc_root(thd->mem_root, greatest_key_length+50))) // Better add "if exists", in case a RESET MASTER has been done @@ -512,14 +524,14 @@ void close_temporary_tables(THD *thd) In we are OOM for 'query' this is not fatal. We skip temporary tables not created directly by the user. */ - if (query && mysql_bin_log.is_open() && (table->real_name[0] != '#')) + if (query && mysql_bin_log.is_open() && (table->s->table_name[0] != '#')) { /* Here we assume table_cache_key always starts with \0 terminated db name */ - end = strxmov(name_in_query, table->table_cache_key, "`.`", - table->real_name, "`", NullS); + end = strxmov(name_in_query, table->s->db, "`.`", + table->s->table_name, "`", NullS); Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE); /* Imagine the thread had created a temp table, then was doing a SELECT, and @@ -540,56 +552,119 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } + /* - Find first suitable table by alias in given list. + Find table in list. SYNOPSIS find_table_in_list() - table - pointer to table list - db_name - data base name or 0 for any - table_name - table name or 0 for any + 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(). RETURN VALUES NULL Table not found # Pointer to found table. */ -TABLE_LIST * find_table_in_list(TABLE_LIST *table, - const char *db_name, const char *table_name) +TABLE_LIST *find_table_in_list(TABLE_LIST *table, + uint offset, + const char *db_name, + const char *table_name) { - 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; + if (lower_case_table_names) + { + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { + if ((table->table == 0 || table->table->s->tmp_table == NO_TMP_TABLE) && + ((!strcmp(table->db, db_name) && + !strcmp(table->table_name, table_name)) || + (table->view && + !my_strcasecmp(table_alias_charset, + table->db, db_name) && + !my_strcasecmp(table_alias_charset, + table->table->alias, table_name)))) + break; + } + } + else + { + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { + if ((table->table == 0 || table->table->s->tmp_table == NO_TMP_TABLE) && + ((!strcmp(table->db, db_name) && + !strcmp(table->table_name, table_name)) || + (table->view && + !strcmp(table->table->s->db, db_name) && + !strcmp(table->table->alias, table_name)))) + break; + } + } return table; } + /* - Find real table in given list. + Test that table is unique SYNOPSIS - find_real_table_in_list() - table - pointer to table list - db_name - data base name - table_name - table name + unique_table() + table table which should be chaked + table_list list of tables - RETURN VALUES - NULL Table not found - # Pointer to found table. + RETURN + found duplicate + 0 if table is unique */ -TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) +TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list) { - for (; table; table= table->next) - if (!strcmp(table->db, db_name) && - !strcmp(table->real_name, table_name)) + DBUG_ENTER("unique_table"); + DBUG_PRINT("enter", ("table alias: %s", table->alias)); + TABLE_LIST *res; + const char *d_name= table->db, *t_name= table->table_name; + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + /* temporary table is always unique */ + if (table->table && table->table->s->tmp_table != NO_TMP_TABLE) + return 0; + if (table->view) + { + /* it is view and table opened */ + if (lower_case_table_names) + { + strmov(t_name_buff, table->table->alias); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + strmov(d_name_buff, table->table->s->db); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + else + { + d_name= table->table->s->db; + t_name= table->table->alias; + } + } + + 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->table || res->table != table->table) break; - return table; + /* if we found entry of this table try again. */ + table_list= res->next_global; + DBUG_PRINT("info", ("found same copy of table")); + } + DBUG_RETURN(res); } + TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; @@ -604,8 +679,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->key_length == key_length && - !memcmp(table->table_cache_key,key,key_length)) + if (table->s->key_length == key_length && + !memcmp(table->s->table_cache_key,key,key_length)) return prev; prev= &table->next; } @@ -633,22 +708,26 @@ 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 */ - 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; + 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; return 0; } @@ -679,15 +758,16 @@ 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->key_length; + uint key_length= find->s->key_length; TABLE *start=list,**prev,*next; prev= &start; - memcpy(key,find->table_cache_key,key_length); + + memcpy(key, find->s->table_cache_key, key_length); for (; list ; list=next) { next=list->next; - if (list->key_length == key_length && - !memcmp(list->table_cache_key,key,key_length)) + if (list->s->key_length == key_length && + !memcmp(list->s->table_cache_key, key, key_length)) { if (thd->locked_tables) mysql_lock_remove(thd, thd->locked_tables,list); @@ -738,29 +818,34 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) DBUG_ENTER("reopen_name_locked_table"); if (thd->killed) DBUG_RETURN(0); - TABLE* table; + TABLE *table; + TABLE_SHARE *share; if (!(table = table_list->table)) DBUG_RETURN(0); char* db = thd->db ? thd->db : table_list->db; - char* table_name = table_list->real_name; + char* table_name = table_list->table_name; char key[MAX_DBKEY_LENGTH]; uint key_length; key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; 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))) + 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))) { + delete table->triggers; closefrm(table); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } - table->key_length=key_length; - table->version=0; - table->flush_version=0; + share= table->s; + share->db= share->table_cache_key; + share->key_length=key_length; + share->version=0; + share->flush_version=0; table->in_use = thd; check_unused(); pthread_mutex_unlock(&LOCK_open); @@ -769,10 +854,10 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query= table->keys_in_use; - table->used_keys= table->keys_for_keyread; + table->keys_in_use_for_query= share->keys_in_use; + table->used_keys= share->keys_for_keyread; DBUG_RETURN(table); } @@ -787,12 +872,13 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) ******************************************************************************/ -TABLE *open_table(THD *thd,const char *db,const char *table_name, - const char *alias,bool *refresh) +TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, + bool *refresh) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; uint key_length; + char *alias= table_list->alias; DBUG_ENTER("open_table"); /* find a unused table in the open table cache */ @@ -800,27 +886,30 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, *refresh=0; if (thd->killed) DBUG_RETURN(0); - key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1; + key_length= (uint) (strmov(strmov(key, table_list->db)+1, + table_list->table_name)-key)+1; int4store(key + key_length, thd->server_id); int4store(key + key_length + 4, thd->variables.pseudo_thread_id); - for (table=thd->temporary_tables; table ; table=table->next) + if (!table_list->skip_temporary) { - if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA && - !memcmp(table->table_cache_key, key, - key_length + TMP_TABLE_KEY_EXTRA)) + for (table= thd->temporary_tables; table ; table=table->next) { - if (table->query_id == thd->query_id) + if (table->s->key_length == key_length + TMP_TABLE_KEY_EXTRA && + !memcmp(table->s->table_cache_key, key, + key_length + TMP_TABLE_KEY_EXTRA)) { - my_printf_error(ER_CANT_REOPEN_TABLE, - ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name); - DBUG_RETURN(0); + if (table->query_id == thd->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; } - table->query_id=thd->query_id; - table->clear_query_id=1; - thd->tmp_table_used= 1; - DBUG_PRINT("info",("Using temporary table")); - goto reset; } } @@ -828,9 +917,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, { // Using table locks for (table=thd->open_tables; table ; table=table->next) { - if (table->key_length == key_length && - !memcmp(table->table_cache_key,key,key_length) && - !my_strcasecmp(system_charset_info, table->table_name, alias) && + if (table->s->key_length == key_length && + !memcmp(table->s->table_cache_key,key,key_length) && + !my_strcasecmp(system_charset_info, table->alias, alias) && table->query_id != thd->query_id) { table->query_id=thd->query_id; @@ -838,7 +927,38 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, goto reset; } } - my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias); + /* + 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]; + 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(path) == FRMTYPE_VIEW) + { + TABLE tab;// will not be used (because it's VIEW) but have to be passed + 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)) + { + table->next=table->prev=table; + free_cache_entry(table); + } + else + { + DBUG_ASSERT(table_list->view); + 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); DBUG_RETURN(0); } @@ -855,13 +975,14 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, } /* close handler tables which are marked for flush */ - mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE); + if (thd->handler_tables) + mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE); for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ; table && table->in_use ; table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length)) { - if (table->version != refresh_version) + if (table->s->version != refresh_version) { /* ** There is a refresh in progress for this table @@ -871,7 +992,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, if (table->in_use != thd) wait_for_refresh(thd); else + { VOID(pthread_mutex_unlock(&LOCK_open)); + } if (refresh) *refresh=1; DBUG_RETURN(0); @@ -887,10 +1010,11 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, } 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 */ @@ -901,25 +1025,35 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (open_unireg_entry(thd, table,db,table_name,alias) || - !(table->table_cache_key=memdup_root(&table->mem_root,(char*) key, - key_length))) + 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)))) { table->next=table->prev=table; free_cache_entry(table); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - table->key_length=key_length; - table->version=refresh_version; - table->flush_version=flush_version; + 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; 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) { @@ -929,51 +1063,29 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, 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->table_name,alias)) + if (strcmp(table->alias, alias)) { uint length=(uint) strlen(alias)+1; - 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; + table->alias= (char*) my_realloc((char*) table->alias, length, + MYF(MY_WME)); + memcpy((char*) table->alias, alias, length); } -#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->outer_join= table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query= table->keys_in_use; - table->used_keys= table->keys_for_keyread; + table->keys_in_use_for_query= table->s->keys_in_use; + table->insert_values= 0; + table->used_keys= table->s->keys_for_keyread; 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_RETURN(table); } @@ -986,8 +1098,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->key_length == key_length && - !memcmp(table->table_cache_key,key,key_length)) + if (table->s->key_length == key_length && + !memcmp(table->s->table_cache_key,key,key_length)) return table; } return(0); @@ -1004,9 +1116,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->table_cache_key; - char *table_name=table->real_name; - bool error=1; + char *db= table->s->table_cache_key; + const char *table_name= table->s->table_name; + bool error= 1; Field **field; uint key,part; DBUG_ENTER("reopen_table"); @@ -1014,60 +1126,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->table_name); + table->alias); #endif if (!locked) VOID(pthread_mutex_lock(&LOCK_open)); safe_mutex_assert_owner(&LOCK_open); - if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name)) + if (open_unireg_entry(table->in_use, &tmp, db, table_name, + table->alias, 0, table->in_use->mem_root)) goto end; free_io_cache(table); - if (!(tmp.table_cache_key= memdup_root(&tmp.mem_root,db, - table->key_length))) + if (!(tmp.s->table_cache_key= memdup_root(&tmp.mem_root,db, + table->s->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.keys_in_use; - tmp.used_keys= tmp.keys_for_keyread; + tmp.keys_in_use_for_query= tmp.s->keys_in_use; + tmp.used_keys= tmp.s->keys_for_keyread; tmp.force_index= tmp.force_index; /* Get state */ - tmp.key_length= table->key_length; + tmp.s->key_length= table->s->key_length; tmp.in_use= table->in_use; tmp.reginfo.lock_type=table->reginfo.lock_type; - tmp.version= refresh_version; - tmp.tmp_table= table->tmp_table; + tmp.s->version= refresh_version; + tmp.s->tmp_table= table->s->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= tmp; + table->s= &table->share_not_to_be_used; table->file->change_table_ptr(table); - DBUG_ASSERT(table->table_name); + DBUG_ASSERT(table->alias); for (field=table->field ; *field ; field++) { (*field)->table= (*field)->orig_table= table; - (*field)->table_name=table->table_name; + (*field)->table_name= &table->alias; } - for (key=0 ; key < table->keys ; key++) + for (key=0 ; key < table->s->keys ; key++) for (part=0 ; part < table->key_info[key].usable_key_parts ; part++) table->key_info[key].key_part[part].field->table= table; VOID(pthread_cond_broadcast(&COND_refresh)); @@ -1091,8 +1207,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->real_name,table_name) && - !strcmp(table->table_cache_key,db)) + if (!strcmp(table->s->table_name, table_name) && + !strcmp(table->s->db, db)) { mysql_lock_remove(thd, thd->locked_tables,table); table->file->close(); @@ -1137,7 +1253,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->table_name); + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); VOID(hash_delete(&open_cache,(byte*) table)); error=1; } @@ -1149,7 +1265,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) *tables_ptr++= table; // need new lock on this if (in_refresh) { - table->version=0; + table->s->version=0; table->locked_by_flush=0; } } @@ -1188,11 +1304,11 @@ void close_old_data_files(THD *thd, TABLE *table, bool abort_locks, bool found=send_refresh; for (; table ; table=table->next) { - if (table->version != refresh_version) + if (table->s->version != refresh_version) { found=1; if (!abort_locks) // If not from flush tables - table->version = refresh_version; // Let other threads use table + table->s->version= refresh_version; // Let other threads use table if (table->db_stat) { if (abort_locks) @@ -1222,8 +1338,8 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) { do { - char *key= table->table_cache_key; - uint key_length=table->key_length; + char *key= table->s->table_cache_key; + uint key_length= table->s->key_length; for (TABLE *search=(TABLE*) hash_search(&open_cache, (byte*) key,key_length) ; search ; @@ -1231,7 +1347,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) { if (search->locked_by_flush || search->locked_by_name && wait_for_name_lock || - search->db_stat && search->version < refresh_version) + search->db_stat && search->s->version < refresh_version) return 1; // Table is used } } while ((table=table->next)); @@ -1278,11 +1394,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->real_name,table_name) && - !strcmp(table->table_cache_key,db)) + if (!strcmp(table->s->table_name, table_name) && + !strcmp(table->s->db, db)) { mysql_lock_remove(thd, thd->locked_tables,table); VOID(hash_delete(&open_cache,(byte*) table)); @@ -1317,8 +1433,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->real_name,table_name) && - !strcmp(table->table_cache_key,db)) + if (!strcmp(table->s->table_name,table_name) && + !strcmp(table->s->db, db)) { mysql_lock_abort(thd,table); break; @@ -1337,6 +1453,8 @@ 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 @@ -1345,9 +1463,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) + const char *name, const char *alias, + TABLE_LIST *table_desc, MEM_ROOT *mem_root) { char path[FN_REFLEN]; int error; @@ -1355,19 +1473,28 @@ 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 (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->crashed) + 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(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)))) + + { + if (!entry->s || !entry->s->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; @@ -1382,7 +1509,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.real_name=(char*) name; + table_list.table_name=(char*) name; safe_mutex_assert_owner(&LOCK_open); @@ -1401,7 +1528,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(path,alias, + if (openfrm(thd, path, alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, @@ -1426,6 +1553,13 @@ 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 + + if (Table_triggers_list::check_n_load(thd, db, name, entry)) + goto err; + /* If we are here, there was no fatal error (but error may be still unitialized). @@ -1454,6 +1588,7 @@ 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; @@ -1462,6 +1597,14 @@ 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); } @@ -1485,12 +1628,18 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) bool refresh; int result=0; DBUG_ENTER("open_tables"); + MEM_ROOT new_frm_mem; + /* + 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; thd->proc_info="Opening tables"; - for (tables=start ; tables ; tables=tables->next) + for (tables= start; tables ;tables= tables->next_global) { /* Ignore placeholders for derived tables. After derived tables @@ -1498,13 +1647,23 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) */ if (tables->derived) 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->db, - tables->real_name, - tables->alias, &refresh))) + !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh))) { + free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); + if (tables->view) + { + (*counter)--; + continue; //VIEW placeholder + } + if (refresh) // Refresh in progress { /* close all 'old' tables used by this thread */ @@ -1514,12 +1673,12 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) thd->version=refresh_version; TABLE **prev_table= &thd->open_tables; bool found=0; - for (TABLE_LIST *tmp=start ; tmp ; tmp=tmp->next) + for (TABLE_LIST *tmp= start; tmp; tmp= tmp->next_global) { /* Close normal (not temporary) changed tables */ - if (tmp->table && ! tmp->table->tmp_table) + if (tmp->table && ! tmp->table->s->tmp_table) { - if (tmp->table->version != refresh_version || + if (tmp->table->s->version != refresh_version || ! tmp->table->db_stat) { VOID(hash_delete(&open_cache,(byte*) tmp->table)); @@ -1542,11 +1701,15 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) result= -1; // Fatal error break; } + else + 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; } thd->proc_info=0; + free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block DBUG_RETURN(result); } @@ -1574,9 +1737,7 @@ 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_printf_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, - ER(ER_TABLE_NOT_LOCKED_FOR_WRITE), - MYF(0),table->table_name); + my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias); DBUG_RETURN(1); } if ((error=table->file->start_stmt(thd))) @@ -1614,9 +1775,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) thd->proc_info="Opening table"; thd->current_tablenr= 0; - while (!(table=open_table(thd,table_list->db, - table_list->real_name,table_list->alias, - &refresh)) && refresh) ; + /* 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)) && + refresh) + ; if (table) { @@ -1685,21 +1848,25 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) tables - list of tables for open&locking RETURN - 0 - ok - -1 - error + FALSE - ok + TRUE - error NOTE The lock will automaticly be freed by close_thread_tables() */ -int open_and_lock_tables(THD *thd, TABLE_LIST *tables) +bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) { - DBUG_ENTER("open_and_lock_tables"); 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)); + DBUG_ENTER("open_and_lock_tables"); + if (open_tables(thd, tables, &counter) || + lock_tables(thd, tables, counter) || + 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 */ + relink_tables_for_multidelete(thd); + DBUG_RETURN(0); } @@ -1720,35 +1887,40 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) data from the tables. */ -int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) +bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) { uint counter; DBUG_ENTER("open_normal_and_derived_tables"); - if (open_tables(thd, tables, &counter)) - DBUG_RETURN(-1); /* purecov: inspected */ - relink_tables_for_derived(thd); - DBUG_RETURN(mysql_handle_derived(thd->lex)); + DBUG_ASSERT(!thd->fill_derived_tables()); + if (open_tables(thd, tables, &counter) || + mysql_handle_derived(thd->lex, &mysql_derived_prepare)) + DBUG_RETURN(TRUE); /* purecov: inspected */ + relink_tables_for_multidelete(thd); // Not really needed, but + DBUG_RETURN(0); } /* Let us propagate pointers to open tables from global table list - to table lists in particular selects if needed. + to table lists for multi-delete */ -void relink_tables_for_derived(THD *thd) +static void relink_tables_for_multidelete(THD *thd) { - if (thd->lex->all_selects_list->next_select_in_list() || - thd->lex->time_zone_tables_used) + if (thd->lex->all_selects_list->next_select_in_list()) { 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; + cursor=cursor->next_local) + { + if (cursor->correspondent_table) + cursor->table= cursor->correspondent_table->table; + } + } } } @@ -1782,11 +1954,11 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; - if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count))) + if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) return -1; - for (table = tables ; table ; table=table->next) + for (table= tables; table; table= table->next_global) { - if (!table->derived) + if (!table->placeholder() && !table->schema_table) *(ptr++)= table->table; } if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start)))) @@ -1794,9 +1966,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) } else { - for (table = tables ; table ; table=table->next) + for (table= tables; table; table= table->next_global) { - if (!table->derived && + if (!table->placeholder() && check_lock_and_start_stmt(thd, table->table, table->lock_type)) { ha_rollback_stmt(thd); @@ -1818,6 +1990,7 @@ 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"); /* @@ -1832,7 +2005,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, MYF(MY_WME)))) DBUG_RETURN(0); /* purecov: inspected */ - if (openfrm(path, table_name, + if (openfrm(thd, path, table_name, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, ha_open_options, @@ -1842,21 +2015,22 @@ 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 - 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, + 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, thd->variables.pseudo_thread_id); - tmp_table->key_length += 4; + share->key_length+= 4; if (link_in_list) { @@ -1896,23 +2070,147 @@ bool rm_temporary_table(enum db_type base, char *path) ** return unique field ******************************************************************************/ +/* Special Field pointers for find_field_in_tables returning */ +Field *not_found_field= (Field*) 0x1; +Field *view_ref_found= (Field*) 0x2; + #define WRONG_GRANT (Field*) -1 -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) + +/* + Find field in table or view + + SYNOPSIS + find_field_in_table() + thd thread handler + table_list table where to find + name name of field + item_name name of item if it will be created (VIEW) + length length of name + ref expression substituted in VIEW should be + passed using this reference (return + view_ref_found) + check_grants_table do check columns grants for table? + check_grants_view do check columns grants for view? + allow_rowid do allow finding of "_rowid" field? + cached_field_index_ptr cached position in field list (used to + speedup prepared tables field finding) + 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 +*/ + +Field * +find_field_in_table(THD *thd, TABLE_LIST *table_list, + const char *name, const char *item_name, + uint length, Item **ref, + bool check_grants_table, bool check_grants_view, + bool allow_rowid, + uint *cached_field_index_ptr, + bool register_tree_change) +{ + Field *fld; + DBUG_ENTER("find_field_in_table"); + DBUG_PRINT("enter", ("table: '%s' name: '%s' item name: '%s' ref 0x%lx", + table_list->alias, name, item_name, (ulong) ref)); + if (table_list->field_translation) + { + DBUG_ASSERT(ref != 0 && table_list->view != 0); + uint num= table_list->view->select_lex.item_list.elements; + Field_translator *trans= table_list->field_translation; + for (uint i= 0; i < num; i ++) + { + if (!my_strcasecmp(system_charset_info, trans[i].name, name)) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_grants_view && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + DBUG_RETURN(WRONG_GRANT); +#endif + if (thd->lex->current_select->no_wrap_view_item) + { + if (register_tree_change) + thd->change_item_tree(ref, trans[i].item); + else + *ref= trans[i].item; + } + else + { + Item_ref *item_ref= new Item_ref(&trans[i].item, + table_list->view_name.str, + item_name); + /* as far as Item_ref have defined reference it do not need tables */ + if (register_tree_change && item_ref) + thd->change_item_tree(ref, item_ref); + else if (item_ref) + *ref= item_ref; + } + DBUG_RETURN((Field*) view_ref_found); + } + } + DBUG_RETURN(0); + } + fld= find_field_in_real_table(thd, table_list->table, name, length, + check_grants_table, allow_rowid, + cached_field_index_ptr); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* check for views with temporary table algorithm */ + if (check_grants_view && table_list->view && + fld && fld != WRONG_GRANT && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + { + DBUG_RETURN(WRONG_GRANT); + } +#endif + DBUG_RETURN(fld); +} + + +/* + Find field in table + + SYNOPSIS + find_field_in_real_table() + thd thread handler + table_list table where to find + name name of field + length length of name + check_grants do check columns grants? + allow_rowid do allow finding of "_rowid" field? + cached_field_index_ptr cached position in field list (used to + speedup prepared tables field finding) + + RETURN + 0 field is not found + # pointer to field +*/ + +Field *find_field_in_real_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; /* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */ - if (cached_field_index < table->fields && + if (cached_field_index < table->s->fields && !my_strcasecmp(system_charset_info, table->field[cached_field_index]->field_name, name)) field_ptr= table->field + cached_field_index; - else if (table->name_hash.records) - field_ptr= (Field**)hash_search(&table->name_hash,(byte*) name, - length); + else if (table->s->name_hash.records) + field_ptr= (Field**) hash_search(&table->s->name_hash, (byte*) name, + length); else { if (!(field_ptr= table->field)) @@ -1947,7 +2245,9 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, thd->dupp_field=field; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_grants && check_grant_column(thd,table,name,length)) + if (check_grants && check_grant_column(thd, &table->grant, + table->s->db, + table->s->table_name, name, length)) return WRONG_GRANT; #endif return field; @@ -1961,26 +2261,33 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, find_field_in_tables() 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 + tables Tables to be searched for item + 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 RETURN VALUES - 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 + 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 */ -// 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 *tables, - TABLE_LIST **where, bool report_error) + Item **ref, find_item_error_report_type report_error, + bool check_privileges) { Field *found=0; const char *db=item->db_name; @@ -1993,24 +2300,42 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, if (item->cached_table) { /* - 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 + 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 optimisation we also miss case when addition of one more - field makes some prepared query ambiguous and so erronous, but we + With this optimization we also miss case when addition of one more + field makes some prepared query ambiguous and so erroneous, but we accept this trade off. */ - 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 (item->cached_table->table && !item->cached_table->view) + { + found= find_field_in_real_table(thd, item->cached_table->table, + name, length, + test(item->cached_table-> + table->grant.want_privilege) && + check_privileges, + 1, &(item->cached_field_index)); + } + else + { + TABLE_LIST *table= item->cached_table; + found= find_field_in_table(thd, table, name, item->name, length, + ref, + (table->table && + test(table->table->grant. + want_privilege) && + check_privileges), + (test(table->grant.want_privilege) && + check_privileges), + 1, &(item->cached_field_index), + TRUE); + } if (found) { - (*where)= tables; if (found == WRONG_GRANT) - return (Field*) 0; + return (Field*) 0; return found; } } @@ -2018,7 +2343,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, if (db && lower_case_table_names) { /* - convert database to lower case for comparision. + convert database to lower case for comparison. 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 */ @@ -2030,19 +2355,27 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, if (table_name && table_name[0]) { /* Qualified field */ bool found_table=0; - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_local) { + /* TODO; Ensure that db and tables->db always points to something ! */ if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) && - (!db || !tables->db || !tables->db[0] || !strcmp(db,tables->db))) + (!db || !db[0] || !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)); + Field *find= find_field_in_table(thd, tables, name, item->name, + length, ref, + (tables->table && + test(tables->table->grant. + want_privilege) && + check_privileges), + (test(tables->grant.want_privilege) && + check_privileges), + 1, &(item->cached_field_index), + TRUE); if (find) { - (*where)= item->cached_table= tables; + item->cached_table= tables; if (!tables->cacheable_table) item->cached_table= 0; if (find == WRONG_GRANT) @@ -2051,8 +2384,10 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return find; if (found) { - my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), - item->full_name(),thd->where); + if (report_error == REPORT_ALL_ERRORS || + report_error == IGNORE_EXCEPT_NON_UNIQUE) + my_error(ER_NON_UNIQ_ERROR, MYF(0), + item->full_name(),thd->where); return (Field*) 0; } found=find; @@ -2061,7 +2396,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, } if (found) return found; - if (!found_table && report_error) + if (!found_table && (report_error == REPORT_ALL_ERRORS || + report_error == REPORT_EXCEPT_NON_UNIQUE)) { char buff[NAME_LEN*2+1]; if (db && db[0]) @@ -2069,44 +2405,51 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, 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); + my_error(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); + 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 return (Field*) not_found_field; return (Field*) 0; } - bool allow_rowid= tables && !tables->next; // Only one table - for (; tables ; tables=tables->next) + + bool allow_rowid= tables && !tables->next_local; // Only one table + for (; tables ; tables= tables->next_local) { - if (!tables->table) + if (!tables->table && !tables->ancestor) { - if (report_error) - my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), - item->full_name(),thd->where); + 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); 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)); + Field *field= find_field_in_table(thd, tables, name, item->name, + length, ref, + (tables->table && + test(tables->table->grant. + want_privilege) && + check_privileges), + (test(tables->grant.want_privilege) && + check_privileges), + allow_rowid, + &(item->cached_field_index), + TRUE); if (field) { if (field == WRONG_GRANT) return (Field*) 0; - (*where)= item->cached_table= tables; - if (!tables->cacheable_table) - item->cached_table= 0; + item->cached_table= (!tables->cacheable_table || found) ? 0 : tables; if (found) { if (!thd->where) // Returns first found break; - my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), - name,thd->where); + if (report_error == REPORT_ALL_ERRORS || + report_error == IGNORE_EXCEPT_NON_UNIQUE) + my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); return (Field*) 0; } found=field; @@ -2114,9 +2457,9 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, } if (found) return found; - if (report_error) - my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), - MYF(0), item->full_name(), thd->where); + 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 return (Field*) not_found_field; return (Field*) 0; @@ -2153,8 +2496,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, found field */ -// Special Item pointer for find_item_in_list returning -const Item **not_found_item= (const Item**) 0x1; +/* Special Item pointer to serve as a return value from find_item_in_list(). */ +Item **not_found_item= (Item**) 0x1; Item ** @@ -2169,6 +2512,8 @@ 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 + *unaliased= FALSE; if (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM) @@ -2228,8 +2573,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_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), - MYF(0), find->full_name(), current_thd->where); + my_error(ER_NON_UNIQ_ERROR, MYF(0), + find->full_name(), current_thd->where); return (Item**) 0; } found_unaliased= li.ref(); @@ -2253,8 +2598,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_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), - MYF(0), find->full_name(), current_thd->where); + my_error(ER_NON_UNIQ_ERROR, MYF(0), + find->full_name(), current_thd->where); return (Item**) 0; } found= li.ref(); @@ -2297,8 +2642,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, if (found_unaliased_non_uniq) { if (report_error != IGNORE_ERRORS) - my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), MYF(0), - find->full_name(), current_thd->where); + my_error(ER_NON_UNIQ_ERROR, MYF(0), + find->full_name(), current_thd->where); return (Item **) 0; } if (found_unaliased) @@ -2313,8 +2658,8 @@ 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_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), - find->full_name(), current_thd->where); + my_error(ER_BAD_FIELD_ERROR, MYF(0), + find->full_name(), current_thd->where); return (Item **) 0; } else @@ -2329,27 +2674,29 @@ 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) - DBUG_RETURN(0); + return(0); - reg2 Item *item; + Item *item; List_iterator<Item> it(fields); Item_arena *arena, backup; + DBUG_ENTER("setup_wild"); + /* - 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 + 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 */ 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) @@ -2362,9 +2709,10 @@ 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,tables,((Item_field*) item)->db_name, - ((Item_field*) item)->table_name, &it)) + ((Item_field*) item)->table_name, &it, + any_privileges, arena != 0)) { - if (arena) + if (arena) thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(-1); } @@ -2381,7 +2729,14 @@ 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_backup_item_arena(arena, &backup); + } DBUG_RETURN(0); } @@ -2389,12 +2744,13 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ** Check that all given fields exists and fill struct with current data ****************************************************************************/ -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) +bool 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; List_iterator<Item> it(fields); + SELECT_LEX *select_lex= thd->lex->current_select; DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; @@ -2420,7 +2776,9 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, { if (!item->fixed && item->fix_fields(thd, tables, it.ref()) || (item= *(it.ref()))->check_cols(1)) - DBUG_RETURN(-1); /* purecov: inspected */ + { + DBUG_RETURN(TRUE); /* purecov: inspected */ + } if (ref) *(ref++)= item; if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && @@ -2433,36 +2791,91 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, /* + 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->view && !table->table) + { + /* it is for multi table views only, check it */ + DBUG_ASSERT(table->ancestor->next_local); + list= make_leaves_list(list, table->ancestor); + } + else + { + *list= table; + list= &table->next_leaf; + } + } + return list; +} + +/* prepare tables SYNOPSIS setup_tables() - tables table list - + thd Thread handler + tables Table list + conds Condition of current SELECT (can be changed by VIEW) + leaves List of join table leaves list + refresh It is onle refresh for subquery + select_insert It is SELECT ... INSERT command - NOTE - Remap table numbers if INSERT ... SELECT - Check also that the 'used keys' and 'ignored keys' exists and set up the - table structure accordingly + NOTE + Check also that the 'used keys' and 'ignored keys' exists and set up the + table structure accordingly + Create leaf tables list - 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. + 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 + RETURN + FALSE ok; In this case *map will includes the choosed index + TRUE error */ -bool setup_tables(TABLE_LIST *tables) +bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds, + TABLE_LIST **leaves, bool refresh, bool select_insert) { + uint tablenr= 0; DBUG_ENTER("setup_tables"); - uint tablenr=0; - for (TABLE_LIST *table_list=tables ; table_list ; - table_list=table_list->next,tablenr++) + /* + 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); + + for (TABLE_LIST *table_list= *leaves; + table_list; + table_list= table_list->next_leaf, tablenr++) { TABLE *table= table_list->table; + if (first_select_table && + (table_list->belong_to_view ? + table_list->belong_to_view : + table_list) == 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->keys_for_keyread; + table->used_keys= table->s->keys_for_keyread; if (table_list->use_index) { key_map map; @@ -2486,6 +2899,18 @@ bool setup_tables(TABLE_LIST *tables) my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); DBUG_RETURN(1); } + if (!refresh) + { + for (TABLE_LIST *table_list= tables; + table_list; + table_list= table_list->next_local) + { + if (table_list->ancestor && + table_list->setup_ancestor(thd, conds, + table_list->effective_with_check)) + DBUG_RETURN(1); + } + } DBUG_RETURN(0); } @@ -2514,11 +2939,13 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, map->clear_all(); while ((name=it++)) { - if ((pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <= - 0) + if (table->s->keynames.type_names == 0 || + (pos= find_type(&table->s->keynames, name->ptr(), + name->length(), 1)) <= + 0) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->real_name); + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), + name->c_ptr(), table->s->table_name); map->set_all(); return 1; } @@ -2528,17 +2955,37 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, } -/**************************************************************************** - This just drops in all fields instead of current '*' field - Returns pointer to last inserted field if ok -****************************************************************************/ +/* + Drops in all fields instead of current '*' field + + SYNOPSIS + insert_fields() + thd Thread handler + tables List of tables + 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 + allocate_view_names if true view names will be copied to current Item_arena + memory (made for SP/PS) + RETURN + 0 ok + 'it' is updated to point at last inserted + 1 error. Error message is generated but not sent to client +*/ bool -insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, - const char *table_name, List_iterator<Item> *it) +insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, + const char *table_name, List_iterator<Item> *it, + bool any_privileges, bool allocate_view_names) { - char name_buff[NAME_LEN+1]; + /* allocate variables on stack to avoid pool alloaction */ + Field_iterator_table table_iter; + Field_iterator_view view_iter; uint found; + char name_buff[NAME_LEN+1]; DBUG_ENTER("insert_fields"); if (db_name && lower_case_table_names) @@ -2553,216 +3000,432 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, db_name= name_buff; } - - found=0; - for (; tables ; tables=tables->next) + found= 0; + for (; tables; tables= tables->next_local) { - TABLE *table=tables->table; + Field_iterator *iterator; + TABLE_LIST *natural_join_table; + Field *field; + TABLE_LIST *embedded; + TABLE_LIST *last; + TABLE_LIST *embedding; + TABLE *table= tables->table; + bool alias_used= 0; + if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, tables->alias) && (!db_name || !strcmp(tables->db,db_name)))) { + bool view; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* 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); + if (!((table && (table->grant.privilege & SELECT_ACL) || + tables->view && (tables->grant.privilege & SELECT_ACL))) && + !any_privileges) + { + if (tables->view) + { + view_iter.set(tables); + if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant, + tables->view_db.str, + tables->view_name.str, + &view_iter)) + goto err; + } + else if (!tables->schema_table) + { + DBUG_ASSERT(table != 0); + table_iter.set(tables); + if (check_grant_all_columns(thd, SELECT_ACL, &table->grant, + table->s->db, + table->s->table_name, + &table_iter)) + goto err; + } + } #endif - Field **ptr=table->field,*field; - TABLE *natural_join_table= 0; + if (table) + thd->used_tables|= table->map; + else + { + view_iter.set(tables); + for (; !view_iter.end_of_fields(); view_iter.next()) + { + thd->used_tables|= view_iter.item(thd)->used_tables(); + } + } + natural_join_table= 0; + last= embedded= tables; - 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; + while ((embedding= embedded->embedding) && + embedding->join_list->elements != 1) + { + TABLE_LIST *next; + List_iterator_fast<TABLE_LIST> it(embedding->nested_join->join_list); + last= it++; + while ((next= it++)) + last= next; + if (last != tables) + break; + embedded= embedding; + } - while ((field = *ptr++)) + if (tables == last && + !embedded->outer_join && + embedded->natural_join && + !embedded->natural_join->outer_join) + { + embedding= embedded->natural_join; + while (embedding->nested_join) + embedding= embedding->nested_join->join_list.head(); + natural_join_table= embedding; + } + if (tables->field_translation) + { + iterator= &view_iter; + view= 1; + alias_used= my_strcasecmp(table_alias_charset, + tables->table_name, tables->alias); + } + else { + iterator= &table_iter; + view= 0; + } + iterator->set(tables); + + for (; !iterator->end_of_fields(); iterator->next()) + { + Item *not_used_item; uint not_used_field_index= NO_CACHED_FIELD_INDEX; + const char *field_name= iterator->name(); /* 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)) + !find_field_in_table(thd, natural_join_table, field_name, + field_name, + strlen(field_name), ¬_used_item, 0, 0, 0, + ¬_used_field_index, TRUE)) { - Item_field *item= new Item_field(thd, field); + Item *item= iterator->item(thd); + if (view && !thd->lex->current_select->no_wrap_view_item) + { + /* + as far as we have view, then item point to view_iter, so we + can use it directly for this view specific operation + */ + item= new Item_ref(view_iter.item_ptr(), tables->view_name.str, + field_name); + } if (!found++) (void) it->replace(item); // Replace '*' else it->after(item); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (any_privileges) + { + /* + In time of view creation MEGRGE algorithm for underlying + VIEWs can't be used => it should be Item_field + */ + DBUG_ASSERT(item->type() == Item::FIELD_ITEM); + Item_field *fld= (Item_field*)item; + char *db, *tab; + if (tables->view) + { + db= tables->view_db.str; + tab= tables->view_name.str; + } + else + { + db= tables->db; + tab= tables->table_name; + } + if (!tables->schema_table && + !(fld->have_privileges= (get_column_grant(thd, + &table->grant, + db, + tab, + fld->field_name) & + VIEW_ANY_ACL))) + { + my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), + "ANY", + thd->priv_user, + thd->host_or_ip, + fld->field_name, + tab); + goto err; + } + } +#endif + } + if ((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; + table->used_keys.intersect(field->part_of_key); + } + else if (allocate_view_names && + thd->lex->current_select->first_execution) + { + Item_field *item; + if (alias_used) + item= new Item_field(0, + thd->strdup(tables->alias), + thd->strdup(field_name)); + else + item= new Item_field(thd->strdup(tables->view_db.str), + thd->strdup(tables->view_name.str), + thd->strdup(field_name)); + /* + during cleunup() this item will be put in list to replace + expression from VIEW + */ + thd->nocheck_register_item_tree_change(it->ref(), item, + thd->mem_root); } - /* - 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); } - /* All fields are used */ - table->used_fields=table->fields; + /* + All fields are used in case if usual tables (in case of view used + fields marked in setup_tables during fix_fields of view columns + */ + if (table) + table->used_fields= table->s->fields; } } - 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); + if (found) + DBUG_RETURN(0); + + 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); +#ifndef NO_EMBEDDED_ACCESS_CHECKS +err: +#endif + DBUG_RETURN(1); } /* -** Fix all conditions and outer join expressions + Fix all conditions and outer join expressions + + SYNOPSIS + setup_conds() + thd thread handler + tables list of tables for name resolving + leaves list of leaves of join table tree */ -int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) +int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) { table_map not_null_tables= 0; - Item_arena *arena= 0, backup; + SELECT_LEX *select_lex= thd->lex->current_select; + Item_arena *arena= thd->current_arena, backup; + bool save_wrapper= thd->lex->current_select->no_wrap_view_item; + TABLE_LIST *table= NULL; // For HP compilers 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; - thd->lex->current_select->cond_count= 0; + + thd->lex->current_select->no_wrap_view_item= 1; + select_lex->cond_count= 0; if (*conds) { thd->where="where clause"; if (!(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds) || (*conds)->check_cols(1)) - DBUG_RETURN(1); - not_null_tables= (*conds)->not_null_tables(); + goto err_no_arena; } - /* Check if we are using outer joins */ - for (TABLE_LIST *table=tables ; table ; table=table->next) + for (table= leaves; table; table= table->next_leaf) { - if (table->on_expr) + TABLE_LIST *embedded; + TABLE_LIST *embedding= table; + do { - /* 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))) + embedded= embedding; + if (embedded->on_expr) { - 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); + /* Make a join an a expression */ + thd->where="on clause"; + if (!embedded->on_expr->fixed && + embedded->on_expr->fix_fields(thd, tables, &embedded->on_expr) || + embedded->on_expr->check_cols(1)) + goto err_no_arena; + select_lex->cond_count++; } - } - if (table->natural_join) - { - 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 (embedded->natural_join) { - 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))) + /* Make a join of all fields wich have the same name */ + TABLE_LIST *tab1= embedded; + TABLE_LIST *tab2= embedded->natural_join; + if (!(embedded->outer_join & JOIN_TYPE_RIGHT)) { - 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); + while (tab1->nested_join) + { + TABLE_LIST *next; + List_iterator_fast<TABLE_LIST> it(tab1->nested_join->join_list); + tab1= it++; + while ((next= it++)) + tab1= next; + } + } + else + { + while (tab1->nested_join) + tab1= tab1->nested_join->join_list.head(); + } + if (embedded->outer_join & JOIN_TYPE_RIGHT) + { + while (tab2->nested_join) + { + TABLE_LIST *next; + List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list); + tab2= it++; + while ((next= it++)) + tab2= next; + } + } + else + { + while (tab2->nested_join) + tab2= tab2->nested_join->join_list.head(); } - } - thd->lex->current_select->cond_count+= cond_and->list.elements; - // to prevent natural join processing during PS re-execution - table->natural_join= 0; + if (arena) + arena= thd->change_arena_if_needed(&backup); + + TABLE *t1=tab1->table; + TABLE *t2=tab2->table; + Field_iterator_table table_iter; + Field_iterator_view view_iter; + Field_iterator *iterator; + Field *t1_field, *t2_field; + Item *item_t2= 0; + Item_cond_and *cond_and= new Item_cond_and(); + + if (!cond_and) // If not out of memory + goto err_no_arena; + cond_and->top_level_item(); + + if (table->field_translation) + { + iterator= &view_iter; + view_iter.set(tab1); + } + else + { + iterator= &table_iter; + table_iter.set(tab1); + } - if (cond_and->list.elements) - { - if (!table->outer_join) // Not left join + for (; !iterator->end_of_fields(); iterator->next()) { - *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) + const char *t1_field_name= iterator->name(); + uint not_used_field_index= NO_CACHED_FIELD_INDEX; + + if ((t2_field= find_field_in_table(thd, tab2, t1_field_name, + t1_field_name, + strlen(t1_field_name), &item_t2, + 0, 0, 0, + ¬_used_field_index, + FALSE))) { - if (!(*conds)->fixed && - (*conds)->fix_fields(thd, tables, conds)) - DBUG_RETURN(1); + if (t2_field != view_ref_found) + { + if (!(item_t2= new Item_field(thd, t2_field))) + goto err; + /* Mark field used for table cache */ + t2_field->query_id= thd->query_id; + t2->used_keys.intersect(t2_field->part_of_key); + } + if ((t1_field= iterator->field())) + { + /* Mark field used for table cache */ + t1_field->query_id= thd->query_id; + t1->used_keys.intersect(t1_field->part_of_key); + } + Item_func_eq *tmp= new Item_func_eq(iterator->item(thd), + item_t2); + if (!tmp) + goto err; + cond_and->list.push_back(tmp); } } - else + select_lex->cond_count+= cond_and->list.elements; + + // to prevent natural join processing during PS re-execution + embedding->natural_join= 0; + + if (cond_and->list.elements) { - 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) + COND *on_expr= cond_and; + on_expr->fix_fields(thd, 0, &on_expr); + if (!embedded->outer_join) // Not left join { - if (!table->on_expr->fixed && - table->on_expr->fix_fields(thd, tables, &table->on_expr)) - DBUG_RETURN(1); + *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)->fix_fields(thd, tables, conds)) + goto err_no_arena; + } + } + else + { + embedded->on_expr= and_conds(embedded->on_expr, cond_and); + // fix_fields() should be made with temporary memory pool + if (arena) + thd->restore_backup_item_arena(arena, &backup); + if (embedded->on_expr && !embedded->on_expr->fixed) + { + if (embedded->on_expr->fix_fields(thd, tables, + &embedded->on_expr)) + goto err_no_arena; + } } } + else if (arena) + thd->restore_backup_item_arena(arena, &backup); } - else if (arena) - { - thd->restore_backup_item_arena(arena, &backup); - arena= 0; // Safety if goto err - } + embedding= embedded->embedding; } + while (embedding && + embedding->nested_join->join_list.head() == embedded); } - if (thd->current_arena->is_stmt_prepare()) + if (!thd->current_arena->is_conventional()) { /* 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 statement. + We do this ON -> WHERE transformation only once per PS/SP statement. */ - thd->lex->current_select->where= *conds; + select_lex->where= *conds; + select_lex->conds_processed_with_permanent_arena= 1; } + thd->lex->current_select->no_wrap_view_item= save_wrapper; DBUG_RETURN(test(thd->net.report_error)); err: if (arena) thd->restore_backup_item_arena(arena, &backup); +err_no_arena: + thd->lex->current_select->no_wrap_view_item= save_wrapper; DBUG_RETURN(1); } @@ -2772,8 +3435,25 @@ err: ** Returns : 1 if some field has wrong type ******************************************************************************/ -int -fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors) + +/* + 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 +*/ + +bool +fill_record(THD * thd, List<Item> &fields, List<Item> &values, + bool ignore_errors) { List_iterator_fast<Item> f(fields),v(values); Item *value; @@ -2788,14 +3468,32 @@ fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors) if (rfield == table->next_number_field) table->auto_increment_field_not_null= TRUE; if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) - DBUG_RETURN(1); + { + my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); + DBUG_RETURN(TRUE); + } } - DBUG_RETURN(0); + DBUG_RETURN(thd->net.report_error); } -int -fill_record(Field **ptr,List<Item> &values, bool ignore_errors) +/* + 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) { List_iterator_fast<Item> v(values); Item *value; @@ -2809,9 +3507,12 @@ fill_record(Field **ptr,List<Item> &values, bool ignore_errors) if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; if ((value->save_in_field(field, 0) < 0) && !ignore_errors) - DBUG_RETURN(1); + { + my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); + DBUG_RETURN(TRUE); + } } - DBUG_RETURN(0); + DBUG_RETURN(thd->net.report_error); } @@ -2869,14 +3570,14 @@ void remove_db_from_cache(const my_string db) for (uint idx=0 ; idx < open_cache.records ; idx++) { TABLE *table=(TABLE*) hash_element(&open_cache,idx); - if (!strcmp(table->table_cache_key,db)) + if (!strcmp(table->s->db, db)) { - table->version=0L; /* Free when thread is ready */ + table->s->version= 0L; /* Free when thread is ready */ if (!table->in_use) relink_unused(table); } } - while (unused_tables && !unused_tables->version) + while (unused_tables && !unused_tables->s->version) VOID(hash_delete(&open_cache,(byte*) unused_tables)); } @@ -2921,7 +3622,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length)) { THD *in_use; - table->version=0L; /* Free when thread is ready */ + table->s->version= 0L; /* Free when thread is ready */ if (!(in_use=table->in_use)) { DBUG_PRINT("info",("Table was not in use")); @@ -2936,7 +3637,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=1; + in_use->killed= THD::KILL_CONNECTION; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -2961,7 +3662,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, else result= result || return_if_owned_by_thd; } - while (unused_tables && !unused_tables->version) + while (unused_tables && !unused_tables->s->version) VOID(hash_delete(&open_cache,(byte*) unused_tables)); DBUG_RETURN(result); } @@ -3002,3 +3703,62 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) } return 0; } + + +/* + open new .frm format table + + SYNOPSIS + open_new_frm() + 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 my_bool +open_new_frm(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 (!strncmp("VIEW", parser->type()->str, parser->type()->length)) + { + 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(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); +} |