mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
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.
This commit is contained in:
@@ -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;
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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))
|
||||||
|
11
sql/item.h
11
sql/item.h
@@ -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:
|
|||||||
|
|
||||||
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()
|
|||||||
|
|
||||||
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()
|
|||||||
@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)
|
|||||||
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:
|
|||||||
/** 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,
|
|||||||
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()
|
|||||||
*/
|
*/
|
||||||
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()
|
|||||||
{
|
{
|
||||||
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()
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user