Skip to content

Commit

Permalink
MDEV-21243: Join buffer: condition is checked in wrong place for rang…
Browse files Browse the repository at this point in the history
…e access

In this scenario:
- There is a possible range access for table T
- And there is a ref access on the same index which uses fewer key parts
- The join optimizer picks the ref access (because it is cheaper)
- make_join_select applies this heuristic to switch to range:
  /* Range uses longer key;  Use this instead of ref on key */

Join buffer will be used without having called
JOIN_TAB::make_scan_filter(). This means, conditions that should be
checked when reading table T will be checked after T is joined with the
contents of the join buffer, instead.

Fixed this by adding a make_scan_filter() check.
(updated patch after backport to 10.3)
(Fix testcase on Windows)
  • Loading branch information
spetrunia committed Jan 12, 2020
1 parent cba9ed1 commit 9c3eca8
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
72 changes: 72 additions & 0 deletions mysql-test/main/join_cache.result
Original file line number Diff line number Diff line change
Expand Up @@ -6056,4 +6056,76 @@ select f2 from t2,t1 where f2 = 0;
f2
drop table t1, t2;
set join_buffer_size=@save_join_buffer_size;
#
# MDEV-21243: Join buffer: condition is checked in wrong place for range access
#
create table t1(a int primary key);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int);
insert into t2 select A.a + 10*B.a from t1 A, t1 B;
create table t3 (
kp1 int,
kp2 int,
col1 int,
col2 int,
key (kp1, kp2)
);
insert into t3
select
A.a,
B.a,
A.a + 100*B.a,
A.a + 100*B.a
from
t2 A, t2 B;
analyze table t3;
Table Op Msg_type Msg_text
test.t3 analyze status Table is already up to date
# The following must have "B.col1 + 1 < 33333" attached to table B
# and not to the block-nl-join node:
explain format=json
select *
from t1 a, t3 b
where
b.kp1=a.a and
b.kp1 <= 10 and
b.kp2 <= 10 and
b.col1 +1 < 33333;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "a",
"access_type": "index",
"possible_keys": ["PRIMARY"],
"key": "PRIMARY",
"key_length": "4",
"used_key_parts": ["a"],
"rows": 10,
"filtered": 100,
"attached_condition": "a.a <= 10",
"using_index": true
},
"block-nl-join": {
"table": {
"table_name": "b",
"access_type": "range",
"possible_keys": ["kp1"],
"key": "kp1",
"key_length": "10",
"used_key_parts": ["kp1", "kp2"],
"rows": 836,
"filtered": 11.962,
"index_condition": "b.kp2 <= 10",
"attached_condition": "b.kp2 <= 10 and b.col1 + 1 < 33333"
},
"buffer_type": "flat",
"buffer_size": "256Kb",
"join_type": "BNL",
"attached_condition": "b.kp1 = a.a"
}
}
}
drop table t1,t2,t3;
set @@optimizer_switch=@save_optimizer_switch;
40 changes: 40 additions & 0 deletions mysql-test/main/join_cache.test
Original file line number Diff line number Diff line change
Expand Up @@ -4014,5 +4014,45 @@ select f2 from t2,t1 where f2 = 0;
drop table t1, t2;
set join_buffer_size=@save_join_buffer_size;


--echo #
--echo # MDEV-21243: Join buffer: condition is checked in wrong place for range access
--echo #
create table t1(a int primary key);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int);
insert into t2 select A.a + 10*B.a from t1 A, t1 B;

create table t3 (
kp1 int,
kp2 int,
col1 int,
col2 int,
key (kp1, kp2)
);

insert into t3
select
A.a,
B.a,
A.a + 100*B.a,
A.a + 100*B.a
from
t2 A, t2 B;
analyze table t3;

--echo # The following must have "B.col1 + 1 < 33333" attached to table B
--echo # and not to the block-nl-join node:
explain format=json
select *
from t1 a, t3 b
where
b.kp1=a.a and
b.kp1 <= 10 and
b.kp2 <= 10 and
b.col1 +1 < 33333;

drop table t1,t2,t3;

# The following command must be the last one in the file
set @@optimizer_switch=@save_optimizer_switch;
10 changes: 10 additions & 0 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10966,6 +10966,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (i != join->const_tables && tab->use_quick != 2 &&
!tab->first_inner)
{ /* Read with cache */
/*
TODO: the execution also gets here when we will not be using
join buffer. Review these cases and perhaps, remove this call.
(The final decision whether to use join buffer is made in
check_join_cache_usage, so we should only call make_scan_filter()
there, too).
*/
if (tab->make_scan_filter())
DBUG_RETURN(1);
}
Expand Down Expand Up @@ -11928,6 +11935,9 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if ((tab->cache= new (root) JOIN_CACHE_BNL(join, tab, prev_cache)))
{
tab->icp_other_tables_ok= FALSE;
/* If make_join_select() hasn't called make_scan_filter(), do it now */
if (!tab->cache_select && tab->make_scan_filter())
goto no_join_cache;
return (2 - MY_TEST(!prev_cache));
}
goto no_join_cache;
Expand Down

0 comments on commit 9c3eca8

Please sign in to comment.