diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 1977 |
1 files changed, 1336 insertions, 641 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index dfed0e36b70..b8b96f14205 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -129,12 +129,11 @@ static void check_unused(void) # Pointer to list of names of open tables. */ -OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) +OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, 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)); @@ -151,12 +150,10 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) DBUG_ASSERT(share->table_name != 0); if ((!share->table_name)) // To be removed continue; // Shouldn't happen - if (wild) - { - strxmov(name,share->table_cache_key,".",share->table_name,NullS); - if (wild_compare(name,wild,0)) - continue; - } + if (db && my_strcasecmp(system_charset_info, db, share->db)) + continue; + if (wild && wild_compare(share->table_name,wild,0)) + continue; /* Check if user has SELECT privilege for any column in the table */ table_list.db= (char*) share->db; @@ -404,8 +401,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) upper level) and will leave prelocked mode if needed. */ -void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, - TABLE *stopper) +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; @@ -502,7 +498,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, */ bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt)); if (!thd->active_transaction()) - thd->transaction.xid.null(); + thd->transaction.xid_state.xid.null(); /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */ if (!lock_in_use) @@ -512,7 +508,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables)); found_old_table= 0; - while (thd->open_tables != stopper) + while (thd->open_tables) found_old_table|=close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; @@ -569,7 +565,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) else { // Free memory and reset for next loop - table->file->ha_reset(); + table->file->reset(); } table->in_use=0; if (unused_tables) @@ -1047,35 +1043,61 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { // 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) && - !my_strcasecmp(system_charset_info, table->alias, alias) && - table->query_id != thd->query_id && /* skip tables already used */ - !(thd->prelocked_mode && table->query_id)) + !memcmp(table->s->table_cache_key, key, key_length)) { - 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) + if (check_if_used && table->query_id && + table->query_id != thd->query_id) { - best_distance= distance; - best_table= table; - if (best_distance == 0) // Found perfect lock - break; + /* + 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; + } + } } } } @@ -1669,7 +1691,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, { /* Give right error message */ thd->clear_error(); - DBUG_PRINT("error", ("Dicovery of %s/%s failed", db, name)); + DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name)); my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while " "unpacking from engine", @@ -1804,6 +1826,9 @@ err: thd - thread handler start - list of tables in/out 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 @@ -1821,7 +1846,7 @@ err: -1 - error */ -int open_tables(THD *thd, TABLE_LIST **start, uint *counter) +int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { TABLE_LIST *tables; bool refresh; @@ -1900,7 +1925,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) (*counter)++; if (!tables->table && - !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, 0))) + !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags))) { free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); @@ -1919,8 +1944,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) has added its base tables after itself, adjust the boundary pointer accordingly. */ - if (query_tables_last_own && - query_tables_last_own == &(tables->next_global) && + if (query_tables_last_own == &(tables->next_global) && tables->view->query_tables) query_tables_last_own= tables->view->query_tables_last; @@ -2143,7 +2167,8 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("simple_open_n_lock_tables"); uint counter; - if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter)) + if (open_tables(thd, &tables, &counter, 0) || + lock_tables(thd, tables, counter)) DBUG_RETURN(-1); /* purecov: inspected */ DBUG_RETURN(0); } @@ -2170,7 +2195,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) { uint counter; DBUG_ENTER("open_and_lock_tables"); - if (open_tables(thd, &tables, &counter) || + if (open_tables(thd, &tables, &counter, 0) || lock_tables(thd, tables, counter) || mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (thd->fill_derived_tables() && @@ -2187,6 +2212,9 @@ 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 @@ -2197,12 +2225,12 @@ 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) +bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) { uint counter; DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ASSERT(!thd->fill_derived_tables()); - if (open_tables(thd, &tables, &counter) || + if (open_tables(thd, &tables, &counter, flags) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(0); @@ -2466,190 +2494,243 @@ bool rm_temporary_table(enum db_type base, char *path) /***************************************************************************** -** find field in list or tables. if field is unqualifed and unique, -** return unique field +* 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. ******************************************************************************/ -/* Special Field pointers for find_field_in_tables returning */ +/* 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 field in table or view + Find a field by name in a view that uses merge algorithm. SYNOPSIS - find_field_in_table() + find_field_in_view() thd thread handler - table_list table where to find + table_list view to search for 'name' name name of field item_name name of item if it will be created (VIEW) length length of name - ref [in/out] expression substituted in VIEW should be - passed using this reference (return - view_ref_found) - (*ref != NULL) only if *ref contains - the item that we need to replace. - 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) + ref expression substituted in VIEW should be passed + using this reference (return view_ref_found) + check_grants do check columns grants for view? register_tree_change TRUE if ref is not stack variable and we - need register changes in item tree + 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 + # pointer to field - only for schema table fields */ -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) +static Field * +find_field_in_view(THD *thd, TABLE_LIST *table_list, + const char *name, const char *item_name, + uint length, Item **ref, bool check_grants, + 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_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); + DBUG_ASSERT(table_list->schema_table_reformed || + (ref != 0 && table_list->view != 0)); + for (; !field_it.end_of_fields(); field_it.next()) { - Field_iterator_view field_it; - field_it.set(table_list); - 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)) { - if (!my_strcasecmp(system_charset_info, field_it.name(), name)) - { - if (table_list->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*) (field_it.item()))->field); - } + if (table_list->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*) (field_it.item()))->field); #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); + if (check_grants && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + DBUG_RETURN(WRONG_GRANT); #endif - Item *item= field_it.create_item(thd); - 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. - */ - if (*ref && !(*ref)->is_autogenerated_name) - 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); - } + Item *item= field_it.create_item(thd); + 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. + */ + if (*ref && !(*ref)->is_autogenerated_name) + 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); } - 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); + DBUG_RETURN(0); } /* - Find field in table, no side effects, only purpose is to check for field - in table object and get reference to the field if found. + Find field by name in a NATURAL/USING join table reference. SYNOPSIS - find_field_in_table_sef() + 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 + check_grants [in] do check columns grants? + 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 - table table where to find - name Name of field searched for + 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 - 0 field is not found - # pointer to field + NULL if the field was not found + WRONG_GRANT if no access rights to the found field + # Pointer to the found Field */ -Field *find_field_in_table_sef(TABLE *table, const char *name) +static Field * +find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, + uint length, Item **ref, bool check_grants, + bool register_tree_change, + TABLE_LIST **actual_table) { - Field **field_ptr; - if (table->s->name_hash.records) - field_ptr= (Field**)hash_search(&table->s->name_hash,(byte*) name, - strlen(name)); - else + List_iterator_fast<Natural_join_column> + field_it(*(table_ref->join_columns)); + Natural_join_column *nj_col; + Field *found_field; + 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 (!(field_ptr= table->field)) - return (Field *)0; - for (; *field_ptr; ++field_ptr) - if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) - break; + if (!(nj_col= field_it++)) + DBUG_RETURN(NULL); + + if (!my_strcasecmp(system_charset_info, nj_col->name(), name)) + break; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_grants && nj_col->check_grants(thd, name, length)) + DBUG_RETURN(WRONG_GRANT); +#endif + + if (nj_col->view_field) + { + /* + The found field is a view field, we do as in find_field_in_view() + and return a pointer to pointer to the Item of that field. + */ + Item *item= nj_col->create_item(thd); + 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; } - if (field_ptr) - return *field_ptr; else - return (Field *)0; + { + /* 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 in table + Find field by name in a base table or a view with temp table algorithm. SYNOPSIS - find_field_in_real_table() + find_field_in_table() thd thread handler - table_list table where to find + table table where to search for the field 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) + 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 + 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 * +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 && @@ -2662,7 +2743,7 @@ Field *find_field_in_real_table(THD *thd, TABLE *table, else { if (!(field_ptr= table->field)) - return (Field *)0; + DBUG_RETURN((Field *)0); for (; *field_ptr; ++field_ptr) if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) break; @@ -2678,32 +2759,141 @@ Field *find_field_in_real_table(THD *thd, TABLE *table, if (!allow_rowid || my_strcasecmp(system_charset_info, name, "_rowid") || !(field=table->rowid_field)) - return (Field*) 0; + DBUG_RETURN((Field*) 0); } - if (thd->set_query_id) - { - table->file->ha_set_bit_in_rw_set(field->fieldnr, - (bool)(thd->set_query_id-1)); - if (field->query_id != thd->query_id) - { - if (table->get_fields_in_item_tree) - field->flags|= GET_FIXED_FIELDS_FLAG; - field->query_id=thd->query_id; - table->used_fields++; - table->used_keys.intersect(field->part_of_key); - } - else - thd->dupp_field=field; - } else if (table->get_fields_in_item_tree) - field->flags|= GET_FIXED_FIELDS_FLAG; + update_field_dependencies(thd, field, table); + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_grants && check_grant_column(thd, &table->grant, table->s->db, table->s->table_name, name, length)) - return WRONG_GRANT; + field= WRONG_GRANT; #endif - return field; + 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 + item_name [in] name of item if it will be created (VIEW) + table_name [in] optional table name that qualifies the field + db_name [in] optional database name that qualifies the + length [in] field length of name + ref [in/out] if 'name' is resolved to a view field, ref + is set to point to the found view field + check_grants_table [in] do check columns grants for table? + check_grants_view [in] do check columns grants for view? + 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. + + 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, const char *item_name, + const char *table_name, const char *db_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, 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 we are going to search for the field. + This is done differently for NATURAL/USING joins because there we can't + simply compare the qualifying table and database names with the ones of + 'table_list' because each field in such a join may originate from a + different table. + TODO: Ensure that table_name, db_name and tables->db always points to + something ! + */ + if (!table_list->is_natural_join && + 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) + { + if ((fld= find_field_in_view(thd, table_list, name, item_name, length, + ref, check_grants_view, + register_tree_change))) + *actual_table= table_list; + } + else if (table_list->is_natural_join) + { + if (table_name && table_name[0]) + { + /* + Qualified field; Search for it in the tables used by the natural join. + */ + 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, item_name, + table_name, db_name, length, ref, + check_grants_table, + check_grants_view, + allow_rowid, cached_field_index_ptr, + register_tree_change, actual_table))) + DBUG_RETURN(fld); + } + DBUG_RETURN(0); + } + /* + Non-qualified field, search directly in the result columns of the + natural join. + */ + fld= find_field_in_natural_join(thd, table_list, name, length, ref, + /* TIMOUR_TODO: check this with Sanja */ + check_grants_table || check_grants_view, + register_tree_change, actual_table); + } + else + { + if ((fld= find_field_in_table(thd, table_list->table, name, length, + check_grants_table, allow_rowid, + cached_field_index_ptr))) + *actual_table= table_list; +#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)) + fld= WRONG_GRANT; +#endif + } + + DBUG_RETURN(fld); } @@ -2712,21 +2902,23 @@ Field *find_field_in_real_table(THD *thd, TABLE *table, SYNOPSIS find_field_in_tables() - thd Pointer to current thread structure - item Field item that should be found - tables Tables to be searched for item - ref If 'item' is resolved to a view field, ref is set to + 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: + 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 + 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 stack variable and we - need register changes in item tree + register_tree_change TRUE if ref is not a stack variable and we + to need register changes in item tree RETURN VALUES 0 If error: the found field is not unique, or there are @@ -2740,63 +2932,74 @@ Field *find_field_in_real_table(THD *thd, TABLE *table, */ Field * -find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, +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) { 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 assuming that - TABLE_LIST *tables is not changed during query execution (which + 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 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 accept this trade off. */ - 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)); - - } + 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, + test(table_ref->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), - register_tree_change); - } + found= find_field_in_table_ref(thd, table_ref, name, item->name, + NULL, NULL, length, ref, + (table_ref->table && + test(table_ref->table->grant. + want_privilege) && + check_privileges), + (test(table_ref->grant.want_privilege) && + check_privileges), + 1, &(item->cached_field_index), + register_tree_change, + &actual_table); if (found) { if (found == WRONG_GRANT) return (Field*) 0; { SELECT_LEX *current_sel= thd->lex->current_select; - SELECT_LEX *last_select= item->cached_table->select_lex; + SELECT_LEX *last_select= table_ref->select_lex; /* If the field was an outer referencee, mark all selects using this - sub query as dependent of the outer query + sub query as dependent on the outer query */ if (current_sel != last_select) mark_select_range_as_dependent(thd, last_select, current_sel, @@ -2818,117 +3021,89 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, db= name_buff; } - if (table_name && table_name[0]) - { /* Qualified field */ - bool found_table= 0; - 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 || !db[0] || !tables->db || !tables->db[0] || - !strcmp(db,tables->db))) - { - found_table=1; - 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), - register_tree_change); - if (find) - { - 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) - { - 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; - } - } - } - if (found) - return found; - if (!found_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); - } - 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 - return (Field*) not_found_field; - return (Field*) 0; - } - allow_rowid= tables && !tables->next_local; // Only one table - for (; tables ; tables= tables->next_local) - { - Field *field; - if (!tables->table && !tables->ancestor) - { - 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; - } + if (last_table) + last_table= last_table->next_name_resolution_table; - 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), - register_tree_change); - if (field) + for (; cur_table != last_table ; + cur_table= cur_table->next_name_resolution_table) + { + Field *cur_field= find_field_in_table_ref(thd, cur_table, name, item->name, + table_name, db, + length, ref, + (cur_table->table && + test(cur_table->table->grant. + want_privilege) && + check_privileges), + (test(cur_table->grant. + want_privilege) + && check_privileges), + allow_rowid, + &(item->cached_field_index), + register_tree_change, + &actual_table); + if (cur_field) { - if (field == WRONG_GRANT) + if (cur_field == WRONG_GRANT) return (Field*) 0; - item->cached_table= (!tables->cacheable_table || found) ? 0 : tables; + + /* + 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 (found) { - if (!thd->where) // Returns first found - break; if (report_error == REPORT_ALL_ERRORS || report_error == IGNORE_EXCEPT_NON_UNIQUE) - my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); + my_error(ER_NON_UNIQ_ERROR, MYF(0), + table_name ? item->full_name() : name, thd->where); return (Field*) 0; } - found= field; + found= cur_field; } } + if (found) return found; - 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); + + /* + 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); + } else - return (Field*) not_found_field; - return (Field*) 0; + { + 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; } @@ -3132,6 +3307,664 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, 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 (!strncmp(find, curr_str->ptr(), find_length)) + 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; + const char *field_name_1; + Query_arena *arena, backup; + bool add_columns= TRUE; + bool result= TRUE; + + 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->change_arena_if_needed(&backup); + + /* + TABLE_LIST::join_columns could be allocated by the previous call to + store_natural_using_join_columns() for the lower level of nested tables. + */ + if (!table_ref_1->join_columns) + { + if (!(table_ref_1->join_columns= new List<Natural_join_column>)) + goto err; + table_ref_1->is_join_columns_complete= FALSE; + } + if (!table_ref_2->join_columns) + { + if (!(table_ref_2->join_columns= new List<Natural_join_column>)) + goto err; + table_ref_2->is_join_columns_complete= FALSE; + } + + for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) + { + bool is_created_1; + bool found= FALSE; + if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created_1))) + goto err; + field_name_1= nj_col_1->name(); + + /* If nj_col_1 was just created add it to the list of join columns. */ + if (is_created_1) + table_ref_1->join_columns->push_back(nj_col_1); + + /* + 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()) + { + bool is_created_2; + 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(thd, &is_created_2))) + goto err; + cur_field_name_2= cur_nj_col_2->name(); + + /* If nj_col_1 was just created add it to the list of join columns. */ + if (add_columns && is_created_2) + table_ref_2->join_columns->push_back(cur_nj_col_2); + + /* Compare the two columns and check for duplicate common fields. */ + if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) + { + if (found) + { + my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); + goto err; + } + nj_col_2= cur_nj_col_2; + found= TRUE; + } + } + /* Force it_2.set() to use table_ref_2->join_columns. */ + table_ref_2->is_join_columns_complete= TRUE; + add_columns= 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); + } + } + table_ref_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_backup_item_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; + bool is_created; + 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->change_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()) + { + if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created))) + goto err; + /* + The following assert checks that mark_common_columns() was run and + we created the list table_ref_1->join_columns. + */ + DBUG_ASSERT(!is_created); + 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()) + { + if (!(nj_col_2= it_2.get_or_create_column_ref(thd, &is_created))) + goto err; + /* + The following assert checks that mark_common_columns() was run and + we created the list table_ref_2->join_columns. + */ + DBUG_ASSERT(!is_created); + 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_backup_item_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->change_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_backup_item_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 ****************************************************************************/ @@ -3212,10 +4045,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ****************************************************************************/ bool setup_fields(THD *thd, Item **ref_pointer_array, - List<Item> &fields, ulong set_query_id, + 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; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); @@ -3243,6 +4077,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, if (!item->fixed && item->fix_fields(thd, it.ref()) || (item= *(it.ref()))->check_cols(1)) { + thd->set_query_id= save_set_query_id; DBUG_RETURN(TRUE); /* purecov: inspected */ } if (ref) @@ -3250,8 +4085,9 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, 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->set_query_id= save_set_query_id; DBUG_RETURN(test(thd->net.report_error)); } @@ -3272,9 +4108,7 @@ 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->effective_algorithm == VIEW_ALGORITHM_MERGE) - { list= make_leaves_list(list, table->ancestor); - } else { *list= table; @@ -3291,33 +4125,36 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) setup_tables() thd Thread handler context name resolution contest to setup table list there - tables Table list + 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 + 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 NOTE Check also that the 'used keys' and 'ignored keys' exists and set up the - table structure accordingly - Create leaf tables list + 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. RETURN - FALSE ok; In this case *map will includes the choosed index + FALSE ok; In this case *map will includes the chosen index TRUE error */ bool setup_tables(THD *thd, Name_resolution_context *context, - TABLE_LIST *tables, Item **conds, - TABLE_LIST **leaves, bool select_insert) + List<TABLE_LIST> *from_clause, TABLE_LIST *tables, + Item **conds, TABLE_LIST **leaves, bool select_insert) { uint tablenr= 0; DBUG_ENTER("setup_tables"); - context->table_list= tables; + context->table_list= context->first_name_resolution_table= tables; /* this is used for INSERT ... SELECT. @@ -3389,6 +4226,11 @@ bool setup_tables(THD *thd, Name_resolution_context *context, 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); } @@ -3447,8 +4289,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, for all columns 1 If any privilege is ok RETURN - 0 ok - 'it' is updated to point at last inserted + 0 ok 'it' is updated to point at last inserted 1 error. Error message is generated but not sent to client */ @@ -3457,12 +4298,11 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, const char *table_name, List_iterator<Item> *it, bool any_privileges) { - /* allocate variables on stack to avoid pool alloaction */ - Field_iterator_table table_iter; - Field_iterator_view view_iter; - uint found; + Field_iterator_table_ref field_iterator; + bool found; char name_buff[NAME_LEN+1]; DBUG_ENTER("insert_fields"); + DBUG_PRINT("arena", ("current arena: 0x%lx", (ulong)thd->current_arena)); if (db_name && lower_case_table_names) { @@ -3476,200 +4316,192 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, db_name= name_buff; } - found= 0; - for (TABLE_LIST *tables= context->table_list; + 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= tables->next_local) + tables= (table_name ? tables->next_local : + tables->next_name_resolution_table) + ) { - Field_iterator *iterator; - TABLE_LIST *natural_join_table; Field *field; - TABLE_LIST *embedded; - TABLE_LIST *last; - TABLE_LIST *embedding; TABLE *table= tables->table; - if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, - tables->alias) && - (!db_name || !strcmp(tables->db,db_name)))) - { - bool view; + 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 right to all columns */ - 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; - } - } + /* 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 - natural_join_table= 0; - last= embedded= tables; - 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; - } - 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) + /* + 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()) + { + Item *item; + + if (!(item= field_iterator.create_item(thd))) + DBUG_RETURN(TRUE); + + if (!found) { - iterator= &view_iter; - view= 1; + found= TRUE; + it->replace(item); /* Replace '*' with the first found item. */ } else - { - iterator= &table_iter; - view= 0; - } - iterator->set(tables); - - /* for view used tables will be collected in following loop */ - if (table) - thd->used_tables|= table->map; + it->after(item); /* Add 'item' to the SELECT list. */ - for (; !iterator->end_of_fields(); iterator->next()) +#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) { - 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_name, - field_name, - strlen(field_name), ¬_used_item, 0, 0, 0, - ¬_used_field_index, TRUE)) + 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))) { - Item *item= iterator->create_item(thd); - if (!item) - goto err; - thd->used_tables|= item->used_tables(); - 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 + my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY", + thd->priv_user, thd->host_or_ip, + fld->field_name, field_table_name); + DBUG_RETURN(TRUE); } - if ((field= iterator->field())) + } +#endif + + 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); + + if (tables->is_natural_join) { + bool is_created; + TABLE *field_table; /* - Mark if field used before in this select. - Used by 'insert' to verify if a field name is used twice + 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. */ - 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 - { - Item *item= ((Field_iterator_view *) iterator)->item(); - item->walk(&Item::reset_query_id_processor, - (byte *)(&thd->query_id)); + Natural_join_column *nj_col; + if (!(nj_col= field_iterator.get_or_create_column_ref(thd, + &is_created))) + DBUG_RETURN(TRUE); + DBUG_ASSERT(nj_col->table_field && !is_created); + 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++; + } } } - /* - 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) + else { - table->used_fields= table->s->fields; - table->file->ha_set_all_bits_in_read_set(); + thd->used_tables|= item->used_tables(); + item->walk(&Item::reset_query_id_processor, + (byte *)(&thd->query_id)); } } + /* + 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(0); + 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); -err: - DBUG_RETURN(1); + + DBUG_RETURN(TRUE); } /* - Fix all conditions and outer join expressions + Fix all conditions and outer join expressions. SYNOPSIS setup_conds() thd thread handler - leaves list of leaves of join table tree + 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 */ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, @@ -3695,6 +4527,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, arena= 0; // For easier test thd->set_query_id=1; + select_lex->cond_count= 0; for (table= tables; table; table= table->next_local) { @@ -3702,7 +4535,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, goto err_no_arena; } - select_lex->cond_count= 0; if (*conds) { thd->where="where clause"; @@ -3711,11 +4543,14 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, goto err_no_arena; } - /* Check if we are using outer joins */ + /* + 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) { - TABLE_LIST *embedded; - TABLE_LIST *embedding= table; + TABLE_LIST *embedded; /* The table at the current level of nesting. */ + TABLE_LIST *embedding= table; /* The parent nested table reference. */ do { embedded= embedding; @@ -3729,146 +4564,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, goto err_no_arena; select_lex->cond_count++; } - - if (embedded->natural_join) - { - /* 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)) - { - 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(); - } - - 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); - } - - for (; !iterator->end_of_fields(); iterator->next()) - { - 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 (t2_field != view_ref_found) - { - if (!(item_t2= new Item_field(thd, &select_lex->context, - t2_field))) - goto err; - /* Mark field used for table cache */ - t2_field->query_id= thd->query_id; - t2->file->ha_set_bit_in_read_set(t2_field->fieldnr); - 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->file->ha_set_bit_in_read_set(t1_field->fieldnr); - t1->used_keys.intersect(t1_field->part_of_key); - } - Item_func_eq *tmp= new Item_func_eq(iterator->create_item(thd), - item_t2); - if (!tmp) - goto err; - cond_and->list.push_back(tmp); - } - } - 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) - { - COND *on_expr= cond_and; - if (!on_expr->fixed) - on_expr->fix_fields(thd, &on_expr); - if (!embedded->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)->fix_fields(thd, 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, &embedded->on_expr)) - goto err_no_arena; - } - } - } - else if (arena) - thd->restore_backup_item_arena(arena, &backup); - } embedding= embedded->embedding; } while (embedding && |