Commit 9f60aa27 authored by Sergey Petrunya's avatar Sergey Petrunya

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.
parent 8bedf1ea
...@@ -2264,6 +2264,35 @@ w 5 19:11:10 ...@@ -2264,6 +2264,35 @@ w 5 19:11:10
set optimizer_switch=@save_optimizer_switch; set optimizer_switch=@save_optimizer_switch;
set join_cache_level=default; set join_cache_level=default;
DROP TABLE t1,t2,t3; 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 # End
set join_cache_level=default; set join_cache_level=default;
show variables like 'join_cache_level'; show variables like 'join_cache_level';
......
...@@ -88,6 +88,32 @@ set join_cache_level=default; ...@@ -88,6 +88,32 @@ set join_cache_level=default;
DROP TABLE t1,t2,t3; 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 --echo # End
set join_cache_level=default; set join_cache_level=default;
......
...@@ -5023,7 +5023,8 @@ bool Item_field::set_no_const_sub(uchar *arg) ...@@ -5023,7 +5023,8 @@ bool Item_field::set_no_const_sub(uchar *arg)
Item *Item_field::replace_equal_field(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(); Item *const_item= item_equal->get_const();
if (const_item) if (const_item)
...@@ -5033,7 +5034,8 @@ Item *Item_field::replace_equal_field(uchar *arg) ...@@ -5033,7 +5034,8 @@ Item *Item_field::replace_equal_field(uchar *arg)
return this; return this;
return const_item; 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) if (subst)
subst= (Item_field *) (subst->real_item()); subst= (Item_field *) (subst->real_item());
if (subst && !field->eq(subst->field)) if (subst && !field->eq(subst->field))
......
...@@ -440,6 +440,16 @@ typedef enum monotonicity_info ...@@ -440,6 +440,16 @@ typedef enum monotonicity_info
class sp_rcontext; 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 class Settable_routine_parameter
{ {
public: public:
...@@ -1153,6 +1163,7 @@ public: ...@@ -1153,6 +1163,7 @@ public:
virtual Item *equal_fields_propagator(uchar * arg) { return this; } virtual Item *equal_fields_propagator(uchar * arg) { return this; }
virtual bool set_no_const_sub(uchar *arg) { return FALSE; } 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; } virtual Item *replace_equal_field(uchar * arg) { return this; }
/* /*
Check if an expression value has allowed arguments, like DATE/DATETIME Check if an expression value has allowed arguments, like DATE/DATETIME
......
...@@ -5716,7 +5716,7 @@ longlong Item_equal::val_int() ...@@ -5716,7 +5716,7 @@ longlong Item_equal::val_int()
void Item_equal::fix_length_and_dec() 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, eval_item= cmp_item::get_comparator(item->cmp_type(), item,
item->collation.collation); item->collation.collation);
} }
...@@ -5816,7 +5816,7 @@ CHARSET_INFO *Item_equal::compare_collation() ...@@ -5816,7 +5816,7 @@ CHARSET_INFO *Item_equal::compare_collation()
@retval 0 if no field found. @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_equal_fields_iterator it(*this);
Item *item; Item *item;
...@@ -5844,7 +5844,11 @@ Item* Item_equal::get_first(Item *field_item) ...@@ -5844,7 +5844,11 @@ Item* Item_equal::get_first(Item *field_item)
in presense of SJM nests. 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) if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used)
{ {
......
...@@ -1712,7 +1712,7 @@ public: ...@@ -1712,7 +1712,7 @@ public:
/** Add a non-constant item to the multiple equality */ /** Add a non-constant item to the multiple equality */
void add(Item *f) { equal_items.push_back(f); } void add(Item *f) { equal_items.push_back(f); }
bool contains(Field *field); 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 */ /** Get number of field items / references to field items in this object */
uint n_field_items() { return equal_items.elements-test(with_const); } uint n_field_items() { return equal_items.elements-test(with_const); }
void merge(Item_equal *item); void merge(Item_equal *item);
......
...@@ -110,7 +110,8 @@ static COND *build_equal_items(THD *thd, COND *cond, ...@@ -110,7 +110,8 @@ static COND *build_equal_items(THD *thd, COND *cond,
COND_EQUAL *inherited, COND_EQUAL *inherited,
List<TABLE_LIST> *join_list, List<TABLE_LIST> *join_list,
COND_EQUAL **cond_equal_ref); 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, COND_EQUAL *cond_equal,
void *table_join_idx); void *table_join_idx);
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
...@@ -1225,7 +1226,8 @@ JOIN::optimize() ...@@ -1225,7 +1226,8 @@ JOIN::optimize()
*/ */
if (conds) 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(); conds->update_used_tables();
DBUG_EXECUTE("where", DBUG_EXECUTE("where",
print_where(conds, print_where(conds,
...@@ -1242,7 +1244,8 @@ JOIN::optimize() ...@@ -1242,7 +1244,8 @@ JOIN::optimize()
{ {
if (*tab->on_expr_ref) 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, tab->cond_equal,
map2table); map2table);
(*tab->on_expr_ref)->update_used_tables(); (*tab->on_expr_ref)->update_used_tables();
...@@ -1265,7 +1268,7 @@ JOIN::optimize() ...@@ -1265,7 +1268,7 @@ JOIN::optimize()
continue; continue;
COND_EQUAL *equals= tab->first_inner ? tab->first_inner->cond_equal : COND_EQUAL *equals= tab->first_inner ? tab->first_inner->cond_equal :
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(); ref_item->update_used_tables();
if (*ref_item_ptr != ref_item) if (*ref_item_ptr != ref_item)
{ {
...@@ -11446,7 +11449,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, ...@@ -11446,7 +11449,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
else else
{ {
TABLE_LIST *emb_nest; TABLE_LIST *emb_nest;
head= item_equal->get_first(NULL); head= item_equal->get_first(NO_PARTICULAR_TAB, NULL);
it++; it++;
if ((emb_nest= embedding_sjm(head))) if ((emb_nest= embedding_sjm(head)))
{ {
...@@ -11555,6 +11558,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, ...@@ -11555,6 +11558,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
return cond; return cond;
} }
/** /**
Substitute every field reference in a condition by the best equal field Substitute every field reference in a condition by the best equal field
and eliminate all multiple equality predicates. and eliminate all multiple equality predicates.
...@@ -11569,6 +11573,9 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, ...@@ -11569,6 +11573,9 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
After this the function retrieves all other conjuncted After this the function retrieves all other conjuncted
predicates substitute every field reference by the field reference predicates substitute every field reference by the field reference
to the first equal field or equal constant if there are any. 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 condition to process
@param cond_equal multiple equalities to take into consideration @param cond_equal multiple equalities to take into consideration
@param table_join_idx index to tables determining field preference @param table_join_idx index to tables determining field preference
...@@ -11579,11 +11586,37 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, ...@@ -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 new fields in multiple equality item of lower levels. We want
the order in them to comply with the order of upper levels. 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 @return
The transformed condition 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, COND_EQUAL *cond_equal,
void *table_join_idx) void *table_join_idx)
{ {
...@@ -11612,7 +11645,8 @@ static COND* substitute_for_best_equal_field(COND *cond, ...@@ -11612,7 +11645,8 @@ static COND* substitute_for_best_equal_field(COND *cond,
Item *item; Item *item;
while ((item= li++)) 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); table_join_idx);
/* /*
This works OK with PS/SP re-execution as changes are made to 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, ...@@ -11659,7 +11693,8 @@ static COND* substitute_for_best_equal_field(COND *cond,
List_iterator_fast<Item_equal> it(cond_equal->current_level); List_iterator_fast<Item_equal> it(cond_equal->current_level);
while((item_equal= it++)) 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; cond_equal= cond_equal->upper_levels;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment