[Commits] Rev 3628: Fixed bug mdev-4177 in file:///home/igor/maria/maria-5.3-mdev4177/

Igor Babaev igor at askmonty.org
Mon Feb 25 05:16:12 EET 2013


At file:///home/igor/maria/maria-5.3-mdev4177/

------------------------------------------------------------
revno: 3628
revision-id: igor at askmonty.org-20130225031611-jk8lyhhjazov66qc
parent: igor at askmonty.org-20130222011312-0n7i0ki83efkz17e
committer: Igor Babaev <igor at askmonty.org>
branch nick: maria-5.3-mdev4177
timestamp: Sun 2013-02-24 19:16:11 -0800
message:
  Fixed bug mdev-4177
  The function remove_eq_cond removes the parts of a disjunction
  for which it has been proved that they are always true. In the
  result of this removal the disjunction may be converted into a 
  formula without OR that must be merged into the the AND formula
  that contains the disjunction.
  The merging of two AND conditions must take into account the
  multiple equalities that may be part of each of them.
  These multiple equality must be merged and become part of the
  and object built as the result of the merge of the AND conditions.
  Erroneously the function remove_eq_cond lacked the code that 
  would merge multiple equalities of the merged AND conditions.
  This could lead to confusing situations when at the same AND 
  level there were two multiple equalities with common members
  and the list of equal items contained only some of these 
  multiple equalities.
  This, in its turn, could lead to an incorrect work of the
  function substitute_for_best_equal_field when it tried to optimize
  ref accesses. This resulted in forming invalid TABLE_REF objects
  that were used to build look-up keys when materialized subqueries
  were exploited.
     
-------------- next part --------------
=== modified file 'mysql-test/r/join_outer.result'
--- a/mysql-test/r/join_outer.result	2013-01-17 11:53:15 +0000
+++ b/mysql-test/r/join_outer.result	2013-02-25 03:16:11 +0000
@@ -1759,7 +1759,7 @@
 1	SIMPLE	t1	const	PRIMARY	PRIMARY	4	const	1	100.00	
 1	SIMPLE	t2	const	PRIMARY	PRIMARY	4	const	1	100.00	Using index
 Warnings:
-Note	1003	select 5 AS `pk` from `test`.`t2` join `test`.`t1` where (1) order by 5
+Note	1003	select 5 AS `pk` from `test`.`t2` join `test`.`t1` where 1 order by 5
 SELECT t1.pk FROM t2 JOIN t1 ON t2.pk = t1.a
 WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5
 ORDER BY t1.pk;

=== modified file 'mysql-test/r/join_outer_jcl6.result'
--- a/mysql-test/r/join_outer_jcl6.result	2013-01-17 11:53:15 +0000
+++ b/mysql-test/r/join_outer_jcl6.result	2013-02-25 03:16:11 +0000
@@ -1770,7 +1770,7 @@
 1	SIMPLE	t1	const	PRIMARY	PRIMARY	4	const	1	100.00	
 1	SIMPLE	t2	const	PRIMARY	PRIMARY	4	const	1	100.00	Using index
 Warnings:
-Note	1003	select 5 AS `pk` from `test`.`t2` join `test`.`t1` where (1) order by 5
+Note	1003	select 5 AS `pk` from `test`.`t2` join `test`.`t1` where 1 order by 5
 SELECT t1.pk FROM t2 JOIN t1 ON t2.pk = t1.a
 WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5
 ORDER BY t1.pk;

=== modified file 'mysql-test/r/subselect_sj2_mat.result'
--- a/mysql-test/r/subselect_sj2_mat.result	2013-02-22 01:13:12 +0000
+++ b/mysql-test/r/subselect_sj2_mat.result	2013-02-25 03:16:11 +0000
@@ -1333,3 +1333,61 @@
 7	v	NULL
 DROP TABLE t1,t2,t3,t4;
 set optimizer_switch=@save_optimizer_switch;
+#
+# mdev-4177: materialization of a subquery whose WHERE condition is OR
+# formula with two disjucts such that the second one is always false
+#
+set @save_optimizer_switch=@@optimizer_switch;
+set @save_join_cache_level=@@join_cache_level;
+CREATE TABLE t1 (i1 int) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1), (7), (4), (8), (4);
+CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (7), (5);
+CREATE TABLE t3 (i3 int) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (7), (2), (9);
+set join_cache_level=3;
+set optimizer_switch='materialization=off,semijoin=off';
+EXPLAIN EXTENDED 
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	5	100.00	Using where
+2	DEPENDENT SUBQUERY	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	
+2	DEPENDENT SUBQUERY	t3	hash_ALL	NULL	#hash#$hj	5	func	3	100.00	Using where; Using join buffer (flat, BNLH join)
+Warnings:
+Note	1003	select `test`.`t1`.`i1` AS `i1` from `test`.`t1` where <expr_cache><`test`.`t1`.`i1`>(<in_optimizer>(`test`.`t1`.`i1`,<exists>(select `test`.`t3`.`i3` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`i3` = `test`.`t2`.`i2`) and (<cache>(`test`.`t1`.`i1`) = `test`.`t3`.`i3`)))))
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+i1
+7
+set optimizer_switch='materialization=on,semijoin=on';
+EXPLAIN EXTENDED 
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	<subquery2>	ALL	distinct_key	NULL	NULL	NULL	3	100.00	
+1	PRIMARY	t1	hash_ALL	NULL	#hash#$hj	5	test.t2.i2	5	100.00	Using where; Using join buffer (flat, BNLH join)
+2	MATERIALIZED	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+2	MATERIALIZED	t3	hash_ALL	NULL	#hash#$hj	5	test.t2.i2	3	100.00	Using where; Using join buffer (flat, BNLH join)
+Warnings:
+Note	1003	select `test`.`t1`.`i1` AS `i1` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where ((`test`.`t3`.`i3` = `test`.`t2`.`i2`) and (`test`.`t1`.`i1` = `test`.`t2`.`i2`))
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+i1
+7
+EXPLAIN EXTENDED 
+SELECT * FROM t1 
+WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 0 AND i3 = i2 OR 1=2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	PRIMARY	<subquery2>	ALL	distinct_key	NULL	NULL	NULL	3	100.00	
+1	PRIMARY	t1	hash_ALL	NULL	#hash#$hj	5	test.t2.i2	5	100.00	Using where; Using join buffer (flat, BNLH join)
+2	MATERIALIZED	t2	ALL	NULL	NULL	NULL	NULL	2	100.00	Using where
+2	MATERIALIZED	t3	hash_ALL	NULL	#hash#$hj	5	test.t2.i2	3	100.00	Using where; Using join buffer (flat, BNLH join)
+Warnings:
+Note	1003	select `test`.`t1`.`i1` AS `i1` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where ((`test`.`t3`.`i3` = `test`.`t2`.`i2`) and (`test`.`t1`.`i1` = `test`.`t2`.`i2`) and (`test`.`t3`.`i3` > 0))
+SELECT * FROM t1 
+WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 0 AND i3 = i2 OR 1=2);
+i1
+7
+SELECT * FROM t1 
+WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 7 AND i3 = i2 OR 1=2);
+i1
+DROP TABLE t1,t2,t3;
+set join_cache_level= @save_join_cache_level;
+set optimizer_switch=@save_optimizer_switch;

=== modified file 'mysql-test/t/subselect_sj2_mat.test'
--- a/mysql-test/t/subselect_sj2_mat.test	2013-02-22 01:13:12 +0000
+++ b/mysql-test/t/subselect_sj2_mat.test	2013-02-25 03:16:11 +0000
@@ -219,3 +219,47 @@
 
 set optimizer_switch=@save_optimizer_switch;
 
+--echo #
+--echo # mdev-4177: materialization of a subquery whose WHERE condition is OR
+--echo # formula with two disjucts such that the second one is always false
+--echo #
+
+set @save_optimizer_switch=@@optimizer_switch;
+set @save_join_cache_level=@@join_cache_level;
+
+CREATE TABLE t1 (i1 int) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1), (7), (4), (8), (4);
+
+CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (7), (5);
+
+CREATE TABLE t3 (i3 int) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (7), (2), (9);
+
+set join_cache_level=3;
+
+set optimizer_switch='materialization=off,semijoin=off';
+
+EXPLAIN EXTENDED 
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+
+set optimizer_switch='materialization=on,semijoin=on';
+
+EXPLAIN EXTENDED 
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 = i2 OR 1=2);
+
+EXPLAIN EXTENDED 
+SELECT * FROM t1 
+  WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 0 AND i3 = i2 OR 1=2);
+SELECT * FROM t1 
+  WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 0 AND i3 = i2 OR 1=2);
+SELECT * FROM t1 
+  WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 7 AND i3 = i2 OR 1=2);
+
+DROP TABLE t1,t2,t3;
+
+set join_cache_level= @save_join_cache_level;
+set optimizer_switch=@save_optimizer_switch;
+

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2013-02-22 01:13:12 +0000
+++ b/sql/item_cmpfunc.cc	2013-02-25 03:16:11 +0000
@@ -5491,7 +5491,7 @@
 }
 
 
-/*
+/**
   @brief
   Add a constant item to the Item_equal object
 
@@ -5537,6 +5537,7 @@
     const_item_cache= 1;
 }
 
+
 /**
   @brief
   Check whether a field is referred to in the multiple equality
@@ -5605,6 +5606,87 @@
 
 /**
   @brief
+  Merge members of another Item_equal object into this one
+  
+  @param item    multiple equality whose members are to be merged
+
+  @details
+  If the Item_equal 'item' happened to have some elements of the list
+  of equal items belonging to 'this' object then the function merges
+  the equal items from 'item' into this list.
+  If both lists contains constants and they are different then
+  the value of the cond_false flag is set to TRUE.
+
+  @retval
+    1    the lists of equal items in 'item' and 'this' contain common elements 
+  @retval
+    0    otherwise 
+
+  @notes
+  The method 'merge' just joins the list of equal items belonging to 'item'
+  to the list of equal items belonging to this object assuming that the lists
+  are disjoint. It would be more correct to call the method 'join'.
+  The method 'merge_with_check' really merges two lists of equal items if they
+  have common members.  
+*/
+  
+bool Item_equal::merge_with_check(Item_equal *item)
+{
+  bool intersected= FALSE;
+  Item_equal_fields_iterator_slow fi(*this);
+  while (fi++)
+  {
+    if (item->contains(fi.get_curr_field()))
+    {
+      fi.remove();
+      intersected= TRUE;
+    }
+  }
+  if (intersected)
+    item->merge(this);
+  return intersected;
+}
+
+
+/**
+  @brief
+  Merge this object into a list of Item_equal objects 
+  
+  @param list   the list of Item_equal objects to merge into
+
+  @details
+  If the list of equal items from 'this' object contains common members
+  with the lists of equal items belonging to Item_equal objects from 'list'
+  then all involved Item_equal objects e1,...,ek are merged into one 
+  Item equal that replaces e1,...,ek in the 'list'. Otherwise this
+  Item_equal is joined to the 'list'.
+*/
+
+void Item_equal::merge_into_list(List<Item_equal> *list)
+{
+  Item_equal *item;
+  List_iterator<Item_equal> it(*list);
+  Item_equal *merge_into= NULL;
+  while((item= it++))
+  {
+    if (!merge_into)
+    {
+      if (merge_with_check(item))
+        merge_into= item;
+    }
+    else
+    {
+      if (item->merge_with_check(merge_into))
+        it.remove();
+    }
+  }
+  if (!merge_into)
+    list->push_back(this);
+}
+
+
+/**
+  @brief
   Order equal items of the  multiple equality according to a sorting criteria
 
   @param compare      function to compare items from the equal_items list

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2013-02-22 01:13:12 +0000
+++ b/sql/item_cmpfunc.h	2013-02-25 03:16:11 +0000
@@ -1612,6 +1612,7 @@
   bool eval_not_null_tables(uchar *opt_arg);
 };
 
+template <template<class> class LI, class T> class Item_equal_iterator;
 
 /*
   The class Item_equal is used to represent conjunctions of equality
@@ -1760,6 +1761,8 @@
   /** Get number of field items / references to field items in this object */   
   uint n_field_items() { return equal_items.elements-test(with_const); }
   void merge(Item_equal *item);
+  bool merge_with_check(Item_equal *equal_item);
+  void merge_into_list(List<Item_equal> *list);
   void update_const();
   enum Functype functype() const { return MULT_EQUAL_FUNC; }
   longlong val_int(); 
@@ -1775,7 +1778,8 @@
   CHARSET_INFO *compare_collation();
 
   void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
-  friend class Item_equal_fields_iterator;
+  friend class Item_equal_iterator<List_iterator_fast,Item>;
+  friend class Item_equal_iterator<List_iterator,Item>;
   friend Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
                            Item_equal *item_equal);
   friend bool setup_sj_materialization_part1(struct st_join_table *tab);
@@ -1798,39 +1802,42 @@
 
 
 /* 
-  The class Item_equal_fields_iterator is used to iterate over references
-  to table/view columns from a list of equal items.
+  The template Item_equal_iterator is used to define classes
+  Item_equal_fields_iterator and Item_equal_fields_iterator_slow.
+  These are helper classes for the class Item equal
+  Both classes are used to iterate over references to table/view columns
+  from the list of equal items that included in an Item_equal object. 
+  The second class supports the operation of removal of the current member
+  from the list when performing an iteration.
 */ 
 
-class Item_equal_fields_iterator : public List_iterator_fast<Item>
+template <template<class> class LI, class T> class Item_equal_iterator
+  : public LI<T>
 {
+protected:
   Item_equal *item_equal;
   Item *curr_item;
 public:
-  Item_equal_fields_iterator(Item_equal &item_eq) 
-    :List_iterator_fast<Item> (item_eq.equal_items)
+  Item_equal_iterator<LI,T>(Item_equal &item_eq) 
+    :LI<T> (item_eq.equal_items)
   {
     curr_item= NULL;
     item_equal= &item_eq;
     if (item_eq.with_const)
     {
-      List_iterator_fast<Item> *list_it= this;
+      LI<T> *list_it= this;
       curr_item=  (*list_it)++;
     }
   }
   Item* operator++(int)
   { 
-    List_iterator_fast<Item> *list_it= this;
+    LI<T> *list_it= this;
     curr_item= (*list_it)++;
     return curr_item;
   }
-  Item ** ref()
-  {
-    return List_iterator_fast<Item>::ref();
-  }
   void rewind(void) 
   { 
-    List_iterator_fast<Item> *list_it= this;
+    LI<T> *list_it= this;
     list_it->rewind();
     if (item_equal->with_const)
       curr_item= (*list_it)++;
@@ -1843,6 +1850,34 @@
 };
 
 
+class Item_equal_fields_iterator
+  :public Item_equal_iterator<List_iterator_fast,Item >
+{
+public:
+  Item_equal_fields_iterator(Item_equal &item_eq) 
+    :Item_equal_iterator(item_eq)
+  { }
+  Item ** ref()
+  {
+    return List_iterator_fast<Item>::ref();
+  }
+};
+
+
+class Item_equal_fields_iterator_slow
+  :public Item_equal_iterator<List_iterator,Item >
+{
+public:
+  Item_equal_fields_iterator_slow(Item_equal &item_eq) 
+    :Item_equal_iterator(item_eq)
+  { }
+  void remove()
+  {
+    List_iterator<Item>::remove();
+  }
+};
+
+
 class Item_cond_and :public Item_cond
 {
 public:

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2013-02-22 01:13:12 +0000
+++ b/sql/sql_select.cc	2013-02-25 03:16:11 +0000
@@ -13159,7 +13159,61 @@
 	li.remove();
       else if (item != new_item)
       {
-	VOID(li.replace(new_item));
+        if (and_level)
+	{
+          /*
+            Take a special care of multiple equality predicates
+            that may be part of 'cond' and 'new_item'.
+            Those multiple equalities that have common members
+            must be merged.
+	  */  
+          Item_cond_and *cond_and= (Item_cond_and *) cond;
+          List<Item_equal> *cond_equal_items=
+            &cond_and->cond_equal.current_level;
+          List<Item> *cond_and_list= cond_and->argument_list();
+
+          if (new_item->type() == Item::COND_ITEM && 
+              ((Item_cond*) new_item)->functype() == Item_func::COND_AND_FUNC)
+          {
+            Item_cond_and *new_item_and= (Item_cond_and *) new_item;
+            List<Item_equal> *new_item_equal_items=
+              &new_item_and->cond_equal.current_level;
+            List<Item> *new_item_and_list= new_item_and->argument_list();
+            cond_and_list->disjoin((List<Item>*) cond_equal_items);
+            new_item_and_list->disjoin((List<Item>*) new_item_equal_items);
+            Item_equal *equal_item;
+            List_iterator<Item_equal> it(*new_item_equal_items);
+	    while ((equal_item= it++))
+	    {
+              equal_item->merge_into_list(cond_equal_items);
+            }
+            if (new_item_and_list->is_empty())
+              li.remove();
+            else
+              li.replace(*new_item_and_list);
+            cond_and_list->concat((List<Item>*) cond_equal_items); 
+          }
+          else if (new_item->type() == Item::FUNC_ITEM && 
+                   ((Item_cond*) new_item)->functype() ==
+                   Item_func::MULT_EQUAL_FUNC)
+	  {
+            cond_and_list->disjoin((List<Item>*) cond_equal_items);
+            ((Item_equal *) new_item)->merge_into_list(cond_equal_items);
+            li.remove();
+            cond_and_list->concat((List<Item>*) cond_equal_items); 
+          }
+          else
+            li.replace(new_item);
+        }
+        else
+	{ 
+          if (new_item->type() == Item::COND_ITEM &&
+              ((Item_cond*) new_item)->functype() == 
+              ((Item_cond*) cond)->functype())
+            li.replace(*((Item_cond*) new_item)->argument_list());
+	  else
+            li.replace(new_item);
+        } 
 	should_fix_fields=1;
       }
       if (*cond_value == Item::COND_UNDEF)



More information about the commits mailing list