mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
BUG#43768: Prepared query with nested subqueries core dumps on second execution
Fix two problems: 1. Let optimize_semijoin_nests() reset sj_nest->sjmat_info irrespectively of value of optimizer_flag. We need this in case somebody has turned optimization off between reexecutions of the same statement. 2. Do not pull out constant tables out of semi-join nests. The problem is that pullout operation is not undoable, and if a table is constant because it is 1/0-row table it may cease to be constant on the next execution. Note that tables that are constant because of possible eq_ref(const) access will still be pulled out as they are considered functionally-dependent.
This commit is contained in:
parent
c2924e155e
commit
77e66c6985
5 changed files with 203 additions and 20 deletions
|
@ -1,4 +1,4 @@
|
|||
drop table if exists t0, t1, t2, t10, t11, t12;
|
||||
drop table if exists t0, t1, t2, t3, t4, t10, t11, t12;
|
||||
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);
|
||||
|
@ -871,3 +871,54 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||
DROP TABLE t1, t2, t3;
|
||||
DROP VIEW v2, v3;
|
||||
# End of Bug#49198
|
||||
#
|
||||
# BUG#43768: Prepared query with nested subqueries core dumps on second execution
|
||||
#
|
||||
create table t1 (
|
||||
id int(11) unsigned not null primary key auto_increment,
|
||||
partner_id varchar(35) not null,
|
||||
t1_status_id int(10) unsigned
|
||||
);
|
||||
insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"),
|
||||
("3", "partner3", "10"), ("4", "partner4", "10");
|
||||
create table t2 (
|
||||
id int(11) unsigned not null default '0',
|
||||
t1_line_id int(11) unsigned not null default '0',
|
||||
article_id varchar(20),
|
||||
sequence int(11) not null default '0',
|
||||
primary key (id,t1_line_id)
|
||||
);
|
||||
insert into t2 values ("1", "1", "sup", "0"), ("2", "1", "sup", "1"),
|
||||
("2", "2", "sup", "2"), ("2", "3", "sup", "3"),
|
||||
("2", "4", "imp", "4"), ("3", "1", "sup", "0"),
|
||||
("4", "1", "sup", "0");
|
||||
create table t3 (
|
||||
id int(11) not null default '0',
|
||||
preceeding_id int(11) not null default '0',
|
||||
primary key (id,preceeding_id)
|
||||
);
|
||||
create table t4 (
|
||||
user_id varchar(50) not null,
|
||||
article_id varchar(20) not null,
|
||||
primary key (user_id,article_id)
|
||||
);
|
||||
insert into t4 values("nicke", "imp");
|
||||
prepare stmt from
|
||||
'select t1.partner_id
|
||||
from t1
|
||||
where
|
||||
t1.id in (
|
||||
select pl_inner.id
|
||||
from t2 as pl_inner
|
||||
where pl_inner.article_id in (
|
||||
select t4.article_id from t4
|
||||
where t4.user_id = \'nicke\'
|
||||
)
|
||||
)';
|
||||
execute stmt;
|
||||
partner_id
|
||||
partner2
|
||||
execute stmt;
|
||||
partner_id
|
||||
partner2
|
||||
drop table t1,t2,t3,t4;
|
||||
|
|
|
@ -2,7 +2,7 @@ set join_cache_level=6;
|
|||
show variables like 'join_cache_level';
|
||||
Variable_name Value
|
||||
join_cache_level 6
|
||||
drop table if exists t0, t1, t2, t10, t11, t12;
|
||||
drop table if exists t0, t1, t2, t3, t4, t10, t11, t12;
|
||||
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);
|
||||
|
@ -876,6 +876,57 @@ DROP TABLE t1, t2, t3;
|
|||
DROP VIEW v2, v3;
|
||||
# End of Bug#49198
|
||||
#
|
||||
# BUG#43768: Prepared query with nested subqueries core dumps on second execution
|
||||
#
|
||||
create table t1 (
|
||||
id int(11) unsigned not null primary key auto_increment,
|
||||
partner_id varchar(35) not null,
|
||||
t1_status_id int(10) unsigned
|
||||
);
|
||||
insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"),
|
||||
("3", "partner3", "10"), ("4", "partner4", "10");
|
||||
create table t2 (
|
||||
id int(11) unsigned not null default '0',
|
||||
t1_line_id int(11) unsigned not null default '0',
|
||||
article_id varchar(20),
|
||||
sequence int(11) not null default '0',
|
||||
primary key (id,t1_line_id)
|
||||
);
|
||||
insert into t2 values ("1", "1", "sup", "0"), ("2", "1", "sup", "1"),
|
||||
("2", "2", "sup", "2"), ("2", "3", "sup", "3"),
|
||||
("2", "4", "imp", "4"), ("3", "1", "sup", "0"),
|
||||
("4", "1", "sup", "0");
|
||||
create table t3 (
|
||||
id int(11) not null default '0',
|
||||
preceeding_id int(11) not null default '0',
|
||||
primary key (id,preceeding_id)
|
||||
);
|
||||
create table t4 (
|
||||
user_id varchar(50) not null,
|
||||
article_id varchar(20) not null,
|
||||
primary key (user_id,article_id)
|
||||
);
|
||||
insert into t4 values("nicke", "imp");
|
||||
prepare stmt from
|
||||
'select t1.partner_id
|
||||
from t1
|
||||
where
|
||||
t1.id in (
|
||||
select pl_inner.id
|
||||
from t2 as pl_inner
|
||||
where pl_inner.article_id in (
|
||||
select t4.article_id from t4
|
||||
where t4.user_id = \'nicke\'
|
||||
)
|
||||
)';
|
||||
execute stmt;
|
||||
partner_id
|
||||
partner2
|
||||
execute stmt;
|
||||
partner_id
|
||||
partner2
|
||||
drop table t1,t2,t3,t4;
|
||||
#
|
||||
# BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off
|
||||
#
|
||||
CREATE TABLE t0 (a INT);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# Nested Loops semi-join subquery evaluation tests
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t0, t1, t2, t10, t11, t12;
|
||||
drop table if exists t0, t1, t2, t3, t4, t10, t11, t12;
|
||||
--enable_warnings
|
||||
|
||||
#
|
||||
|
@ -770,3 +770,60 @@ DROP TABLE t1, t2, t3;
|
|||
DROP VIEW v2, v3;
|
||||
|
||||
--echo # End of Bug#49198
|
||||
|
||||
--echo #
|
||||
--echo # BUG#43768: Prepared query with nested subqueries core dumps on second execution
|
||||
--echo #
|
||||
create table t1 (
|
||||
id int(11) unsigned not null primary key auto_increment,
|
||||
partner_id varchar(35) not null,
|
||||
t1_status_id int(10) unsigned
|
||||
);
|
||||
|
||||
insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"),
|
||||
("3", "partner3", "10"), ("4", "partner4", "10");
|
||||
|
||||
create table t2 (
|
||||
id int(11) unsigned not null default '0',
|
||||
t1_line_id int(11) unsigned not null default '0',
|
||||
article_id varchar(20),
|
||||
sequence int(11) not null default '0',
|
||||
primary key (id,t1_line_id)
|
||||
);
|
||||
|
||||
insert into t2 values ("1", "1", "sup", "0"), ("2", "1", "sup", "1"),
|
||||
("2", "2", "sup", "2"), ("2", "3", "sup", "3"),
|
||||
("2", "4", "imp", "4"), ("3", "1", "sup", "0"),
|
||||
("4", "1", "sup", "0");
|
||||
create table t3 (
|
||||
id int(11) not null default '0',
|
||||
preceeding_id int(11) not null default '0',
|
||||
primary key (id,preceeding_id)
|
||||
);
|
||||
|
||||
create table t4 (
|
||||
user_id varchar(50) not null,
|
||||
article_id varchar(20) not null,
|
||||
primary key (user_id,article_id)
|
||||
);
|
||||
|
||||
insert into t4 values("nicke", "imp");
|
||||
prepare stmt from
|
||||
'select t1.partner_id
|
||||
from t1
|
||||
where
|
||||
t1.id in (
|
||||
select pl_inner.id
|
||||
from t2 as pl_inner
|
||||
where pl_inner.article_id in (
|
||||
select t4.article_id from t4
|
||||
where t4.user_id = \'nicke\'
|
||||
)
|
||||
)';
|
||||
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
drop table t1,t2,t3,t4;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -963,7 +963,6 @@ int pull_out_semijoin_tables(JOIN *join)
|
|||
{
|
||||
/* Action #1: Mark the constant tables to be pulled out */
|
||||
table_map pulled_tables= 0;
|
||||
|
||||
List_iterator<TABLE_LIST> child_li(sj_nest->nested_join->join_list);
|
||||
TABLE_LIST *tbl;
|
||||
while ((tbl= child_li++))
|
||||
|
@ -971,12 +970,34 @@ int pull_out_semijoin_tables(JOIN *join)
|
|||
if (tbl->table)
|
||||
{
|
||||
tbl->table->reginfo.join_tab->emb_sj_nest= sj_nest;
|
||||
#if 0
|
||||
/*
|
||||
Do not pull out tables because they are constant. This operation has
|
||||
a problem:
|
||||
- Some constant tables may become/cease to be constant across PS
|
||||
re-executions
|
||||
- Contrary to our initial assumption, it turned out that table pullout
|
||||
operation is not easily undoable.
|
||||
|
||||
The solution is to leave constant tables where they are. This will
|
||||
affect only constant tables that are 1-row or empty, tables that are
|
||||
constant because they are accessed via eq_ref(const) access will
|
||||
still be pulled out as functionally-dependent.
|
||||
|
||||
This will cause us to miss the chance to flatten some of the
|
||||
subqueries, but since const tables do not generate many duplicates,
|
||||
it really doesn't matter that much whether they were pulled out or
|
||||
not.
|
||||
|
||||
All of this was done as fix for BUG#43768.
|
||||
*/
|
||||
if (tbl->table->map & join->const_table_map)
|
||||
{
|
||||
pulled_tables |= tbl->table->map;
|
||||
DBUG_PRINT("info", ("Table %s pulled out (reason: constant)",
|
||||
tbl->table->alias));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1048,6 +1069,7 @@ int pull_out_semijoin_tables(JOIN *join)
|
|||
pointers.
|
||||
*/
|
||||
child_li.remove();
|
||||
sj_nest->nested_join->used_tables &= ~tbl->table->map;
|
||||
upper_join_list->push_back(tbl);
|
||||
tbl->join_list= upper_join_list;
|
||||
tbl->embedding= sj_nest->embedding;
|
||||
|
@ -1104,20 +1126,20 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
|
|||
DBUG_ENTER("optimize_semijoin_nests");
|
||||
List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
|
||||
TABLE_LIST *sj_nest;
|
||||
/*
|
||||
The statement may have been executed with 'semijoin=on' earlier.
|
||||
We need to verify that 'semijoin=on' still holds.
|
||||
*/
|
||||
if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_SEMIJOIN) &&
|
||||
optimizer_flag(join->thd, OPTIMIZER_SWITCH_MATERIALIZATION))
|
||||
while ((sj_nest= sj_list_it++))
|
||||
{
|
||||
while ((sj_nest= sj_list_it++))
|
||||
{
|
||||
/* semi-join nests with only constant tables are not valid */
|
||||
DBUG_ASSERT(sj_nest->sj_inner_tables & ~join->const_table_map);
|
||||
/* semi-join nests with only constant tables are not valid */
|
||||
/// DBUG_ASSERT(sj_nest->sj_inner_tables & ~join->const_table_map);
|
||||
|
||||
sj_nest->sj_mat_info= NULL;
|
||||
if (sj_nest->sj_inner_tables && /* not everything was pulled out */
|
||||
sj_nest->sj_mat_info= NULL;
|
||||
/*
|
||||
The statement may have been executed with 'semijoin=on' earlier.
|
||||
We need to verify that 'semijoin=on' still holds.
|
||||
*/
|
||||
if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_SEMIJOIN) &&
|
||||
optimizer_flag(join->thd, OPTIMIZER_SWITCH_MATERIALIZATION))
|
||||
{
|
||||
if ((sj_nest->sj_inner_tables & ~join->const_table_map) && /* not everything was pulled out */
|
||||
!sj_nest->sj_subq_pred->is_correlated &&
|
||||
sj_nest->sj_subq_pred->types_allow_materialization)
|
||||
{
|
||||
|
@ -1128,7 +1150,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
|
|||
The best plan to run the subquery is now in join->best_positions,
|
||||
save it.
|
||||
*/
|
||||
uint n_tables= my_count_bits(sj_nest->sj_inner_tables);
|
||||
uint n_tables= my_count_bits(sj_nest->sj_inner_tables & ~join->const_table_map);
|
||||
SJ_MATERIALIZATION_INFO* sjm;
|
||||
if (!(sjm= new SJ_MATERIALIZATION_INFO) ||
|
||||
!(sjm->positions= (POSITION*)join->thd->alloc(sizeof(POSITION)*
|
||||
|
@ -1443,7 +1465,7 @@ void advance_sj_state(JOIN *join, table_map remaining_tables,
|
|||
new_join_tab->emb_sj_nest->nested_join->sj_corr_tables |
|
||||
new_join_tab->emb_sj_nest->nested_join->sj_depends_on;
|
||||
const table_map sj_inner_tables=
|
||||
new_join_tab->emb_sj_nest->sj_inner_tables;
|
||||
new_join_tab->emb_sj_nest->sj_inner_tables & ~join->const_table_map;
|
||||
|
||||
/*
|
||||
Enter condition:
|
||||
|
|
|
@ -5127,7 +5127,9 @@ greedy_search(JOIN *join,
|
|||
/* number of tables that remain to be optimized */
|
||||
n_tables= size_remain= my_count_bits(remaining_tables &
|
||||
(join->emb_sjm_nest?
|
||||
join->emb_sjm_nest->sj_inner_tables :
|
||||
(join->emb_sjm_nest->sj_inner_tables &
|
||||
~join->const_table_map)
|
||||
:
|
||||
~(table_map)0));
|
||||
|
||||
do {
|
||||
|
@ -5387,7 +5389,7 @@ best_extension_by_limited_search(JOIN *join,
|
|||
|
||||
table_map allowed_tables= ~(table_map)0;
|
||||
if (join->emb_sjm_nest)
|
||||
allowed_tables= join->emb_sjm_nest->sj_inner_tables;
|
||||
allowed_tables= join->emb_sjm_nest->sj_inner_tables & ~join->const_table_map;
|
||||
|
||||
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue