From 92d2ceac73aa175a01f520fd4b7a31ed338c1ef5 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 22 May 2023 18:58:45 +0300 Subject: [PATCH] MDEV-28285 Unexpected result when combining DISTINCT, subselect and LIMIT The problem was that when JOIN_TAB::remove_duplicates() noticed there can only be one possible row in the output, it adjusted limits but didn't take into account any possible offset. Fixed by not adjusting limit offset when setting one-row-limit. --- mysql-test/main/distinct.result | 25 +++++++++++++++++++++++++ mysql-test/main/distinct.test | 21 +++++++++++++++++++++ sql/sql_limit.h | 9 +++++++++ sql/sql_select.cc | 2 +- 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/distinct.result b/mysql-test/main/distinct.result index fa9f0259a0f..ac693421ba2 100644 --- a/mysql-test/main/distinct.result +++ b/mysql-test/main/distinct.result @@ -1157,3 +1157,28 @@ explain select * from t1 limit 0 offset 10; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Zero limit drop table t1, t2; +# +# MDEV-28285 Unexpected result when combining DISTINCT, subselect +# and LIMIT +# +create table t1 (a int primary key); +create table t2 (a int primary key, b int not null); +insert into t1 select seq from seq_1_to_10; +insert into t2 select seq,seq from seq_1_to_10; +select distinct a from t1 where t1.a=1 and t1.a in (select a from t2 where t2.b in (1,2)); +a +1 +explain select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index; Using temporary +1 PRIMARY eq_ref distinct_key distinct_key 8 func 1 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 10 Using where +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +a +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 0,1; +a +1 +drop table t1,t2; +# +# end of 10.5 tests +# diff --git a/mysql-test/main/distinct.test b/mysql-test/main/distinct.test index 893e2dcc9a7..9aa3b2921aa 100644 --- a/mysql-test/main/distinct.test +++ b/mysql-test/main/distinct.test @@ -892,3 +892,24 @@ explain select * from t1 limit 0; explain select * from t1 limit 0 offset 10; drop table t1, t2; + +--echo # +--echo # MDEV-28285 Unexpected result when combining DISTINCT, subselect +--echo # and LIMIT +--echo # + +create table t1 (a int primary key); +create table t2 (a int primary key, b int not null); + +insert into t1 select seq from seq_1_to_10; +insert into t2 select seq,seq from seq_1_to_10; + +select distinct a from t1 where t1.a=1 and t1.a in (select a from t2 where t2.b in (1,2)); +explain select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 10,10; +select distinct a from t1 where t1.a=1 and t1.a in (select a+0 from t2 where t2.b in (1,2)) limit 0,1; +drop table t1,t2; + +--echo # +--echo # end of 10.5 tests +--echo # diff --git a/sql/sql_limit.h b/sql/sql_limit.h index 19c1ce57e99..a85e8118175 100644 --- a/sql/sql_limit.h +++ b/sql/sql_limit.h @@ -52,6 +52,15 @@ class Select_limit_counters select_limit_cnt= 1; } + /* Send the first row, still honoring offset_limit_cnt */ + void send_first_row() + { + /* Guard against overflow */ + if ((select_limit_cnt= offset_limit_cnt +1 ) == 0) + select_limit_cnt= offset_limit_cnt; + // with_ties= false; Remove // on merge to 10.6 + } + bool is_unlimited() { return select_limit_cnt == HA_POS_ERROR; } bool is_unrestricted() diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2f1cc62fe39..5af2a9e6896 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24640,7 +24640,7 @@ JOIN_TAB::remove_duplicates() !(join->select_options & OPTION_FOUND_ROWS)) { // only const items with no OPTION_FOUND_ROWS - join->unit->lim.set_single_row(); // Only send first row + join->unit->lim.send_first_row(); // Only send first row my_free(sortorder); DBUG_RETURN(false); }