diff options
author | Chaithra Gopalareddy <chaithra.gopalareddy@oracle.com> | 2015-11-20 12:30:15 +0530 |
---|---|---|
committer | Chaithra Gopalareddy <chaithra.gopalareddy@oracle.com> | 2015-11-20 12:30:15 +0530 |
commit | a7fb5aecfd527c6b9274db02dcec69daf06c97a3 (patch) | |
tree | 36b2b9221db5ae52d347512089258aa55bb7da68 /sql | |
parent | f3554bf148710c73df2a1ca5547ea7ff7c21a969 (diff) | |
download | mariadb-git-a7fb5aecfd527c6b9274db02dcec69daf06c97a3.tar.gz |
Bug#19941403: FATAL_SIGNAL(SIG 6) IN BUILD_EQUAL_ITEMS_FOR_COND | IN SQL/SQL_OPTIMIZER.CC:1657
Problem:
At the end of first execution select_lex->prep_where is pointing to
a runtime created object (temporary table field). As a result
server exits trying to access a invalid pointer during second
execution.
Analysis:
While optimizing the join conditions for the query, after the
permanent transformation, optimizer makes a copy of the new
where conditions in select_lex->prep_where. "prep_where" is what
is used as the "where condition" for the query at the start of execution.
W.r.t the query in question, "where" condition is actually pointing
to a field in the temporary table. As a result, for the second
execution the pointer is no more valid resulting in server exit.
Fix:
At the end of the first execution, select_lex->where will have the
original item of the where condition.
Make prep_where the new place where the original item of select->where
has to be rolled back.
Fixed in 5.7 with the wl#7082 - Move permanent transformations from
JOIN::optimize to JOIN::prepare
Patch for 5.5 includes the following backports from 5.6:
Bugfix for Bug12603141 - This makes the first execute statement in the testcase
pass in 5.5
However it was noted later in in Bug16163596 that the above bugfix needed to
be modified. Although Bug16163596 is reproducible only with changes done for
Bug12582849, we have decided include the fix.
Considering that Bug12582849 is related to Bug12603141, the fix is
also included here. However this results in Bug16317817, Bug16317685,
Bug16739050. So fix for the above three bugs is also part of this patch.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 2 | ||||
-rw-r--r-- | sql/item.h | 8 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 5 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 10 | ||||
-rw-r--r-- | sql/sql_class.cc | 15 | ||||
-rw-r--r-- | sql/sql_class.h | 16 | ||||
-rw-r--r-- | sql/sql_lex.cc | 23 | ||||
-rw-r--r-- | sql/sql_select.cc | 19 |
8 files changed, 85 insertions, 13 deletions
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<Item> 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<Item_change_record> 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<TABLE_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; |