Skip to content

Commit f41b6dd

Browse files
committed
MDEV-37913: disable_index_merge_plans causes SELECT data loss when more than 100 ORs
*DRAFT* SEL_TREE* tree_or(SEL_TREE *X, SEL_TREE *Y) tries to conserve memory. In certain cases it reuses the X object for the return value. MDEV-34620 has added logic to disable construction of index_merge plans for multi-way ORs. That logic interferred with the logic above and has caused an error like this: for parameters of: X = SEL_TREE{ trees=[key1_treeA, key2_treeB]} Y = SEL_TREE{ trees=[key1_treeC ] } we would decide to to reuse object X. for key1, we would produce key_or(key1_treeA, key1_treeC) but key2_treeB would be just left there. As as result, for "X OR Y" we would a SEL_TREE with key2_treeB. If one tries to construct index merge, that's essentially only reading rows matching one of AND-parts of Y and missing rows matching X. Fixed by clearing out trees like key2_treeB.
1 parent 21be9fb commit f41b6dd

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

mysql-test/main/range.result

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3736,6 +3736,27 @@ DROP TABLE t1;
37363736
#
37373737
# End of 10.5 tests
37383738
#
3739+
#
3740+
# MDEV-37913: disable_index_merge_plans causes SELECT data loss when more than 100 ORs
3741+
#
3742+
CREATE TABLE t1 (
3743+
id int primary key,
3744+
key1 int,
3745+
index(key1)
3746+
);
3747+
INSERT INTO t1 VALUES
3748+
(1, 1),
3749+
(2, 1),
3750+
(3, 2);
3751+
$query;
3752+
id key1
3753+
1 1
3754+
2 1
3755+
3 2
3756+
drop table t1;
3757+
#
3758+
# End of 10.11 tests
3759+
#
37393760
set global innodb_stats_persistent= @innodb_stats_persistent_save;
37403761
set global innodb_stats_persistent_sample_pages=
37413762
@innodb_stats_persistent_sample_pages_save;

mysql-test/main/range.test

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,32 @@ DROP TABLE t1;
25332533
--echo # End of 10.5 tests
25342534
--echo #
25352535

2536+
--echo #
2537+
--echo # MDEV-37913: disable_index_merge_plans causes SELECT data loss when more than 100 ORs
2538+
--echo #
2539+
CREATE TABLE t1 (
2540+
id int primary key,
2541+
key1 int,
2542+
index(key1)
2543+
);
2544+
2545+
INSERT INTO t1 VALUES
2546+
(1, 1),
2547+
(2, 1),
2548+
(3, 2);
2549+
2550+
let $query=`
2551+
select concat('select * from t1 where (key1 = 2 AND id = 3) ',
2552+
REPEAT(' OR (key1 = 1)', 100))
2553+
`;
2554+
2555+
evalp $query;
2556+
2557+
drop table t1;
2558+
2559+
--echo #
2560+
--echo # End of 10.11 tests
2561+
--echo #
25362562
set global innodb_stats_persistent= @innodb_stats_persistent_save;
25372563
set global innodb_stats_persistent_sample_pages=
25382564
@innodb_stats_persistent_sample_pages_save;

sql/opt_range.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9979,6 +9979,20 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
99799979

99809980
if (no_imerge_from_ranges && no_merges1 && no_merges2)
99819981
{
9982+
/*
9983+
If we have force-disabled index_merge, tree1 may have tree->keys[N] set
9984+
for some N which are not in the ored_keys bitmap.
9985+
Clear those out as they must not get into the result set.
9986+
*/
9987+
if (param->disable_index_merge_plans)
9988+
{
9989+
key_map map1= tree1->keys_map;
9990+
map1.subtract(ored_keys);
9991+
key_map::Iterator it(map1);
9992+
int key_no;
9993+
while ((key_no= it++) != key_map::Iterator::BITMAP_END)
9994+
tree1->keys[key_no]= NULL;
9995+
}
99829996
/*
99839997
Reuse tree1 as the result in simple cases. This reduces memory usage
99849998
for e.g. "key IN (c1, ..., cN)" which produces a lot of ranges.

0 commit comments

Comments
 (0)