mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off
- The problem was that DuplicateWeedout strategy setup code wasn't aware of the fact that join buffering will be used and applied optimization that doesn't work together with join buffering. Fixed by making DuplicateWeedout setup code to have a pessimistic check about whether there is a chance that join buffering will be used. - Make JOIN_CACHE_BKA::init() correctly process Copy_field elements that denote saving current rowids in the join buffer. mysql-test/r/subselect_sj2.result: Update test results mysql-test/r/subselect_sj2_jcl6.result: Update test results mysql-test/r/subselect_sj_jcl6.result: Testcase mysql-test/t/subselect_sj2.test: Update test results mysql-test/t/subselect_sj_jcl6.test: Testcase sql/opt_subselect.cc: - The problem was that DuplicateWeedout strategy setup code wasn't aware of the fact that join buffering will be used and applied optimization that doesn't work together with join buffering. Fixed by making DuplicateWeedout setup code to have a pessimistic check about whether there is a chance that join buffering will be used. sql/sql_join_cache.cc: Make JOIN_CACHE_BKA::init() correctly process Copy_field elements that denote saving current rowids in the join buffer. sql/sql_select.cc: Added a question note
This commit is contained in:
@ -264,8 +264,8 @@ explain select *
|
|||||||
from t0 where a in
|
from t0 where a in
|
||||||
(select t2.a+t3.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a);
|
(select t2.a+t3.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a);
|
||||||
id select_type table type possible_keys key key_len ref rows Extra
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
1 PRIMARY t0 ALL NULL NULL NULL NULL 10
|
1 PRIMARY t0 ALL NULL NULL NULL NULL 10 Start temporary
|
||||||
1 PRIMARY t1 index NULL a 5 NULL 10 Using index; Start temporary; Using join buffer
|
1 PRIMARY t1 index NULL a 5 NULL 10 Using index; Using join buffer
|
||||||
1 PRIMARY t2 ref a a 5 test.t1.a 1 Using index
|
1 PRIMARY t2 ref a a 5 test.t1.a 1 Using index
|
||||||
1 PRIMARY t3 ref a a 5 test.t1.a 1 Using where; Using index; End temporary
|
1 PRIMARY t3 ref a a 5 test.t1.a 1 Using where; Using index; End temporary
|
||||||
drop table t0, t1,t2,t3;
|
drop table t0, t1,t2,t3;
|
||||||
|
@ -268,8 +268,8 @@ explain select *
|
|||||||
from t0 where a in
|
from t0 where a in
|
||||||
(select t2.a+t3.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a);
|
(select t2.a+t3.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a);
|
||||||
id select_type table type possible_keys key key_len ref rows Extra
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
1 PRIMARY t0 ALL NULL NULL NULL NULL 10
|
1 PRIMARY t0 ALL NULL NULL NULL NULL 10 Start temporary
|
||||||
1 PRIMARY t1 index NULL a 5 NULL 10 Using index; Start temporary; Using join buffer
|
1 PRIMARY t1 index NULL a 5 NULL 10 Using index; Using join buffer
|
||||||
1 PRIMARY t2 ref a a 5 test.t1.a 1 Using index
|
1 PRIMARY t2 ref a a 5 test.t1.a 1 Using index
|
||||||
1 PRIMARY t3 ref a a 5 test.t1.a 1 Using where; Using index; End temporary
|
1 PRIMARY t3 ref a a 5 test.t1.a 1 Using where; Using index; End temporary
|
||||||
drop table t0, t1,t2,t3;
|
drop table t0, t1,t2,t3;
|
||||||
@ -421,20 +421,23 @@ explain extended select * from t0
|
|||||||
where t0.a in ( select t1.a from t1,t2 where t2.a=t0.a and
|
where t0.a in ( select t1.a from t1,t2 where t2.a=t0.a and
|
||||||
t1.b=t2.b);
|
t1.b=t2.b);
|
||||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
1 PRIMARY t0 ALL NULL NULL NULL NULL 5 100.00
|
1 PRIMARY t0 ALL NULL NULL NULL NULL 5 100.00 Start temporary
|
||||||
1 PRIMARY t1 ref a a 5 test.t0.a 1 100.00 Start temporary; Using join buffer
|
1 PRIMARY t1 ref a a 5 test.t0.a 1 100.00 Using join buffer
|
||||||
1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t0.a 1 100.00 Using where; End temporary; Using join buffer
|
1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t0.a 1 100.00 Using where; End temporary; Using join buffer
|
||||||
Warnings:
|
Warnings:
|
||||||
Note 1276 Field or reference 'test.t0.a' of SELECT #2 was resolved in SELECT #1
|
Note 1276 Field or reference 'test.t0.a' of SELECT #2 was resolved in SELECT #1
|
||||||
Note 1003 select `test`.`t0`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) join `test`.`t0` where ((`test`.`t2`.`b` = `test`.`t1`.`b`) and (`test`.`t1`.`a` = `test`.`t0`.`a`) and (`test`.`t2`.`a` = `test`.`t0`.`a`))
|
Note 1003 select `test`.`t0`.`a` AS `a` from `test`.`t2` semi join (`test`.`t1`) join `test`.`t0` where ((`test`.`t2`.`b` = `test`.`t1`.`b`) and (`test`.`t1`.`a` = `test`.`t0`.`a`) and (`test`.`t2`.`a` = `test`.`t0`.`a`))
|
||||||
update t1 set a=3, b=11 where a=4;
|
update t1 set a=3, b=11 where a=4;
|
||||||
update t2 set b=11 where a=3;
|
update t2 set b=11 where a=3;
|
||||||
|
# Not anymore:
|
||||||
# The following query gives wrong result due to Bug#49129
|
# The following query gives wrong result due to Bug#49129
|
||||||
select * from t0 where t0.a in
|
select * from t0 where t0.a in
|
||||||
(select t1.a from t1, t2 where t2.a=t0.a and t1.b=t2.b);
|
(select t1.a from t1, t2 where t2.a=t0.a and t1.b=t2.b);
|
||||||
a
|
a
|
||||||
0
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
drop table t0, t1, t2;
|
drop table t0, t1, t2;
|
||||||
CREATE TABLE t1 (
|
CREATE TABLE t1 (
|
||||||
id int(11) NOT NULL,
|
id int(11) NOT NULL,
|
||||||
|
@ -875,6 +875,37 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||||||
DROP TABLE t1, t2, t3;
|
DROP TABLE t1, t2, t3;
|
||||||
DROP VIEW v2, v3;
|
DROP VIEW v2, v3;
|
||||||
# End of Bug#49198
|
# End of Bug#49198
|
||||||
|
#
|
||||||
|
# BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off
|
||||||
|
#
|
||||||
|
CREATE TABLE t0 (a INT);
|
||||||
|
INSERT INTO t0 VALUES (0),(1),(2),(3),(4);
|
||||||
|
CREATE TABLE t1 (a INT, b INT, KEY(a));
|
||||||
|
INSERT INTO t1 SELECT a, a from t0;
|
||||||
|
CREATE TABLE t2 (a INT, b INT, PRIMARY KEY(a));
|
||||||
|
INSERT INTO t2 SELECT * FROM t1;
|
||||||
|
UPDATE t1 SET a=3, b=11 WHERE a=4;
|
||||||
|
UPDATE t2 SET b=11 WHERE a=3;
|
||||||
|
set @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
set optimizer_switch='firstmatch=off';
|
||||||
|
The following should use a join order of t0,t1,t2, with DuplicateElimination:
|
||||||
|
explain
|
||||||
|
SELECT * FROM t0 WHERE t0.a IN
|
||||||
|
(SELECT t1.a FROM t1, t2 WHERE t2.a=t0.a AND t1.b=t2.b);
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 PRIMARY t0 ALL NULL NULL NULL NULL 5 Start temporary
|
||||||
|
1 PRIMARY t1 ref a a 5 test.t0.a 1 Using join buffer
|
||||||
|
1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t0.a 1 Using where; End temporary; Using join buffer
|
||||||
|
SELECT * FROM t0 WHERE t0.a IN
|
||||||
|
(SELECT t1.a FROM t1, t2 WHERE t2.a=t0.a AND t1.b=t2.b);
|
||||||
|
a
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
set optimizer_switch=@save_optimizer_switch;
|
||||||
|
drop table t0, t1, t2;
|
||||||
|
# End
|
||||||
set join_cache_level=default;
|
set join_cache_level=default;
|
||||||
show variables like 'join_cache_level';
|
show variables like 'join_cache_level';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
|
@ -583,7 +583,7 @@ update t2 set b=11 where a=3;
|
|||||||
|
|
||||||
if (`select @@join_cache_level=6`)
|
if (`select @@join_cache_level=6`)
|
||||||
{
|
{
|
||||||
--echo
|
--echo # Not anymore:
|
||||||
--echo # The following query gives wrong result due to Bug#49129
|
--echo # The following query gives wrong result due to Bug#49129
|
||||||
}
|
}
|
||||||
select * from t0 where t0.a in
|
select * from t0 where t0.a in
|
||||||
|
@ -7,5 +7,33 @@ show variables like 'join_cache_level';
|
|||||||
|
|
||||||
--source t/subselect_sj.test
|
--source t/subselect_sj.test
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t0 (a INT);
|
||||||
|
INSERT INTO t0 VALUES (0),(1),(2),(3),(4);
|
||||||
|
CREATE TABLE t1 (a INT, b INT, KEY(a));
|
||||||
|
INSERT INTO t1 SELECT a, a from t0;
|
||||||
|
CREATE TABLE t2 (a INT, b INT, PRIMARY KEY(a));
|
||||||
|
INSERT INTO t2 SELECT * FROM t1;
|
||||||
|
UPDATE t1 SET a=3, b=11 WHERE a=4;
|
||||||
|
UPDATE t2 SET b=11 WHERE a=3;
|
||||||
|
|
||||||
|
set @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
set optimizer_switch='firstmatch=off';
|
||||||
|
|
||||||
|
--echo The following should use a join order of t0,t1,t2, with DuplicateElimination:
|
||||||
|
explain
|
||||||
|
SELECT * FROM t0 WHERE t0.a IN
|
||||||
|
(SELECT t1.a FROM t1, t2 WHERE t2.a=t0.a AND t1.b=t2.b);
|
||||||
|
|
||||||
|
SELECT * FROM t0 WHERE t0.a IN
|
||||||
|
(SELECT t1.a FROM t1, t2 WHERE t2.a=t0.a AND t1.b=t2.b);
|
||||||
|
|
||||||
|
set optimizer_switch=@save_optimizer_switch;
|
||||||
|
drop table t0, t1, t2;
|
||||||
|
|
||||||
|
--echo # End
|
||||||
|
|
||||||
set join_cache_level=default;
|
set join_cache_level=default;
|
||||||
show variables like 'join_cache_level';
|
show variables like 'join_cache_level';
|
||||||
|
@ -3035,10 +3035,24 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
|
|||||||
forwards, but do not destroy other duplicate elimination methods.
|
forwards, but do not destroy other duplicate elimination methods.
|
||||||
*/
|
*/
|
||||||
uint first_table= i;
|
uint first_table= i;
|
||||||
|
uint join_cache_level= join->thd->variables.join_cache_level;
|
||||||
for (uint j= i; j < i + pos->n_sj_tables; j++)
|
for (uint j= i; j < i + pos->n_sj_tables; j++)
|
||||||
{
|
{
|
||||||
if (join->best_positions[j].use_join_buffer && j <= no_jbuf_after)
|
/*
|
||||||
|
When we'll properly take join buffering into account during
|
||||||
|
join optimization, the below check should be changed to
|
||||||
|
"if (join->best_positions[j].use_join_buffer &&
|
||||||
|
j <= no_jbuf_after)".
|
||||||
|
For now, use a rough criteria:
|
||||||
|
*/
|
||||||
|
JOIN_TAB *js_tab=join->join_tab + j;
|
||||||
|
if (j != join->const_tables && js_tab->use_quick != 2 &&
|
||||||
|
j <= no_jbuf_after &&
|
||||||
|
((js_tab->type == JT_ALL && join_cache_level != 0) ||
|
||||||
|
(join_cache_level > 4 && (tab->type == JT_REF ||
|
||||||
|
tab->type == JT_EQ_REF))))
|
||||||
{
|
{
|
||||||
|
/* Looks like we'll be using join buffer */
|
||||||
first_table= join->const_tables;
|
first_table= join->const_tables;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3116,7 +3130,12 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
|
|||||||
JOIN_TAB *j, *jump_to= tab-1;
|
JOIN_TAB *j, *jump_to= tab-1;
|
||||||
for (j= tab; j != tab + pos->n_sj_tables; j++)
|
for (j= tab; j != tab + pos->n_sj_tables; j++)
|
||||||
{
|
{
|
||||||
if (!tab->emb_sj_nest)
|
/*
|
||||||
|
NOTE: this loop probably doesn't do the right thing for the case
|
||||||
|
where FirstMatch's duplicate-generating range is interleaved with
|
||||||
|
"unrelated" tables (as specified in WL#3750, section 2.2).
|
||||||
|
*/
|
||||||
|
if (!j->emb_sj_nest)
|
||||||
jump_to= tab;
|
jump_to= tab;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -608,7 +608,12 @@ int JOIN_CACHE_BKA::init()
|
|||||||
copy_end= cache->field_descr+cache->fields;
|
copy_end= cache->field_descr+cache->fields;
|
||||||
for (copy= cache->field_descr+cache->flag_fields; copy < copy_end; copy++)
|
for (copy= cache->field_descr+cache->flag_fields; copy < copy_end; copy++)
|
||||||
{
|
{
|
||||||
if (copy->field->table == tab->table &&
|
/*
|
||||||
|
(1) - when we store rowids for DuplicateWeedout, they have
|
||||||
|
copy->field==NULL
|
||||||
|
*/
|
||||||
|
if (copy->field && // (1)
|
||||||
|
copy->field->table == tab->table &&
|
||||||
bitmap_is_set(key_read_set, copy->field->field_index))
|
bitmap_is_set(key_read_set, copy->field->field_index))
|
||||||
{
|
{
|
||||||
*copy_ptr++= copy;
|
*copy_ptr++= copy;
|
||||||
|
@ -5632,7 +5632,11 @@ void calc_used_field_length(THD *thd, JOIN_TAB *join_tab)
|
|||||||
uint blob_length=(uint) (join_tab->table->file->stats.mean_rec_length-
|
uint blob_length=(uint) (join_tab->table->file->stats.mean_rec_length-
|
||||||
(join_tab->table->s->reclength-rec_length));
|
(join_tab->table->s->reclength-rec_length));
|
||||||
rec_length+=(uint) max(4,blob_length);
|
rec_length+=(uint) max(4,blob_length);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
psergey-todo: why we don't count here rowid that we might need to store
|
||||||
|
when using DuplicateElimination?
|
||||||
|
*/
|
||||||
join_tab->used_fields=fields;
|
join_tab->used_fields=fields;
|
||||||
join_tab->used_fieldlength=rec_length;
|
join_tab->used_fieldlength=rec_length;
|
||||||
join_tab->used_blobs=blobs;
|
join_tab->used_blobs=blobs;
|
||||||
|
Reference in New Issue
Block a user