Commit 3b6d9038 authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-17493: Partition pruning doesn't work for nested outer joins

Reuse the fix for MDEV-17518 here, too.
parent 03680a9b
...@@ -3484,3 +3484,36 @@ select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) O ...@@ -3484,3 +3484,36 @@ select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) O
a b c d a b c d
1 a b 1 1 a b 1
drop table t1; drop table t1;
#
# MDEV-17493: Partition pruning doesn't work for nested outer joins
#
create table t0(a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int, b int, c int);
insert into t1 select a,a,a from t0;
create table t2 (a int, b int, c int);
insert into t2 select a,a,a from t0;
create table t3 (
part_id int,
a int
) partition by list (part_id) (
partition p0 values in (0),
partition p1 values in (1),
partition p2 values in (2),
partition p3 values in (3),
partition p4 values in (4)
);
insert into t3 select mod(a,5), a from t0;
explain partitions
select * from t1 left join t3 on (t1.a=t3.a and t3.part_id=3);
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 10
1 SIMPLE t3 p3 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
# The following should have partitions="p3", NOT p0,p1,p2,p3,p4:
explain partitions
select * from t1 left join (t3 join t2) on (t1.a=t3.a and t3.a=t2.b and t3.part_id=3);
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 10
1 SIMPLE t3 p3 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
1 SIMPLE t2 NULL ALL NULL NULL NULL NULL 10 Using where; Using join buffer (incremental, BNL join)
drop table t0,t1,t2,t3;
...@@ -1535,4 +1535,35 @@ select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) O ...@@ -1535,4 +1535,35 @@ select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) O
(a = 1 AND b >= 'a' AND (c = 'c' OR (c = 'd' AND d = 2)))); (a = 1 AND b >= 'a' AND (c = 'c' OR (c = 'd' AND d = 2))));
drop table t1; drop table t1;
--echo #
--echo # MDEV-17493: Partition pruning doesn't work for nested outer joins
--echo #
create table t0(a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int, b int, c int);
insert into t1 select a,a,a from t0;
create table t2 (a int, b int, c int);
insert into t2 select a,a,a from t0;
create table t3 (
part_id int,
a int
) partition by list (part_id) (
partition p0 values in (0),
partition p1 values in (1),
partition p2 values in (2),
partition p3 values in (3),
partition p4 values in (4)
);
insert into t3 select mod(a,5), a from t0;
explain partitions
select * from t1 left join t3 on (t1.a=t3.a and t3.part_id=3);
--echo # The following should have partitions="p3", NOT p0,p1,p2,p3,p4:
explain partitions
select * from t1 left join (t3 join t2) on (t1.a=t3.a and t3.a=t2.b and t3.part_id=3);
drop table t0,t1,t2,t3;
...@@ -292,6 +292,9 @@ static bool find_order_in_list(THD *, Ref_ptr_array, TABLE_LIST *, ORDER *, ...@@ -292,6 +292,9 @@ static bool find_order_in_list(THD *, Ref_ptr_array, TABLE_LIST *, ORDER *,
static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
table_map rem_tables); table_map rem_tables);
void set_postjoin_aggr_write_func(JOIN_TAB *tab); void set_postjoin_aggr_write_func(JOIN_TAB *tab);
static Item **get_sargable_cond(JOIN *join, TABLE *table);
#ifndef DBUG_OFF #ifndef DBUG_OFF
/* /*
...@@ -1770,19 +1773,9 @@ JOIN::optimize_inner() ...@@ -1770,19 +1773,9 @@ JOIN::optimize_inner()
List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
while ((tbl= li++)) while ((tbl= li++))
{ {
/* Item **prune_cond= get_sargable_cond(this, tbl->table);
If tbl->embedding!=NULL that means that this table is in the inner tbl->table->all_partitions_pruned_away=
part of the nested outer join, and we can't do partition pruning prune_partitions(thd, tbl->table, *prune_cond);
(TODO: check if this limitation can be lifted)
*/
if (!tbl->embedding ||
(tbl->embedding && tbl->embedding->sj_on_expr))
{
Item *prune_cond= tbl->on_expr? tbl->on_expr : conds;
tbl->table->all_partitions_pruned_away= prune_partitions(thd,
tbl->table,
prune_cond);
}
} }
} }
#endif #endif
...@@ -4334,6 +4327,47 @@ void mark_join_nest_as_const(JOIN *join, ...@@ -4334,6 +4327,47 @@ void mark_join_nest_as_const(JOIN *join,
} }
} }
/*
@brief Get the condition that can be used to do range analysis/partition
pruning/etc
@detail
Figure out which condition we can use:
- For INNER JOIN, we use the WHERE,
- "t1 LEFT JOIN t2 ON ..." uses t2's ON expression
- "t1 LEFT JOIN (...) ON ..." uses the join nest's ON expression.
*/
static Item **get_sargable_cond(JOIN *join, TABLE *table)
{
Item **retval;
if (table->pos_in_table_list->on_expr)
{
/*
This is an inner table from a single-table LEFT JOIN, "t1 LEFT JOIN
t2 ON cond". Use the condition cond.
*/
retval= &table->pos_in_table_list->on_expr;
}
else if (table->pos_in_table_list->embedding &&
!table->pos_in_table_list->embedding->sj_on_expr)
{
/*
This is the inner side of a multi-table outer join. Use the
appropriate ON expression.
*/
retval= &(table->pos_in_table_list->embedding->on_expr);
}
else
{
/* The table is not inner wrt some LEFT JOIN. Use the WHERE clause */
retval= &join->conds;
}
return retval;
}
/** /**
Calculate the best possible join and initialize the join structure. Calculate the best possible join and initialize the join structure.
...@@ -4919,42 +4953,11 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, ...@@ -4919,42 +4953,11 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
SQL_SELECT *select= 0; SQL_SELECT *select= 0;
if (!s->const_keys.is_clear_all()) if (!s->const_keys.is_clear_all())
{ {
Item *sargable_cond; Item **sargable_cond= get_sargable_cond(join, s->table);
int cond_source;
/*
Figure out which condition we should use for range analysis. For
INNER JOIN, we use the WHERE, for inner side of LEFT JOIN we should
use the ON expression.
*/
if (*s->on_expr_ref)
{
/*
This is an inner table from a single-table LEFT JOIN, "t1 LEFT JOIN
t2 ON cond". Use the condition cond.
*/
cond_source= 0;
sargable_cond= *s->on_expr_ref;
}
else if (s->table->pos_in_table_list->embedding &&
!s->table->pos_in_table_list->embedding->sj_on_expr)
{
/*
This is the inner side of a multi-table outer join. Use the
appropriate ON expression.
*/
cond_source= 1;
sargable_cond= s->table->pos_in_table_list->embedding->on_expr;
}
else
{
/* The table is not inner wrt some LEFT JOIN. Use the WHERE clause */
cond_source= 2;
sargable_cond= join->conds;
}
select= make_select(s->table, found_const_table_map, select= make_select(s->table, found_const_table_map,
found_const_table_map, found_const_table_map,
sargable_cond, *sargable_cond,
(SORT_INFO*) 0, (SORT_INFO*) 0,
1, &error); 1, &error);
if (!select) if (!select)
...@@ -4966,19 +4969,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, ...@@ -4966,19 +4969,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
Range analyzer might have modified the condition. Put it the new Range analyzer might have modified the condition. Put it the new
condition to where we got it from. condition to where we got it from.
*/ */
switch (cond_source) { *sargable_cond= select->cond;
case 0:
*s->on_expr_ref= select->cond;
break;
case 1:
s->table->pos_in_table_list->embedding->on_expr= select->cond;
break;
case 2:
join->conds= select->cond;
break;
default:
DBUG_ASSERT(0);
}
s->quick=select->quick; s->quick=select->quick;
s->needed_reg=select->needed_reg; s->needed_reg=select->needed_reg;
......
...@@ -263,8 +263,12 @@ typedef struct st_join_table { ...@@ -263,8 +263,12 @@ typedef struct st_join_table {
/* /*
Pointer to the associated ON expression. on_expr_ref=!NULL except for Pointer to the associated ON expression. on_expr_ref=!NULL except for
degenerate joins. degenerate joins.
*on_expr_ref!=NULL for tables that are first inner tables within an outer
join. Optimization phase: *on_expr_ref!=NULL for tables that are the single
tables on the inner side of the outer join (t1 LEFT JOIN t2 ON...)
Execution phase: *on_expr_ref!=NULL for tables that are first inner tables
within an outer join (which may have multiple tables)
*/ */
Item **on_expr_ref; Item **on_expr_ref;
COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */ COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */
......
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