Commit 981a6b70 authored by Monty's avatar Monty

MDEV-30395 Wrong result with semijoin and Federated as outer table

The problem was that federated engine does not support comparable rowids
which was not taken into account by semijoin code.

Fixed by checking that we don't use semijoin with tables that does not
support comparable rowids.

Other things:
- Fixed some typos in the code comments
parent 0595dd0f
...@@ -2325,6 +2325,38 @@ DROP TABLE federated.t1; ...@@ -2325,6 +2325,38 @@ DROP TABLE federated.t1;
connection slave; connection slave;
DROP TABLE federated.t1; DROP TABLE federated.t1;
connection default; connection default;
#
# MDEV-30395 Wrong result with semijoin and Federated as outer table
#
create server s foreign data wrapper mysql options (host "127.0.0.1", database "test", user "root", port MASTER_PORT);
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (3),(2),(3);
CREATE TABLE t2 (pk INT PRIMARY KEY);
INSERT INTO t2 VALUES (1),(2),(3),(4);
set @save_optimizer_switch=@@optimizer_switch;
set optimizer_switch="materialization=off";
CREATE TABLE t2_fed ENGINE=FEDERATED CONNECTION='s/t2';
explain SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2_fed ALL NULL NULL NULL NULL 4 Using where
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
pk
2
3
SET optimizer_switch='semijoin=off';
explain SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2_fed ALL NULL NULL NULL NULL 4 Using where
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
pk
2
3
DROP TABLE t2_fed, t1, t2;
set @@optimizer_switch=@save_optimizer_switch;
DROP SERVER s;
# End of 10.5 tests
connection master; connection master;
DROP TABLE IF EXISTS federated.t1; DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated; DROP DATABASE IF EXISTS federated;
......
...@@ -2060,4 +2060,34 @@ connection slave; ...@@ -2060,4 +2060,34 @@ connection slave;
DROP TABLE federated.t1; DROP TABLE federated.t1;
connection default; connection default;
--echo #
--echo # MDEV-30395 Wrong result with semijoin and Federated as outer table
--echo #
--replace_result $MASTER_MYPORT MASTER_PORT
eval create server s foreign data wrapper mysql options (host "127.0.0.1", database "test", user "root", port $MASTER_MYPORT);
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (3),(2),(3);
CREATE TABLE t2 (pk INT PRIMARY KEY);
INSERT INTO t2 VALUES (1),(2),(3),(4);
set @save_optimizer_switch=@@optimizer_switch;
set optimizer_switch="materialization=off";
CREATE TABLE t2_fed ENGINE=FEDERATED CONNECTION='s/t2';
explain SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
SET optimizer_switch='semijoin=off';
explain SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
DROP TABLE t2_fed, t1, t2;
set @@optimizer_switch=@save_optimizer_switch;
DROP SERVER s;
--echo # End of 10.5 tests
source include/federated_cleanup.inc; source include/federated_cleanup.inc;
...@@ -356,9 +356,9 @@ enum chf_create_flags { ...@@ -356,9 +356,9 @@ enum chf_create_flags {
Rowid's are not comparable. This is set if the rowid is unique to the Rowid's are not comparable. This is set if the rowid is unique to the
current open handler, like it is with federated where the rowid is a current open handler, like it is with federated where the rowid is a
pointer to a local result set buffer. The effect of having this set is pointer to a local result set buffer. The effect of having this set is
that the optimizer will not consirer the following optimizations for that the optimizer will not consider the following optimizations for
the table: the table:
ror scans or filtering ror scans, filtering or duplicate weedout
*/ */
#define HA_NON_COMPARABLE_ROWID (1ULL << 60) #define HA_NON_COMPARABLE_ROWID (1ULL << 60)
......
...@@ -664,6 +664,17 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -664,6 +664,17 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
} }
/* Check if any table is not supporting comparable rowids */
{
List_iterator_fast<TABLE_LIST> li(select_lex->outer_select()->leaf_tables);
TABLE_LIST *tbl;
while ((tbl = li++))
{
TABLE *table= tbl->table;
if (table && table->file->ha_table_flags() & HA_NON_COMPARABLE_ROWID)
join->not_usable_rowid_map|= table->map;
}
}
DBUG_PRINT("info", ("Checking if subq can be converted to semi-join")); DBUG_PRINT("info", ("Checking if subq can be converted to semi-join"));
/* /*
...@@ -683,8 +694,11 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -683,8 +694,11 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
9. Parent select is not a table-less select 9. Parent select is not a table-less select
10. Neither parent nor child select have STRAIGHT_JOIN option. 10. Neither parent nor child select have STRAIGHT_JOIN option.
11. It is first optimisation (the subquery could be moved from ON 11. It is first optimisation (the subquery could be moved from ON
clause during first optimisation and then be considered for SJ clause during first optimisation and then be considered for SJ
on the second when it is too late) on the second when it is too late)
12. All tables supports comparable rowids.
This is needed for DuplicateWeedout strategy to work (which
is the catch-all semi-join strategy so it must be applicable).
*/ */
if (optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN) && if (optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN) &&
in_subs && // 1 in_subs && // 1
...@@ -699,7 +713,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -699,7 +713,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
!((join->select_options | // 10 !((join->select_options | // 10
select_lex->outer_select()->join->select_options) // 10 select_lex->outer_select()->join->select_options) // 10
& SELECT_STRAIGHT_JOIN) && // 10 & SELECT_STRAIGHT_JOIN) && // 10
select_lex->first_cond_optimization) // 11 select_lex->first_cond_optimization && // 11
join->not_usable_rowid_map == 0) // 12
{ {
DBUG_PRINT("info", ("Subquery is semi-join conversion candidate")); DBUG_PRINT("info", ("Subquery is semi-join conversion candidate"));
...@@ -3544,6 +3559,9 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join, ...@@ -3544,6 +3559,9 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join,
} }
else else
{ {
/* Ensure that table supports comparable rowids */
DBUG_ASSERT(!(p->table->table->file->ha_table_flags() & HA_NON_COMPARABLE_ROWID));
sj_outer_fanout= COST_MULT(sj_outer_fanout, p->records_read); sj_outer_fanout= COST_MULT(sj_outer_fanout, p->records_read);
temptable_rec_size += p->table->table->file->ref_length; temptable_rec_size += p->table->table->file->ref_length;
} }
......
...@@ -2309,7 +2309,7 @@ JOIN::optimize_inner() ...@@ -2309,7 +2309,7 @@ JOIN::optimize_inner()
/* /*
We have to remove constants and duplicates from group_list before We have to remove constants and duplicates from group_list before
calling make_join_statistics() as this may call get_best_group_min_max() calling make_join_statistics() as this may call get_best_group_min_max()
which needs a simplfied group_list. which needs a simplified group_list.
*/ */
if (group_list && table_count == 1) if (group_list && table_count == 1)
{ {
......
...@@ -1238,6 +1238,8 @@ class JOIN :public Sql_alloc ...@@ -1238,6 +1238,8 @@ class JOIN :public Sql_alloc
table_map outer_join; table_map outer_join;
/* Bitmap of tables used in the select list items */ /* Bitmap of tables used in the select list items */
table_map select_list_used_tables; table_map select_list_used_tables;
/* Tables that has HA_NON_COMPARABLE_ROWID (does not support rowid) set */
table_map not_usable_rowid_map;
ha_rows send_records,found_records,join_examined_rows; ha_rows send_records,found_records,join_examined_rows;
/* /*
...@@ -1550,7 +1552,7 @@ class JOIN :public Sql_alloc ...@@ -1550,7 +1552,7 @@ class JOIN :public Sql_alloc
table_count= 0; table_count= 0;
top_join_tab_count= 0; top_join_tab_count= 0;
const_tables= 0; const_tables= 0;
const_table_map= found_const_table_map= 0; const_table_map= found_const_table_map= not_usable_rowid_map= 0;
aggr_tables= 0; aggr_tables= 0;
eliminated_tables= 0; eliminated_tables= 0;
join_list= 0; join_list= 0;
......
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