From 3f9e14f97886e9465b69029b44ec9683c8041343 Mon Sep 17 00:00:00 2001
From: Igor Babaev <igor@askmonty.org>
Date: Sun, 16 Oct 2011 13:23:57 -0700
Subject: [PATCH] Fixed LP bug #874006. This bug manifested itself with queries
 containing non-correlated IN subqueries over materialized views/derived
 tables. The bug happened because the code of the function
 generate_derived_keys did not take into account that the function could be
 called twice when the optimizer was deciding whether in-exist transformation
 should be applied.

---
 mysql-test/r/derived_view.result | 44 +++++++++++++++++++++++++++++++-
 mysql-test/t/derived_view.test   | 37 +++++++++++++++++++++++++++
 sql/sql_select.cc                | 36 ++++++++++++++++++--------
 sql/table.cc                     | 13 ++++++----
 4 files changed, 113 insertions(+), 17 deletions(-)

diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result
index ceb63ddc967..96fcde64b57 100644
--- a/mysql-test/r/derived_view.result
+++ b/mysql-test/r/derived_view.result
@@ -781,7 +781,7 @@ SELECT * FROM t3
 WHERE t3.a IN (SELECT v1.a FROM v1, t2 WHERE t2.a = v1.b);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
 1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	4	100.00	Using where
-2	DEPENDENT SUBQUERY	<derived3>	ref	key0	key0	5	func	2	100.00	
+2	DEPENDENT SUBQUERY	<derived3>	ref	key1	key1	5	func	2	100.00	
 2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	3	100.00	Using where; Using join buffer (flat, BNL join)
 3	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	3	100.00	Using temporary; Using filesort
 Warnings:
@@ -1462,4 +1462,46 @@ b	a
 9	2
 7	2
 DROP TABLE t1,t2,t3;
+#
+# LP bug #874006: materialized view used in IN subquery
+#
+CREATE TABLE t3 (a int NOT NULL, b varchar(1), c varchar(1));
+INSERT INTO t3 VALUES (19,NULL,NULL), (20,'r','r');
+CREATE TABLE t1 (a int, b varchar(1) , c varchar(1));
+INSERT INTO t1 VALUES (1,NULL,NULL), (5,'r','r'), (7,'y','y');
+CREATE TABLE t2 (a int NOT NULL , b int, c varchar(1));
+INSERT INTO t2 VALUES (4,3,'r');
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
+SET SESSION optimizer_switch='derived_with_keys=off';
+EXPLAIN
+SELECT * FROM t3 
+WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	system	NULL	NULL	NULL	NULL	1	
+2	DEPENDENT SUBQUERY	<derived3>	ALL	NULL	NULL	NULL	NULL	3	Using where
+3	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	3	
+SELECT * FROM t3 
+WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+a	b	c
+20	r	r
+SET SESSION optimizer_switch='derived_with_keys=on';
+EXPLAIN
+SELECT * FROM t3 
+WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t3	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DEPENDENT SUBQUERY	t2	system	NULL	NULL	NULL	NULL	1	
+2	DEPENDENT SUBQUERY	<derived3>	ref	key1	key1	10	const,const	0	Using where
+3	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	3	
+SELECT * FROM t3 
+WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+a	b	c
+20	r	r
+DROP VIEW v1;
+DROP TABLE t1,t2,t3;
 set optimizer_switch=@exit_optimizer_switch;
diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test
index 9f7e24481a0..044f6a1704f 100644
--- a/mysql-test/t/derived_view.test
+++ b/mysql-test/t/derived_view.test
@@ -910,5 +910,42 @@ SELECT * FROM t1 , t2
 
 DROP TABLE t1,t2,t3;
 
+--echo #
+--echo # LP bug #874006: materialized view used in IN subquery
+--echo #
+
+CREATE TABLE t3 (a int NOT NULL, b varchar(1), c varchar(1));
+INSERT INTO t3 VALUES (19,NULL,NULL), (20,'r','r');
+
+CREATE TABLE t1 (a int, b varchar(1) , c varchar(1));
+INSERT INTO t1 VALUES (1,NULL,NULL), (5,'r','r'), (7,'y','y');
+
+CREATE TABLE t2 (a int NOT NULL , b int, c varchar(1));
+INSERT INTO t2 VALUES (4,3,'r');
+
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
+
+SET SESSION optimizer_switch='derived_with_keys=off';
+EXPLAIN
+SELECT * FROM t3 
+  WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+                   WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+SELECT * FROM t3 
+  WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+                   WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+
+SET SESSION optimizer_switch='derived_with_keys=on';
+EXPLAIN
+SELECT * FROM t3 
+  WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+                   WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+SELECT * FROM t3 
+  WHERE t3.b IN (SELECT v1.b FROM  v1, t2 
+                   WHERE t2.c = v1.c AND t2.c = v1.b AND v1.b = t3.c);
+
+DROP VIEW v1;
+DROP TABLE t1,t2,t3;
+
+
 # The following command must be the last one the file 
 set optimizer_switch=@exit_optimizer_switch;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 61981f87c0a..24778ef6a98 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -8413,17 +8413,17 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
   TABLE *table= keyuse->table;
   if (table->alloc_keys(keys))
     return TRUE;
-  uint keyno= 0;
+  uint key_count= 0;
   KEYUSE *first_keyuse= keyuse;
   uint prev_part= keyuse->keypart;
   uint parts= 0;
   uint i= 0;
 
-  for ( ; i < count && keyno < keys; )
+  for ( ; i < count && key_count < keys; )
   {
     do
     {
-      keyuse->key= keyno;
+      keyuse->key= table->s->keys;
       keyuse->keypart_map= (key_part_map) (1 << parts);     
       keyuse++;
       i++;
@@ -8437,14 +8437,14 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
     }
     else
     {
-      if (table->add_tmp_key(keyno, parts, 
+      if (table->add_tmp_key(table->s->keys, parts, 
                              get_next_field_for_derived_key, 
                              (uchar *) &first_keyuse,
                              FALSE))
         return TRUE;
-      table->reginfo.join_tab->keys.set_bit(keyno);
+      table->reginfo.join_tab->keys.set_bit(table->s->keys);
       first_keyuse= keyuse;
-      keyno++;
+      key_count++;
       parts= 0;
       prev_part= keyuse->keypart;
     }
@@ -8471,12 +8471,23 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array)
     TABLE_LIST *derived= NULL;
     if (keyuse->table != prev_table)
       derived= keyuse->table->pos_in_table_list;
-    while (derived && derived->is_materialized_derived() && 
-           keyuse->key == MAX_KEY)
+    while (derived && derived->is_materialized_derived())
     {
       if (keyuse->table != prev_table)
       {
         prev_table= keyuse->table;
+        while (keyuse->table == prev_table && keyuse->key != MAX_KEY)
+	{
+          keyuse++;
+          i++;
+        }
+        if (keyuse->table != prev_table)
+	{
+          keyuse--;
+          i--;
+          derived= NULL;
+          continue;
+        }
         first_table_keyuse= keyuse;
         last_used_tables= keyuse->used_tables;
         count= 0;
@@ -8489,11 +8500,13 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array)
       }
       count++;
       keyuse++;
+      i++;
       if (keyuse->table != prev_table)
       {
         if (generate_derived_keys_for_table(first_table_keyuse, count, ++keys))
           return TRUE;
         keyuse--;
+        i--;
 	derived= NULL;
       }
     }
@@ -8524,12 +8537,13 @@ void JOIN::drop_unused_derived_keys()
     TABLE *table=tab->table;
     if (!table)
       continue;
-    if (!table->pos_in_table_list->is_materialized_derived() ||
-        table->max_keys <= 1)
+    if (!table->pos_in_table_list->is_materialized_derived())
       continue;
-    table->use_index(tab->ref.key);
+    if (table->max_keys > 1)
+      table->use_index(tab->ref.key);
     if (table->s->keys)
       tab->ref.key= 0;
+    tab->keys= (key_map) (table->s->keys ? 1 : 0);
   }
 }
 
diff --git a/sql/table.cc b/sql/table.cc
index d54135b5620..382de16b81c 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -5220,10 +5220,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl)
   @brief
   Allocate space for keys
 
-  @param key_count  number of keys to allocate
+  @param key_count  number of keys to allocate additionally
 
   @details
-  The function allocates memory  to fit 'key_count' keys for this table.
+  The function allocates memory  to fit additionally 'key_count' keys 
+  for this table.
 
   @return FALSE   space was successfully allocated
   @return TRUE    an error occur
@@ -5231,9 +5232,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl)
 
 bool TABLE::alloc_keys(uint key_count)
 {
-  key_info= s->key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*key_count);
-  s->keys= 0;
-  max_keys= key_count;
+  key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count));
+  if (s->keys)
+    memmove(key_info, s->key_info, sizeof(KEY)*s->keys);
+  s->key_info= key_info;
+  max_keys= s->keys+key_count;
   return !(key_info);
 }
 
-- 
2.30.9