mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
MDEV-17493: Partition pruning doesn't work for nested outer joins
Reuse the fix for MDEV-17518 here, too.
This commit is contained in:
parent
03680a9b4f
commit
3b6d903852
4 changed files with 120 additions and 61 deletions
|
@ -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
|
||||||
(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 *,
|
||||||
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()
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
@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,
|
||||||
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,
|
||||||
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 {
|
||||||
/*
|
/*
|
||||||
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 */
|
||||||
|
|
Loading…
Reference in a new issue