From 15ef38d2ea97575c71b83db6669ee20000c23a6b Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 27 Jul 2016 00:38:51 +0300 Subject: [PATCH] MDEV-10228: Delete missing rows with OR conditions Fix get_quick_keys(): When building range tree from a condition in form keypart1=const AND (keypart2 < 0 OR keypart2>=0) the SEL_ARG for keypart2 represents an interval (-inf, +inf). However, the logic that sets UNIQUE_RANGE flag fails to recognize this, and sets UNIQUE_RANGE flag if (keypart1, keypart2) covered a unique key. As a result, range access executor assumes the interval can have at most one row and only reads the first row from it. --- mysql-test/r/range.result | 31 +++++++++++++++++++++++++++++++ mysql-test/t/range.test | 29 +++++++++++++++++++++++++++++ sql/opt_range.cc | 2 ++ 3 files changed, 62 insertions(+) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index a19d906b64593..630a692cef602 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -2113,3 +2113,34 @@ a b 0 0 1 1 drop table t2; +# +# MDEV-10228: Delete missing rows with OR conditions +# (The example uses UPDATE, because UPDATE allows to use index hints +# and so it's possible to make an example that works with any storage +# engine) +# +CREATE TABLE t1 ( +key1varchar varchar(14) NOT NULL, +key2int int(11) NOT NULL DEFAULT '0', +col1 int, +PRIMARY KEY (key1varchar,key2int), +KEY key1varchar (key1varchar), +KEY key2int (key2int) +) DEFAULT CHARSET=utf8; +insert into t1 values +('value1',0, 0), +('value1',1, 0), +('value1',1000685, 0), +('value1',1003560, 0), +('value1',1004807, 0); +update t1 force index (PRIMARY) set col1=12345 +where (key1varchar='value1' AND (key2int <=1 OR key2int > 1)); +# The following must show col1=12345 for all rows: +select * from t1; +key1varchar key2int col1 +value1 0 12345 +value1 1 12345 +value1 1000685 12345 +value1 1003560 12345 +value1 1004807 12345 +drop table t1; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index b73b09dffd5c3..393ca68e9458f 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -1689,3 +1689,32 @@ insert into t2 values (0, 0, 0, 0), (1, 1, 1, 1); analyze table t2; select a, b from t2 where (a, b) in ((0, 0), (1, 1)); drop table t2; + +--echo # +--echo # MDEV-10228: Delete missing rows with OR conditions +--echo # (The example uses UPDATE, because UPDATE allows to use index hints +--echo # and so it's possible to make an example that works with any storage +--echo # engine) +--echo # + +CREATE TABLE t1 ( + key1varchar varchar(14) NOT NULL, + key2int int(11) NOT NULL DEFAULT '0', + col1 int, + PRIMARY KEY (key1varchar,key2int), + KEY key1varchar (key1varchar), + KEY key2int (key2int) +) DEFAULT CHARSET=utf8; + +insert into t1 values + ('value1',0, 0), + ('value1',1, 0), + ('value1',1000685, 0), + ('value1',1003560, 0), + ('value1',1004807, 0); + +update t1 force index (PRIMARY) set col1=12345 +where (key1varchar='value1' AND (key2int <=1 OR key2int > 1)); +--echo # The following must show col1=12345 for all rows: +select * from t1; +drop table t1; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index f4ac47fee9617..a40363ff9ab3a 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -10409,8 +10409,10 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, KEY *table_key=quick->head->key_info+quick->index; flag=EQ_RANGE; if ((table_key->flags & HA_NOSAME) && + min_part == key_tree->part && key_tree->part == table_key->key_parts-1) { + DBUG_ASSERT(min_part == max_part); if ((table_key->flags & HA_NULL_PART_KEY) && null_part_in_key(key, param->min_key,