From 19a847d40c03213fd05693b1b43d974ce0bfe6bf Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 5 Nov 2020 17:37:55 +0100 Subject: [PATCH 1/5] MDEV-19838: followup to make happy following protocol implementations: - mysqlnd from PHP < 7.3 - mysql-connector-python any version - mysql-connector-java any version Relaxed check about garbage at the end of the packet in case of no parameters. Added check for array binding. Fixed test according to the new paradigm (allow junk at the end of the packet) --- sql/sql_prepare.cc | 21 +++++++++++++++++---- tests/mysql_client_test.c | 7 +++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 11edd577309..4bf8142959d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3271,10 +3271,19 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length) { uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround + DBUG_ENTER("mysqld_stmt_execute_bulk"); + + const uint packet_header_lenght= 4 + 2; //ID & 2 bytes of flags + + if (packet_length < packet_header_lenght) + { + my_error(ER_MALFORMED_PACKET, MYF(0)); + DBUG_VOID_RETURN; + } + ulong stmt_id= uint4korr(packet); uint flags= (uint) uint2korr(packet + 4); uchar *packet_end= packet + packet_length; - DBUG_ENTER("mysqld_stmt_execute_bulk"); if (!(thd->client_capabilities & MARIADB_CLIENT_STMT_BULK_OPERATIONS)) @@ -3282,16 +3291,18 @@ void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_PRINT("error", ("An attempt to execute bulk operation without support")); my_error(ER_UNSUPPORTED_PS, MYF(0)); + DBUG_VOID_RETURN; } /* Check for implemented parameters */ if (flags & (~STMT_BULK_FLAG_CLIENT_SEND_TYPES)) { DBUG_PRINT("error", ("unsupported bulk execute flags %x", flags)); my_error(ER_UNSUPPORTED_PS, MYF(0)); + DBUG_VOID_RETURN; } /* stmt id and two bytes of flags */ - packet+= 4 + 2; + packet+= packet_header_lenght; mysql_stmt_execute_common(thd, stmt_id, packet, packet_end, 0, TRUE, (flags & STMT_BULK_FLAG_CLIENT_SEND_TYPES)); DBUG_VOID_RETURN; @@ -3368,9 +3379,11 @@ stmt_execute_packet_sanity_check(Prepared_statement *stmt, { /* If there is no parameters, this should be normally already end - of the packet. If it's not - then error + of the packet, but it is not a problem if something left (popular + mistake in protocol implementation) because we will not read anymore + from the buffer. */ - return (packet_end > packet); + return false; } return false; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 058168eedd5..93f23236dbc 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -20032,8 +20032,11 @@ static void test_mdev19838() " VALUES " "(0x1111111111111111)", -1); - /* Expecting an error if parameters are sent */ - DIE_UNLESS(rc != 0 || paramCount == 0); + /* + We allow junk at the end of the packet in case of + no parameters. So it will succeed. + */ + DIE_UNLESS(rc == 0); } mysql_stmt_close(stmt); From 2845b656a3ed058bc424ca371760fb17b2a28703 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 5 Nov 2020 17:49:58 +0100 Subject: [PATCH 2/5] Bump the version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a1f0ad08a61..b451aa8a789 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=2 -MYSQL_VERSION_PATCH=35 +MYSQL_VERSION_PATCH=36 From f81eef62e7742806e5e74b5f37f35b7cd2f82291 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Mon, 9 Nov 2020 18:47:55 +0300 Subject: [PATCH 3/5] MDEV-24117: Memory management problem in statistics state for ... IN Part#1: Revert the patch that caused it: commit 291be494744abe90f4bdf6b5a35c4c26ee8ddda5 Author: Igor Babaev Date: Thu Sep 24 22:02:00 2020 -0700 MDEV-23811: With large number of indexes optimizer chooses an inefficient plan --- mysql-test/r/range.result | 74 +------------------ mysql-test/r/range_mrr_icp.result | 74 +------------------ .../r/range_vs_index_merge_innodb.result | 2 +- mysql-test/t/range.test | 42 ----------- sql/opt_range.cc | 20 ++--- 5 files changed, 11 insertions(+), 201 deletions(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 7299982e72d..26ea2c6d323 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -1274,7 +1274,7 @@ SELECT * FROM t1 WHERE 5 <= a AND b = 3 OR 3 <= a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index +1 SIMPLE t1 range a a 5 NULL 4 Using where; Using index SELECT * FROM t1 WHERE 3 <= a AND a <= 5 OR 5 <= a AND b = 3 OR @@ -3054,77 +3054,5 @@ a b set eq_range_index_dive_limit=default; drop table t1; # -# MDEV-23811: Both disjunct of WHERE condition contain range conditions -# for the same index such that the second range condition -# fully covers the first one. Additionally one of the disjuncts -# contains a range condition for the other index. -# -create table t1 ( -pk int primary key auto_increment, a int, b int, -index idx1(a), index idx2(b) -); -insert into t1(a,b) values -(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); -insert into t1(a,b) select a+10, b+100 from t1; -insert into t1(a,b) select a+20, b+200 from t1; -insert into t1(a,b) select a+30, b+300 from t1; -insert into t1(a,b) select a,b from t1; -analyze table t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where -select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); -pk a b -7 2 20 -71 2 20 -3 3 30 -67 3 30 -6 4 40 -70 4 40 -1 5 50 -65 5 50 -explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where -select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); -pk a b -7 2 20 -71 2 20 -3 3 30 -67 3 30 -6 4 40 -70 4 40 -1 5 50 -65 5 50 -explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where -select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); -pk a b -7 2 20 -71 2 20 -3 3 30 -67 3 30 -6 4 40 -70 4 40 -1 5 50 -65 5 50 -explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where -select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); -pk a b -7 2 20 -71 2 20 -3 3 30 -67 3 30 -6 4 40 -70 4 40 -1 5 50 -65 5 50 -drop table t1; -# # End of 10.2 tests # diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result index cdaaa5e8ab7..fe4eb99f31f 100644 --- a/mysql-test/r/range_mrr_icp.result +++ b/mysql-test/r/range_mrr_icp.result @@ -1276,7 +1276,7 @@ SELECT * FROM t1 WHERE 5 <= a AND b = 3 OR 3 <= a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index +1 SIMPLE t1 range a a 5 NULL 4 Using where; Using index SELECT * FROM t1 WHERE 3 <= a AND a <= 5 OR 5 <= a AND b = 3 OR @@ -3066,78 +3066,6 @@ a b set eq_range_index_dive_limit=default; drop table t1; # -# MDEV-23811: Both disjunct of WHERE condition contain range conditions -# for the same index such that the second range condition -# fully covers the first one. Additionally one of the disjuncts -# contains a range condition for the other index. -# -create table t1 ( -pk int primary key auto_increment, a int, b int, -index idx1(a), index idx2(b) -); -insert into t1(a,b) values -(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); -insert into t1(a,b) select a+10, b+100 from t1; -insert into t1(a,b) select a+20, b+200 from t1; -insert into t1(a,b) select a+30, b+300 from t1; -insert into t1(a,b) select a,b from t1; -analyze table t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan -select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); -pk a b -1 5 50 -3 3 30 -6 4 40 -7 2 20 -65 5 50 -67 3 30 -70 4 40 -71 2 20 -explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan -select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); -pk a b -1 5 50 -3 3 30 -6 4 40 -7 2 20 -65 5 50 -67 3 30 -70 4 40 -71 2 20 -explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan -select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); -pk a b -1 5 50 -3 3 30 -6 4 40 -7 2 20 -65 5 50 -67 3 30 -70 4 40 -71 2 20 -explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan -select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); -pk a b -1 5 50 -3 3 30 -6 4 40 -7 2 20 -65 5 50 -67 3 30 -70 4 40 -71 2 20 -drop table t1; -# # End of 10.2 tests # set optimizer_switch=@mrr_icp_extra_tmp; diff --git a/mysql-test/r/range_vs_index_merge_innodb.result b/mysql-test/r/range_vs_index_merge_innodb.result index 916c30bb770..581f512768c 100644 --- a/mysql-test/r/range_vs_index_merge_innodb.result +++ b/mysql-test/r/range_vs_index_merge_innodb.result @@ -369,7 +369,7 @@ WHERE ((ID < 200) AND (Name LIKE 'Ha%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 200) AND (Name LIKE 'Pa%' OR (Population > 103000 AND Population < 104000))); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE City range PRIMARY,Population,Country,Name PRIMARY 4 NULL 200 Using where +1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Population,PRIMARY 39,4,4 NULL 307 Using sort_union(Name,Population,PRIMARY); Using where SELECT * FROM City USE INDEX () WHERE ((ID < 10) AND (Name LIKE 'H%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 110) AND diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 0d2fbe24835..67d876d5f10 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -2095,48 +2095,6 @@ set eq_range_index_dive_limit=default; drop table t1; ---echo # ---echo # MDEV-23811: Both disjunct of WHERE condition contain range conditions ---echo # for the same index such that the second range condition ---echo # fully covers the first one. Additionally one of the disjuncts ---echo # contains a range condition for the other index. ---echo # - -create table t1 ( - pk int primary key auto_increment, a int, b int, - index idx1(a), index idx2(b) -); -insert into t1(a,b) values - (5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); -insert into t1(a,b) select a+10, b+100 from t1; -insert into t1(a,b) select a+20, b+200 from t1; -insert into t1(a,b) select a+30, b+300 from t1; -insert into t1(a,b) select a,b from t1; - -analyze table t1; - -let $q1= -select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); -eval explain $q1; -eval $q1; - -let $q2= -select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); -eval explain $q2; -eval $q2; - -let $q3= -select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); -eval explain $q3; -eval $q3; - -let $q4= -select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); -eval explain $q4; -eval $q4; - -drop table t1; - --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/opt_range.cc b/sql/opt_range.cc index cd58202ef5f..e933d2af355 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1852,9 +1852,6 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() next_key_part=arg.next_key_part; max_part_no= arg.max_part_no; use_count=1; elements=1; - next= 0; - if (next_key_part) - ++next_key_part->use_count; } @@ -8872,15 +8869,9 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) } bool no_imerge_from_ranges= FALSE; - SEL_TREE *rt1= tree1; - SEL_TREE *rt2= tree2; /* Build the range part of the tree for the formula (1) */ if (sel_trees_can_be_ored(param, tree1, tree2, &ored_keys)) { - if (no_merges1) - rt1= new SEL_TREE(tree1, TRUE, param); - if (no_merges2) - rt2= new SEL_TREE(tree2, TRUE, param); bool must_be_ored= sel_trees_must_be_ored(param, tree1, tree2, ored_keys); no_imerge_from_ranges= must_be_ored; @@ -8938,6 +8929,12 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) else if (!no_ranges1 && !no_ranges2 && !no_imerge_from_ranges) { /* Build the imerge part of the tree for the formula (1) */ + SEL_TREE *rt1= tree1; + SEL_TREE *rt2= tree2; + if (no_merges1) + rt1= new SEL_TREE(tree1, TRUE, param); + if (no_merges2) + rt2= new SEL_TREE(tree2, TRUE, param); if (!rt1 || !rt2 || result->merges.push_back(imerge_from_ranges) || imerge_from_ranges->or_sel_tree(param, rt1) || @@ -9600,11 +9597,10 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) if (!tmp->next_key_part) { - SEL_ARG *key2_next= key2->next; if (key2->use_count) { SEL_ARG *key2_cpy= new SEL_ARG(*key2); - if (!key2_cpy) + if (key2_cpy) return 0; key2= key2_cpy; } @@ -9625,7 +9621,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) Move on to next range in key2 */ key2->increment_use_count(-1); // Free not used tree - key2=key2_next; + key2=key2->next; continue; } else From 1404f3bea796c8479cf401cb36d518658600ddca Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Mon, 9 Nov 2020 19:27:56 +0300 Subject: [PATCH 4/5] MDEV-24117: Memory management problem ...: Add a testcase Add a testcase. --- mysql-test/r/range.result | 33 ++++++++++++++++++++++ mysql-test/r/range_mrr_icp.result | 33 ++++++++++++++++++++++ mysql-test/t/range.test | 46 +++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 26ea2c6d323..b3324601e47 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -3054,5 +3054,38 @@ a b set eq_range_index_dive_limit=default; drop table t1; # +# MDEV-24117: Memory management problem in statistics state... +# (just the testcase) +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 +select A.a + B.a* 10 + C.a * 100 + D.a * 1000 +from t0 A, t0 B, t0 C, t0 D +where D.a<4; +create table t2 ( +a int, +b int, +key(a) +); +insert into t2 values (1,1),(2,2),(3,3); +set @query=(select group_concat(a) from t1); +set @tmp_24117= @@max_session_mem_used; +# +# On debug build, the usage was +# - 2.8M without the bug +# - 1G with the bug. +set max_session_mem_used=64*1024*1024; +set @query=concat('explain select * from t2 where a in (', @query, ')'); +prepare s from @query; +# This should not fail with an error: +execute s; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL a NULL NULL NULL 3 Using where +set max_session_mem_used=@tmp_24117; +deallocate prepare s; +drop table t0,t1,t2; +# # End of 10.2 tests # diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result index fe4eb99f31f..d614a3397ee 100644 --- a/mysql-test/r/range_mrr_icp.result +++ b/mysql-test/r/range_mrr_icp.result @@ -3066,6 +3066,39 @@ a b set eq_range_index_dive_limit=default; drop table t1; # +# MDEV-24117: Memory management problem in statistics state... +# (just the testcase) +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 +select A.a + B.a* 10 + C.a * 100 + D.a * 1000 +from t0 A, t0 B, t0 C, t0 D +where D.a<4; +create table t2 ( +a int, +b int, +key(a) +); +insert into t2 values (1,1),(2,2),(3,3); +set @query=(select group_concat(a) from t1); +set @tmp_24117= @@max_session_mem_used; +# +# On debug build, the usage was +# - 2.8M without the bug +# - 1G with the bug. +set max_session_mem_used=64*1024*1024; +set @query=concat('explain select * from t2 where a in (', @query, ')'); +prepare s from @query; +# This should not fail with an error: +execute s; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL a NULL NULL NULL 3 Using where +set max_session_mem_used=@tmp_24117; +deallocate prepare s; +drop table t0,t1,t2; +# # End of 10.2 tests # set optimizer_switch=@mrr_icp_extra_tmp; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 67d876d5f10..f34ac2049e1 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -2095,6 +2095,52 @@ set eq_range_index_dive_limit=default; drop table t1; +--echo # +--echo # MDEV-24117: Memory management problem in statistics state... +--echo # (just the testcase) +--echo # + +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1(a int); + +# 4K rows +insert into t1 +select A.a + B.a* 10 + C.a * 100 + D.a * 1000 +from t0 A, t0 B, t0 C, t0 D +where D.a<4; + +create table t2 ( + a int, + b int, + key(a) +); + +insert into t2 values (1,1),(2,2),(3,3); + +set @query=(select group_concat(a) from t1); + +set @tmp_24117= @@max_session_mem_used; + +--echo # +--echo # On debug build, the usage was +--echo # - 2.8M without the bug +--echo # - 1G with the bug. + +set max_session_mem_used=64*1024*1024; + +set @query=concat('explain select * from t2 where a in (', @query, ')'); + +prepare s from @query; + +--echo # This should not fail with an error: +execute s; +set max_session_mem_used=@tmp_24117; + +deallocate prepare s; + +drop table t0,t1,t2; --echo # --echo # End of 10.2 tests --echo # From bea84aefb0563a10a310ea81d46c372919345c10 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 9 Nov 2020 13:51:32 -0800 Subject: [PATCH 5/5] MDEV-23811: With large number of indexes optimizer chooses an inefficient plan This bug could manifest itself for a query with WHERE condition containing top level OR formula such that each conjunct contained a single-range condition supported by the same index. One of these range conditions must be fully covered by another range condition that is used later in the OR formula. Additionally at least one of these condition should be ANDed with a sargable range condition supported by a different index. There were several attempts to fix related problems for OR conditions after the backport of range optimizer code from MySQL (commit 0e19f3e36f7842583feb6bead2c2600cd620bced). Unfortunately the first of these fixes contained typo remained unnoticed until recently. This typo bug led to rejection of valid range accesses. This patch fixed this typo bug. The fix revealed another two bugs: one in a constructor for SEL_ARG, the other in the function tree_or(). Both are fixed in this patch. --- mysql-test/r/range.result | 72 +++++++++++++++++++ mysql-test/r/range_mrr_icp.result | 72 +++++++++++++++++++ .../r/range_vs_index_merge_innodb.result | 2 +- mysql-test/t/range.test | 42 +++++++++++ sql/opt_range.cc | 10 ++- 5 files changed, 194 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index b3324601e47..9ea0dc12a0b 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -3087,5 +3087,77 @@ set max_session_mem_used=@tmp_24117; deallocate prepare s; drop table t0,t1,t2; # +# MDEV-23811: Both disjunct of WHERE condition contain range conditions +# for the same index such that the second range condition +# fully covers the first one. Additionally one of the disjuncts +# contains a range condition for the other index. +# +create table t1 ( +pk int primary key auto_increment, a int, b int, +index idx1(a), index idx2(b) +); +insert into t1(a,b) values +(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); +insert into t1(a,b) select a+10, b+100 from t1; +insert into t1(a,b) select a+20, b+200 from t1; +insert into t1(a,b) select a+30, b+300 from t1; +insert into t1(a,b) select a,b from t1; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where +select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); +pk a b +7 2 20 +71 2 20 +3 3 30 +67 3 30 +6 4 40 +70 4 40 +1 5 50 +65 5 50 +explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where +select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); +pk a b +7 2 20 +71 2 20 +3 3 30 +67 3 30 +6 4 40 +70 4 40 +1 5 50 +65 5 50 +explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where +select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); +pk a b +7 2 20 +71 2 20 +3 3 30 +67 3 30 +6 4 40 +70 4 40 +1 5 50 +65 5 50 +explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where +select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); +pk a b +7 2 20 +71 2 20 +3 3 30 +67 3 30 +6 4 40 +70 4 40 +1 5 50 +65 5 50 +drop table t1; +# # End of 10.2 tests # diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result index d614a3397ee..55613261ce9 100644 --- a/mysql-test/r/range_mrr_icp.result +++ b/mysql-test/r/range_mrr_icp.result @@ -3099,6 +3099,78 @@ set max_session_mem_used=@tmp_24117; deallocate prepare s; drop table t0,t1,t2; # +# MDEV-23811: Both disjunct of WHERE condition contain range conditions +# for the same index such that the second range condition +# fully covers the first one. Additionally one of the disjuncts +# contains a range condition for the other index. +# +create table t1 ( +pk int primary key auto_increment, a int, b int, +index idx1(a), index idx2(b) +); +insert into t1(a,b) values +(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); +insert into t1(a,b) select a+10, b+100 from t1; +insert into t1(a,b) select a+20, b+200 from t1; +insert into t1(a,b) select a+30, b+300 from t1; +insert into t1(a,b) select a,b from t1; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan +select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); +pk a b +1 5 50 +3 3 30 +6 4 40 +7 2 20 +65 5 50 +67 3 30 +70 4 40 +71 2 20 +explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan +select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); +pk a b +1 5 50 +3 3 30 +6 4 40 +7 2 20 +65 5 50 +67 3 30 +70 4 40 +71 2 20 +explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan +select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); +pk a b +1 5 50 +3 3 30 +6 4 40 +7 2 20 +65 5 50 +67 3 30 +70 4 40 +71 2 20 +explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan +select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); +pk a b +1 5 50 +3 3 30 +6 4 40 +7 2 20 +65 5 50 +67 3 30 +70 4 40 +71 2 20 +drop table t1; +# # End of 10.2 tests # set optimizer_switch=@mrr_icp_extra_tmp; diff --git a/mysql-test/r/range_vs_index_merge_innodb.result b/mysql-test/r/range_vs_index_merge_innodb.result index 581f512768c..916c30bb770 100644 --- a/mysql-test/r/range_vs_index_merge_innodb.result +++ b/mysql-test/r/range_vs_index_merge_innodb.result @@ -369,7 +369,7 @@ WHERE ((ID < 200) AND (Name LIKE 'Ha%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 200) AND (Name LIKE 'Pa%' OR (Population > 103000 AND Population < 104000))); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Population,PRIMARY 39,4,4 NULL 307 Using sort_union(Name,Population,PRIMARY); Using where +1 SIMPLE City range PRIMARY,Population,Country,Name PRIMARY 4 NULL 200 Using where SELECT * FROM City USE INDEX () WHERE ((ID < 10) AND (Name LIKE 'H%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 110) AND diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index f34ac2049e1..264f7c784ce 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -2141,6 +2141,48 @@ set max_session_mem_used=@tmp_24117; deallocate prepare s; drop table t0,t1,t2; +--echo # +--echo # MDEV-23811: Both disjunct of WHERE condition contain range conditions +--echo # for the same index such that the second range condition +--echo # fully covers the first one. Additionally one of the disjuncts +--echo # contains a range condition for the other index. +--echo # + +create table t1 ( + pk int primary key auto_increment, a int, b int, + index idx1(a), index idx2(b) +); +insert into t1(a,b) values + (5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60); +insert into t1(a,b) select a+10, b+100 from t1; +insert into t1(a,b) select a+20, b+200 from t1; +insert into t1(a,b) select a+30, b+300 from t1; +insert into t1(a,b) select a,b from t1; + +analyze table t1; + +let $q1= +select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5); +eval explain $q1; +eval $q1; + +let $q2= +select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100); +eval explain $q2; +eval $q2; + +let $q3= +select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100); +eval explain $q3; +eval $q3; + +let $q4= +select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4); +eval explain $q4; +eval $q4; + +drop table t1; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e933d2af355..798b1f284bc 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1852,6 +1852,9 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() next_key_part=arg.next_key_part; max_part_no= arg.max_part_no; use_count=1; elements=1; + next= 0; + if (next_key_part) + ++next_key_part->use_count; } @@ -9597,10 +9600,11 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) if (!tmp->next_key_part) { - if (key2->use_count) + SEL_ARG *key2_next= key2->next; + if (key2_shared) { SEL_ARG *key2_cpy= new SEL_ARG(*key2); - if (key2_cpy) + if (!key2_cpy) return 0; key2= key2_cpy; } @@ -9621,7 +9625,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) Move on to next range in key2 */ key2->increment_use_count(-1); // Free not used tree - key2=key2->next; + key2=key2_next; continue; } else