From bd39e4ba2e7fb34e00027e0f1e9a2b048d1ce5c2 Mon Sep 17 00:00:00 2001
From: unknown <igor@olga.mysql.com>
Date: Sun, 11 Mar 2007 23:34:40 -0700
Subject: [PATCH] Fixed bug #26963: invalid optimization of the pushdown
 conditions after single-row table substitution could lead to a wrong result
 set. The bug happened because the function Item_field::replace_equal_field
 erroniously assumed that any field included in a multiple equality with a
 constant has been already substituted for this constant. This not true for
 fields becoming constant after row substitutions for constant tables.

mysql-test/r/select.result:
  Added a test case for bug #26963.
mysql-test/t/select.test:
  Added a test case for bug #26963.
sql/item.cc:
  Fixed bug #26963: invalid optimization of the pushdown conditions
  after single-row table substitution could lead to a wrong result set.
  The bug happened because the function Item_field::replace_equal_field
  erroneously assumed that any field included in a multiple equality
  with a constant has been already substituted for this constant.
  This not true for fields becoming constant after row substitutions
  for constant tables.
---
 mysql-test/r/select.result | 38 ++++++++++++++++++++++++++++++
 mysql-test/t/select.test   | 47 ++++++++++++++++++++++++++++++++++++++
 sql/item.cc                | 15 ++++++++++--
 3 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result
index c3132a1b5f6..ae32607973a 100644
--- a/mysql-test/r/select.result
+++ b/mysql-test/r/select.result
@@ -3933,4 +3933,42 @@ cc 	cc 	7
 aa  	aa	2
 aa	aa	2
 DROP TABLE t1,t2;
+CREATE TABLE t1 (
+access_id int NOT NULL default '0',
+name varchar(20) default NULL,
+rank int NOT NULL default '0',
+KEY idx (access_id)
+);
+CREATE TABLE t2 (
+faq_group_id int NOT NULL default '0',
+faq_id int NOT NULL default '0',
+access_id int default NULL,
+UNIQUE KEY idx1 (faq_id),
+KEY idx2 (faq_group_id,faq_id)
+);
+INSERT INTO t1 VALUES 
+(1,'Everyone',2),(2,'Help',3),(3,'Technical Support',1),(4,'Chat User',4);
+INSERT INTO t2 VALUES
+(261,265,1),(490,494,1);
+SELECT t2.faq_id 
+FROM t1 INNER JOIN t2 IGNORE INDEX (idx1)
+ON (t1.access_id = t2.access_id)
+LEFT JOIN t2 t
+ON (t.faq_group_id = t2.faq_group_id AND
+find_in_set(t.access_id, '1,4') < find_in_set(t2.access_id, '1,4'))
+WHERE
+t2.access_id IN (1,4) AND t.access_id IS NULL AND t2.faq_id in (265);
+faq_id
+265
+SELECT t2.faq_id 
+FROM t1 INNER JOIN t2
+ON (t1.access_id = t2.access_id)
+LEFT JOIN t2 t
+ON (t.faq_group_id = t2.faq_group_id AND
+find_in_set(t.access_id, '1,4') < find_in_set(t2.access_id, '1,4'))
+WHERE
+t2.access_id IN (1,4) AND t.access_id IS NULL AND t2.faq_id in (265);
+faq_id
+265
+DROP TABLE t1,t2;
 End of 5.0 tests
diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test
index ea5fadb2e1b..8442a073620 100644
--- a/mysql-test/t/select.test
+++ b/mysql-test/t/select.test
@@ -3299,4 +3299,51 @@ SELECT * FROM t1 LEFT JOIN t2 ON t1.name=t2.name;
 
 DROP TABLE t1,t2;
 
+
+#
+# Bug #26963: join with predicates that contain fields from equalities evaluated
+#             to constants after constant table substitution
+#
+
+CREATE TABLE t1 (
+ access_id int NOT NULL default '0',
+ name varchar(20) default NULL,
+ rank int NOT NULL default '0',
+ KEY idx (access_id)
+);
+
+CREATE TABLE t2 (
+  faq_group_id int NOT NULL default '0',
+  faq_id int NOT NULL default '0',
+  access_id int default NULL,
+  UNIQUE KEY idx1 (faq_id),
+  KEY idx2 (faq_group_id,faq_id)
+);
+
+INSERT INTO t1 VALUES 
+  (1,'Everyone',2),(2,'Help',3),(3,'Technical Support',1),(4,'Chat User',4);
+INSERT INTO t2 VALUES
+  (261,265,1),(490,494,1);
+
+
+SELECT t2.faq_id 
+  FROM t1 INNER JOIN t2 IGNORE INDEX (idx1)
+       ON (t1.access_id = t2.access_id)
+       LEFT JOIN t2 t
+       ON (t.faq_group_id = t2.faq_group_id AND
+           find_in_set(t.access_id, '1,4') < find_in_set(t2.access_id, '1,4'))
+   WHERE
+     t2.access_id IN (1,4) AND t.access_id IS NULL AND t2.faq_id in (265);
+
+SELECT t2.faq_id 
+  FROM t1 INNER JOIN t2
+       ON (t1.access_id = t2.access_id)
+       LEFT JOIN t2 t
+       ON (t.faq_group_id = t2.faq_group_id AND
+           find_in_set(t.access_id, '1,4') < find_in_set(t2.access_id, '1,4'))
+   WHERE
+     t2.access_id IN (1,4) AND t.access_id IS NULL AND t2.faq_id in (265);
+
+DROP TABLE t1,t2;
+
 --echo End of 5.0 tests
diff --git a/sql/item.cc b/sql/item.cc
index ec33bf1ddc7..11a5039ca19 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -4075,7 +4075,9 @@ bool Item_field::set_no_const_sub(byte *arg)
   DESCRIPTION
     The function returns a pointer to an item that is taken from
     the very beginning of the item_equal list which the Item_field
-    object refers to (belongs to).  
+    object refers to (belongs to) unless item_equal contains  a constant
+    item. In this case the function returns this constant item, 
+    (if the substitution does not require conversion).   
     If the Item_field object does not refer any Item_equal object
     'this' is returned 
 
@@ -4084,7 +4086,8 @@ bool Item_field::set_no_const_sub(byte *arg)
     of the thransformer method.  
 
   RETURN VALUES
-    pointer to a replacement Item_field if there is a better equal item;
+    pointer to a replacement Item_field if there is a better equal item or
+    a pointer to a constant equal item;
     this - otherwise.
 */
 
@@ -4092,6 +4095,14 @@ Item *Item_field::replace_equal_field(byte *arg)
 {
   if (item_equal)
   {
+    Item *const_item= item_equal->get_const();
+    if (const_item)
+    {
+      if (cmp_context != (Item_result)-1 &&
+          const_item->cmp_context != cmp_context)
+        return this;
+      return const_item;
+    }
     Item_field *subst= item_equal->get_first();
     if (subst && !field->eq(subst->field))
       return subst;
-- 
2.30.9