diff --git a/sql/item.cc b/sql/item.cc index fd590574e56..beb68c5d321 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4684,7 +4684,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) It's not an Item_field in the select list so we must make a new Item_ref to point to the Item in the select list and replace the Item_field created by the parser with the new Item_ref. - + Ex: SELECT func1(col) as c ... ORDER BY func2(c); NOTE: If we are fixing an alias reference inside ORDER/GROUP BY item tree, then we use new Item_ref as an intermediate value to resolve referenced item only. diff --git a/sql/item.h b/sql/item.h index 629f9afa241..831343de7ad 100644 --- a/sql/item.h +++ b/sql/item.h @@ -960,7 +960,13 @@ public: */ virtual void no_rows_in_result() {} virtual Item *copy_or_same(THD *thd) { return this; } - virtual Item *copy_andor_structure(THD *thd) { return this; } + /** + @param real_items True <=> in the copy, replace any Item_ref with its + real_item() + @todo this argument should be always false and removed in WL#7082. + */ + virtual Item *copy_andor_structure(THD *thd, bool real_items= false) + { return real_items ? real_item() : this; } virtual Item *real_item() { return this; } virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e6cc3272ceb..b302440272e 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4303,11 +4303,12 @@ Item_cond::Item_cond(THD *thd, Item_cond *item) } -void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item) +void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item, bool real_items) { List_iterator_fast li(item->list); while (Item *it= li++) - list.push_back(it->copy_andor_structure(thd)); + list.push_back((real_items ? it->real_item() : it)-> + copy_andor_structure(thd, real_items)); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 115d6db300d..00c7bccbfb8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1514,7 +1514,7 @@ public: friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds); void top_level_item() { abort_on_null=1; } - void copy_andor_arguments(THD *thd, Item_cond *item); + void copy_andor_arguments(THD *thd, Item_cond *item, bool real_items= false); bool walk(Item_processor processor, bool walk_subquery, uchar *arg); Item *transform(Item_transformer transformer, uchar *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); @@ -1689,11 +1689,11 @@ public: const char *func_name() const { return "and"; } table_map not_null_tables() const { return abort_on_null ? not_null_tables_cache: and_tables_cache; } - Item* copy_andor_structure(THD *thd) + Item* copy_andor_structure(THD *thd, bool real_items) { Item_cond_and *item; if ((item= new Item_cond_and(thd, this))) - item->copy_andor_arguments(thd, this); + item->copy_andor_arguments(thd, this, real_items); return item; } Item *neg_transformer(THD *thd); @@ -1719,11 +1719,11 @@ public: longlong val_int(); const char *func_name() const { return "or"; } table_map not_null_tables() const { return and_tables_cache; } - Item* copy_andor_structure(THD *thd) + Item* copy_andor_structure(THD *thd, bool real_items) { Item_cond_or *item; if ((item= new Item_cond_or(thd, this))) - item->copy_andor_arguments(thd, this); + item->copy_andor_arguments(thd, this, real_items); return item; } Item *neg_transformer(THD *thd); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 05bb38d8358..629088dd862 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2064,6 +2064,21 @@ struct Item_change_record: public ilink static void operator delete(void *ptr, void *mem) { /* never called */ } }; +void THD::change_item_tree_place(Item **old_ref, Item **new_ref) +{ + I_List_iterator it(change_list); + Item_change_record *change; + while ((change= it++)) + { + if (change->place == old_ref) + { + DBUG_PRINT("info", ("change_item_tree_place old_ref %p new_ref %p", + old_ref, new_ref)); + change->place= new_ref; + break; + } + } +} /* Register an item tree tree transformation, performed by the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 5a5e8b48754..c9900231615 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2515,6 +2515,22 @@ public: nocheck_register_item_tree_change(place, *place, mem_root); *place= new_value; } + + /* + Find and update change record of an underlying item. + + @param old_ref The old place of moved expression. @param new_ref The + new place of moved expression. @details During permanent + transformations, e.g. join flattening in simplify_joins, a condition + could be moved from one place to another, e.g. from on_expr to WHERE + condition. If the moved condition has replaced some other with + change_item_tree() function, the change record will restore old value to + the wrong place during rollback_item_tree_changes. This function goes + through the list of change records, and replaces + Item_change_record::place. + */ + void change_item_tree_place(Item **old_ref, Item **new_ref); + void nocheck_register_item_tree_change(Item **place, Item *old_value, MEM_ROOT *runtime_memroot); void rollback_item_tree_changes(); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 17446778034..50cdabe341a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3158,7 +3158,26 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, } if (*conds) { - prep_where= *conds; + /* + In "WHERE outer_field", *conds may be an Item_outer_ref allocated in + the execution memroot. + @todo change this line in WL#7082. Currently, when we execute a SP, + containing "SELECT (SELECT ... WHERE t1.col) FROM t1", + resolution may make *conds equal to an Item_outer_ref, then below + *conds becomes Item_field, which then goes straight on to execution, + undoing the effects of putting Item_outer_ref in the first place... + With a PS the problem is not as severe, as after the code below we + don't go to execution: a next execution will do a new name resolution + which will create Item_outer_ref again. + + To reviewers: in WL#7082, + prep_where= (*conds)->real_item(); + becomes: + prep_where= *conds; + thd->change_item_tree_place(conds, &prep_where); + and same for HAVING. + */ + prep_where= (*conds)->real_item(); *conds= where= prep_where->copy_andor_structure(thd); } if (*having_conds) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 96f60d46651..96271f26b0f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -915,7 +915,22 @@ JOIN::optimize() conds= simplify_joins(this, join_list, conds, TRUE); build_bitmap_for_nested_joins(join_list, 0); - sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; + /* + After permanent transformations above, prep_where created in + st_select_lex::fix_prepare_information() is out-of-date, we need to + refresh it. + For that We must copy "conds" because it contains AND/OR items in a + non-permanent memroot. And this copy must contain real items only, + because the new AND/OR items will not have their argument pointers + restored by rollback_item_tree_changes(). + @see st_select_lex::fix_prepare_information() for problems with this. + @todo in WL#7082 move transformations above to before + st_select_lex::fix_prepare_information(), and remove this second copy + below. + */ + sel->prep_where= conds ? conds->copy_andor_structure(thd, true) : NULL; + if (conds) + thd->change_item_tree_place(&conds, &select_lex->prep_where); if (arena) thd->restore_active_arena(arena, &backup); @@ -9061,7 +9076,7 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top) DBUG_ASSERT(expr); table->on_expr= expr; - table->prep_on_expr= expr->copy_andor_structure(join->thd); + table->prep_on_expr= expr->copy_andor_structure(join->thd, true); } } nested_join->used_tables= (table_map) 0;