From 38f4c6137a64192a962ad9d357055ced0f9150fa Mon Sep 17 00:00:00 2001
From: unknown <sergefp@mysql.com>
Date: Fri, 13 Jul 2007 19:13:40 +0400
Subject: [PATCH] BUG#29740: Wrong query results for index_merge/union over
 HEAP table. - return HA_KEY_SCAN_NOT_ROR flag for HASH indexes; - Fix
 ha_heap::cmp_ref() to work with BTREE index scans.

mysql-test/r/index_merge.result:
  BUG#29740: testcase
mysql-test/t/index_merge.test:
  BUG#29740: testcase
sql/ha_heap.h:
  BUG#29740: Wrong query results for index_merge/union over HEAP table.
  - make HEAP table engine return HA_KEY_SCAN_NOT_ROR flag for HASH
    indexes,as HASH index does not guarantee any ordering for rows
    within the hash bucket.
  - Fix BTREE indexes: make ha_heap::cmp_ref() compare the rowids in the
    same way as ha_key_cmp() does.
sql/opt_range.cc:
  BUG#29740: Fix comment about ROR scans.
---
 mysql-test/r/index_merge.result | 61 +++++++++++++++++++++++++++++++++
 mysql-test/t/index_merge.test   | 43 +++++++++++++++++++++++
 sql/ha_heap.h                   |  8 ++---
 sql/opt_range.cc                | 25 ++++++--------
 4 files changed, 118 insertions(+), 19 deletions(-)

diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result
index 9456b4ec978..c7e4ead9eeb 100644
--- a/mysql-test/r/index_merge.result
+++ b/mysql-test/r/index_merge.result
@@ -455,3 +455,64 @@ a
 1
 UNLOCK TABLES;
 DROP TABLE t1, t2;
+CREATE TABLE `t1` (
+`a` int(11) DEFAULT NULL,
+`filler` char(200) DEFAULT NULL,
+`b` int(11) DEFAULT NULL,
+KEY `a` (`a`),
+KEY `b` (`b`)
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+insert into t1 values
+(0, 'filler', 0), (1, 'filler', 1), (2, 'filler', 2), (3, 'filler', 3), 
+(4, 'filler', 4), (5, 'filler', 5), (6, 'filler', 6), (7, 'filler', 7), 
+(8, 'filler', 8), (9, 'filler', 9), (0, 'filler', 0), (1, 'filler', 1), 
+(2, 'filler', 2), (3, 'filler', 3), (4, 'filler', 4), (5, 'filler', 5), 
+(6, 'filler', 6), (7, 'filler', 7), (8, 'filler', 8), (9, 'filler', 9), 
+(10, 'filler', 10), (11, 'filler', 11), (12, 'filler', 12), (13, 'filler', 13),
+(14, 'filler', 14), (15, 'filler', 15), (16, 'filler', 16), (17, 'filler', 17), 
+(18, 'filler', 18), (19, 'filler', 19), (4, '5      ', 0), (5, '4      ', 0), 
+(4, '4      ', 0), (4, 'qq     ', 5), (5, 'qq     ', 4), (4, 'zz     ', 4);
+create table t2(
+`a` int(11) DEFAULT NULL,
+`filler` char(200) DEFAULT NULL,
+`b` int(11) DEFAULT NULL,
+KEY USING BTREE (`a`),
+KEY USING BTREE (`b`)
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+insert into t2 select * from t1;
+must use sort-union rather than union:
+explain select * from t1 where a=4 or b=4;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index_merge	a,b	a,b	5,5	NULL	4	Using sort_union(a,b); Using where
+select * from t1 where a=4 or b=4;
+a	filler	b
+4	zz	4
+5	qq	4
+4	filler	4
+4	qq	5
+4	4	0
+4	filler	4
+4	5	0
+select * from t1 ignore index(a,b) where a=4 or b=4;
+a	filler	b
+4	filler	4
+4	filler	4
+4	5	0
+4	4	0
+4	qq	5
+5	qq	4
+4	zz	4
+must use union, not sort-union:
+explain select * from t2 where a=4 or b=4;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	index_merge	a,b	a,b	5,5	NULL	7	Using union(a,b); Using where
+select * from t2 where a=4 or b=4;
+a	filler	b
+4	5	0
+4	zz	4
+5	qq	4
+4	filler	4
+4	qq	5
+4	4	0
+4	filler	4
+drop table t1, t2;
diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test
index 30eb0b40fca..7d9a4340b0f 100644
--- a/mysql-test/t/index_merge.test
+++ b/mysql-test/t/index_merge.test
@@ -415,3 +415,46 @@ INSERT INTO t2(a,b) VALUES(1,2);
 SELECT t2.a FROM t1,t2 WHERE t2.b=2 AND t2.a=1;
 UNLOCK TABLES;
 DROP TABLE t1, t2;
+
+#
+# BUG#29740: HA_KEY_SCAN_NOT_ROR wasn't set for HEAP engine
+# 
+CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL,
+  `filler` char(200) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL,
+  KEY `a` (`a`),
+  KEY `b` (`b`)
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+
+insert into t1 values
+(0, 'filler', 0), (1, 'filler', 1), (2, 'filler', 2), (3, 'filler', 3), 
+(4, 'filler', 4), (5, 'filler', 5), (6, 'filler', 6), (7, 'filler', 7), 
+(8, 'filler', 8), (9, 'filler', 9), (0, 'filler', 0), (1, 'filler', 1), 
+(2, 'filler', 2), (3, 'filler', 3), (4, 'filler', 4), (5, 'filler', 5), 
+(6, 'filler', 6), (7, 'filler', 7), (8, 'filler', 8), (9, 'filler', 9), 
+(10, 'filler', 10), (11, 'filler', 11), (12, 'filler', 12), (13, 'filler', 13),
+(14, 'filler', 14), (15, 'filler', 15), (16, 'filler', 16), (17, 'filler', 17), 
+(18, 'filler', 18), (19, 'filler', 19), (4, '5      ', 0), (5, '4      ', 0), 
+(4, '4      ', 0), (4, 'qq     ', 5), (5, 'qq     ', 4), (4, 'zz     ', 4);
+
+create table t2(
+  `a` int(11) DEFAULT NULL,
+  `filler` char(200) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL,
+  KEY USING BTREE (`a`),
+  KEY USING BTREE (`b`)
+) ENGINE=MEMORY DEFAULT CHARSET=latin1;
+insert into t2 select * from t1;
+
+--echo must use sort-union rather than union:
+explain select * from t1 where a=4 or b=4;
+select * from t1 where a=4 or b=4;
+select * from t1 ignore index(a,b) where a=4 or b=4;
+
+--echo must use union, not sort-union:
+explain select * from t2 where a=4 or b=4;
+select * from t2 where a=4 or b=4;
+
+drop table t1, t2;
+
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
index 18389c1298d..27846ca4a8e 100644
--- a/sql/ha_heap.h
+++ b/sql/ha_heap.h
@@ -54,8 +54,8 @@ class ha_heap: public handler
   ulong index_flags(uint inx, uint part, bool all_parts) const
   {
     return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
-	    HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE :
-	    HA_ONLY_WHOLE_INDEX);
+            HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE :
+            HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
   }
   const key_map *keys_to_use_for_scanning() { return &btree_keys; }
   uint max_supported_keys()          const { return MAX_KEY; }
@@ -101,9 +101,7 @@ class ha_heap: public handler
 			     enum thr_lock_type lock_type);
   int cmp_ref(const byte *ref1, const byte *ref2)
   {
-    HEAP_PTR ptr1=*(HEAP_PTR*)ref1;
-    HEAP_PTR ptr2=*(HEAP_PTR*)ref2;
-    return ptr1 < ptr2? -1 : (ptr1 > ptr2? 1 : 0);
+    return memcmp(ref1, ref2, sizeof(HEAP_PTR));
   }
 private:
   void update_key_stats();
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index c3aa1f52556..952e5abfd13 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -6007,27 +6007,24 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
     ROR (Rowid Ordered Retrieval) key scan is a key scan that produces
     ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function)
 
-    An index scan is a ROR scan if it is done using a condition in form
+    This function is needed to handle a practically-important special case:
+    an index scan is a ROR scan if it is done using a condition in form
 
-        "key1_1=c_1 AND ... AND key1_n=c_n"  (1)
+        "key1_1=c_1 AND ... AND key1_n=c_n"
 
     where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n])
 
-    and the table has a clustered Primary Key
+    and the table has a clustered Primary Key defined as 
 
-    PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being
-    identical to uncovered parts ot the key being scanned (2)
-
-    Scans on HASH indexes are not ROR scans,
-    any range scan on clustered primary key is ROR scan  (3)
-
-    Check (1) is made in check_quick_keys()
-    Check (3) is made check_quick_select()
-    Check (2) is made by this function.
+      PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) 
+    
+    i.e. the first key parts of it are identical to uncovered parts ot the 
+    key being scanned. This function assumes that the index flags do not
+    include HA_KEY_SCAN_NOT_ROR flag (that is checked elsewhere).
 
   RETURN
-    TRUE  If the scan is ROR-scan
-    FALSE otherwise
+    TRUE   The scan is ROR-scan
+    FALSE  Otherwise
 */
 
 static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
-- 
2.30.9