diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2012-01-20 02:11:53 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2012-01-20 02:11:53 +0400 |
commit | 9f60aa27f707dea88e8882c5aaf096dce35a3a72 (patch) | |
tree | b34dbc9872bdb7a2d090d1e144b20536a3fc5f7c | |
parent | 8bedf1ea1c328fabea14a1ce232794d73814b6d7 (diff) | |
download | mariadb-git-9f60aa27f707dea88e8882c5aaf096dce35a3a72.tar.gz |
BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on
- equality substitution code was geared towards processing WHERE/ON clauses.
that is, it assumed that it was doing substitions on the code that
= wasn't attached to any particular join_tab yet
= was going to be fed to make_join_select() which would take the condition
apart and attach various parts of it to tables inside/outside semi-joins.
- However, somebody added equality substition for ref access. That is, if
we have a ref access on TBL.key=expr, they would do equality substition in
'expr'. This possibility wasn't accounted for.
- Fixed equality substition code by adding a mode that does equality
substition under assumption that the processed expression will be
attached to a certain particular table TBL.
-rw-r--r-- | mysql-test/r/subselect_sj_jcl6.result | 29 | ||||
-rw-r--r-- | mysql-test/t/subselect_sj_jcl6.test | 26 | ||||
-rw-r--r-- | sql/item.cc | 6 | ||||
-rw-r--r-- | sql/item.h | 11 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 10 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 51 |
7 files changed, 121 insertions, 14 deletions
diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 21a8ea39457..faa4140d375 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -2264,6 +2264,35 @@ w 5 19:11:10 set optimizer_switch=@save_optimizer_switch; set join_cache_level=default; DROP TABLE t1,t2,t3; +# +# BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on +# +set @os_912513= @@optimizer_switch; +set @jcl_912513= @@join_cache_level; +SET optimizer_switch = 'semijoin=on,materialization=on,join_cache_hashed=on'; +SET join_cache_level = 3; +CREATE TABLE t1 ( a INT, b INT, KEY(a) ); +INSERT INTO t1 VALUES +(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7); +CREATE TABLE t2 ( c INT ); +INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7); +SELECT alias1.* FROM +t1 AS alias1 INNER JOIN t1 AS alias2 +ON alias2.a = alias1.b +WHERE alias1.b IN ( +SELECT a FROM t1, t2 +); +a b +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +DROP table t1, t2; +set @@optimizer_switch= @os_912513; +set @@join_cache_level= @jcl_912513; # End set join_cache_level=default; show variables like 'join_cache_level'; diff --git a/mysql-test/t/subselect_sj_jcl6.test b/mysql-test/t/subselect_sj_jcl6.test index 4eeaa465b11..6d563cab3d3 100644 --- a/mysql-test/t/subselect_sj_jcl6.test +++ b/mysql-test/t/subselect_sj_jcl6.test @@ -88,6 +88,32 @@ set join_cache_level=default; DROP TABLE t1,t2,t3; +--echo # +--echo # BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on +--echo # +set @os_912513= @@optimizer_switch; +set @jcl_912513= @@join_cache_level; +SET optimizer_switch = 'semijoin=on,materialization=on,join_cache_hashed=on'; +SET join_cache_level = 3; + +CREATE TABLE t1 ( a INT, b INT, KEY(a) ); +INSERT INTO t1 VALUES + (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7); + +CREATE TABLE t2 ( c INT ); +INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7); + +SELECT alias1.* FROM + t1 AS alias1 INNER JOIN t1 AS alias2 + ON alias2.a = alias1.b +WHERE alias1.b IN ( + SELECT a FROM t1, t2 + ); + +DROP table t1, t2; +set @@optimizer_switch= @os_912513; +set @@join_cache_level= @jcl_912513; + --echo # End set join_cache_level=default; diff --git a/sql/item.cc b/sql/item.cc index d0608e392b3..aaf9b0c2a12 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -5023,7 +5023,8 @@ bool Item_field::set_no_const_sub(uchar *arg) Item *Item_field::replace_equal_field(uchar *arg) { - if (item_equal && item_equal == (Item_equal *) arg) + REPLACE_EQUAL_FIELD_ARG* param= (REPLACE_EQUAL_FIELD_ARG*)arg; + if (item_equal && item_equal == param->item_equal) { Item *const_item= item_equal->get_const(); if (const_item) @@ -5033,7 +5034,8 @@ Item *Item_field::replace_equal_field(uchar *arg) return this; return const_item; } - Item_field *subst= (Item_field *)(item_equal->get_first(this)); + Item_field *subst= + (Item_field *)(item_equal->get_first(param->context_tab, this)); if (subst) subst= (Item_field *) (subst->real_item()); if (subst && !field->eq(subst->field)) diff --git a/sql/item.h b/sql/item.h index d9aa6f3497e..9891d690798 100644 --- a/sql/item.h +++ b/sql/item.h @@ -440,6 +440,16 @@ typedef enum monotonicity_info class sp_rcontext; +class Item_equal; + +struct st_join_table* const NO_PARTICULAR_TAB= (struct st_join_table*)0x1; + +typedef struct replace_equal_field_arg +{ + Item_equal *item_equal; + struct st_join_table *context_tab; +} REPLACE_EQUAL_FIELD_ARG; + class Settable_routine_parameter { public: @@ -1153,6 +1163,7 @@ public: virtual Item *equal_fields_propagator(uchar * arg) { return this; } virtual bool set_no_const_sub(uchar *arg) { return FALSE; } + /* arg points to REPLACE_EQUAL_FIELD_ARG object */ virtual Item *replace_equal_field(uchar * arg) { return this; } /* Check if an expression value has allowed arguments, like DATE/DATETIME diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5b2c862e9eb..8b8a85ca59b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5716,7 +5716,7 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { - Item *item= get_first(NULL); + Item *item= get_first(NO_PARTICULAR_TAB, NULL); eval_item= cmp_item::get_comparator(item->cmp_type(), item, item->collation.collation); } @@ -5816,7 +5816,7 @@ CHARSET_INFO *Item_equal::compare_collation() @retval 0 if no field found. */ -Item* Item_equal::get_first(Item *field_item) +Item* Item_equal::get_first(JOIN_TAB *context, Item *field_item) { Item_equal_fields_iterator it(*this); Item *item; @@ -5844,7 +5844,11 @@ Item* Item_equal::get_first(Item *field_item) in presense of SJM nests. */ - TABLE_LIST *emb_nest= field->table->pos_in_table_list->embedding; + TABLE_LIST *emb_nest; + if (context != NO_PARTICULAR_TAB) + emb_nest= context->emb_sj_nest; + else + emb_nest= field->table->pos_in_table_list->embedding; if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 81a18bb594e..ad4f889f4df 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1712,7 +1712,7 @@ public: /** Add a non-constant item to the multiple equality */ void add(Item *f) { equal_items.push_back(f); } bool contains(Field *field); - Item* get_first(Item *field); + Item* get_first(struct st_join_table *context, Item *field); /** Get number of field items / references to field items in this object */ uint n_field_items() { return equal_items.elements-test(with_const); } void merge(Item_equal *item); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dc13e1ddcab..4058e72c547 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -110,7 +110,8 @@ static COND *build_equal_items(THD *thd, COND *cond, COND_EQUAL *inherited, List<TABLE_LIST> *join_list, COND_EQUAL **cond_equal_ref); -static COND* substitute_for_best_equal_field(COND *cond, +static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab, + COND *cond, COND_EQUAL *cond_equal, void *table_join_idx); static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, @@ -1225,7 +1226,8 @@ JOIN::optimize() */ if (conds) { - conds= substitute_for_best_equal_field(conds, cond_equal, map2table); + conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds, + cond_equal, map2table); conds->update_used_tables(); DBUG_EXECUTE("where", print_where(conds, @@ -1242,7 +1244,8 @@ JOIN::optimize() { if (*tab->on_expr_ref) { - *tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref, + *tab->on_expr_ref= substitute_for_best_equal_field(NO_PARTICULAR_TAB, + *tab->on_expr_ref, tab->cond_equal, map2table); (*tab->on_expr_ref)->update_used_tables(); @@ -1265,7 +1268,7 @@ JOIN::optimize() continue; COND_EQUAL *equals= tab->first_inner ? tab->first_inner->cond_equal : cond_equal; - ref_item= substitute_for_best_equal_field(ref_item, equals, map2table); + ref_item= substitute_for_best_equal_field(tab, ref_item, equals, map2table); ref_item->update_used_tables(); if (*ref_item_ptr != ref_item) { @@ -11446,7 +11449,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, else { TABLE_LIST *emb_nest; - head= item_equal->get_first(NULL); + head= item_equal->get_first(NO_PARTICULAR_TAB, NULL); it++; if ((emb_nest= embedding_sjm(head))) { @@ -11555,6 +11558,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, return cond; } + /** Substitute every field reference in a condition by the best equal field and eliminate all multiple equality predicates. @@ -11569,6 +11573,9 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, After this the function retrieves all other conjuncted predicates substitute every field reference by the field reference to the first equal field or equal constant if there are any. + + @param context_tab Join tab that 'cond' will be attached to, or + NO_PARTICULAR_TAB. See notes above. @param cond condition to process @param cond_equal multiple equalities to take into consideration @param table_join_idx index to tables determining field preference @@ -11579,11 +11586,37 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, new fields in multiple equality item of lower levels. We want the order in them to comply with the order of upper levels. + context_tab may be used to specify which join tab `cond` will be + attached to. There are two possible cases: + + 1. context_tab != NO_PARTICULAR_TAB + We're doing substitution for an Item which will be evaluated in the + context of a particular item. For example, if the optimizer does a + ref access on "tbl1.key= expr" then + = equality substitution will be perfomed on 'expr' + = it is known in advance that 'expr' will be evaluated when + table t1 is accessed. + Note that in this kind of substution we never have to replace Item_equal + objects. For example, for + + t.key= func(col1=col2 AND col2=const) + + we will not build Item_equal or do equality substution (if we decide to, + this function will need to be fixed to handle it) + + 2. context_tab == NO_PARTICULAR_TAB + We're doing substitution in WHERE/ON condition, which is not yet + attached to any particular join_tab. We will use information about the + chosen join order to make "optimal" substitions, i.e. those that allow + to apply filtering as soon as possible. See eliminate_item_equal() and + Item_equal::get_first() for details. + @return The transformed condition */ -static COND* substitute_for_best_equal_field(COND *cond, +static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab, + COND *cond, COND_EQUAL *cond_equal, void *table_join_idx) { @@ -11612,7 +11645,8 @@ static COND* substitute_for_best_equal_field(COND *cond, Item *item; while ((item= li++)) { - Item *new_item= substitute_for_best_equal_field(item, cond_equal, + Item *new_item= substitute_for_best_equal_field(context_tab, + item, cond_equal, table_join_idx); /* This works OK with PS/SP re-execution as changes are made to @@ -11659,7 +11693,8 @@ static COND* substitute_for_best_equal_field(COND *cond, List_iterator_fast<Item_equal> it(cond_equal->current_level); while((item_equal= it++)) { - cond= cond->transform(&Item::replace_equal_field, (uchar *) item_equal); + REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab}; + cond= cond->transform(&Item::replace_equal_field, (uchar *) &arg); } cond_equal= cond_equal->upper_levels; } |