[Commits] Rev 3390: BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on in file:///home/psergey/dev2/5.3-look46/

Sergey Petrunya psergey at askmonty.org
Fri Jan 20 00:11:55 EET 2012


At file:///home/psergey/dev2/5.3-look46/

------------------------------------------------------------
revno: 3390
revision-id: psergey at askmonty.org-20120119221153-vrvfy11wccbr47ql
parent: psergey at askmonty.org-20120119194443-rxtmqwzmg4bpe88u
committer: Sergey Petrunya <psergey at askmonty.org>
branch nick: 5.3-look46
timestamp: Fri 2012-01-20 02:11:53 +0400
message:
  BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on
  - equality substitution code was geared towards processing WHERE/ON clauses.
    that is, it assumed that it was doing substitions on the code that 
     = wasn't attached to any particular join_tab yet
     = was going to be fed to make_join_select() which would take the condition
       apart and attach various parts of it to tables inside/outside semi-joins.
  - However, somebody added equality substition for ref access. That is, if 
    we have a ref access on TBL.key=expr, they would do equality substition in
    'expr'. This possibility wasn't accounted for.
  - Fixed equality substition code by adding a mode that does equality 
    substition under assumption that the processed expression will be 
    attached to a certain particular table TBL.
=== modified file 'mysql-test/r/subselect_sj_jcl6.result'
--- a/mysql-test/r/subselect_sj_jcl6.result	2012-01-08 10:43:14 +0000
+++ b/mysql-test/r/subselect_sj_jcl6.result	2012-01-19 22:11:53 +0000
@@ -2264,6 +2264,35 @@
 set optimizer_switch=@save_optimizer_switch;
 set join_cache_level=default;
 DROP TABLE t1,t2,t3;
+#
+# BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on
+#
+set @os_912513= @@optimizer_switch;
+set @jcl_912513= @@join_cache_level;
+SET optimizer_switch = 'semijoin=on,materialization=on,join_cache_hashed=on';
+SET join_cache_level = 3;
+CREATE TABLE t1 ( a INT, b INT, KEY(a) );
+INSERT INTO t1 VALUES
+(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7);
+CREATE TABLE t2 ( c INT );
+INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7);
+SELECT alias1.* FROM
+t1 AS alias1 INNER JOIN t1 AS alias2
+ON alias2.a = alias1.b
+WHERE alias1.b IN (
+SELECT a FROM t1, t2
+);
+a	b
+1	1
+2	2
+3	3
+4	4
+5	5
+6	6
+7	7
+DROP table t1, t2;
+set @@optimizer_switch= @os_912513;
+set @@join_cache_level= @jcl_912513;
 # End
 set join_cache_level=default;
 show variables like 'join_cache_level';

=== modified file 'mysql-test/t/subselect_sj_jcl6.test'
--- a/mysql-test/t/subselect_sj_jcl6.test	2011-12-15 22:26:59 +0000
+++ b/mysql-test/t/subselect_sj_jcl6.test	2012-01-19 22:11:53 +0000
@@ -88,6 +88,32 @@
 
 DROP TABLE t1,t2,t3;
 
+--echo #
+--echo # BUG#912513: Wrong result (missing rows) with join_cache_hashed+materialization+semijoin=on
+--echo #
+set @os_912513= @@optimizer_switch;
+set @jcl_912513= @@join_cache_level;
+SET optimizer_switch = 'semijoin=on,materialization=on,join_cache_hashed=on';
+SET join_cache_level = 3;
+
+CREATE TABLE t1 ( a INT, b INT, KEY(a) );
+INSERT INTO t1 VALUES
+  (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7);
+
+CREATE TABLE t2 ( c INT );
+INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7);
+
+SELECT alias1.* FROM
+    t1 AS alias1 INNER JOIN t1 AS alias2
+      ON alias2.a = alias1.b
+WHERE alias1.b IN (
+        SELECT a FROM t1, t2
+    );
+
+DROP table t1, t2;
+set @@optimizer_switch= @os_912513;
+set @@join_cache_level= @jcl_912513;
+
 --echo # End
 
 set join_cache_level=default;

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2011-12-11 09:34:44 +0000
+++ b/sql/item.cc	2012-01-19 22:11:53 +0000
@@ -5023,7 +5023,8 @@
 
 Item *Item_field::replace_equal_field(uchar *arg)
 {
-  if (item_equal && item_equal == (Item_equal *) arg)
+  REPLACE_EQUAL_FIELD_ARG* param= (REPLACE_EQUAL_FIELD_ARG*)arg;
+  if (item_equal && item_equal == param->item_equal)
   {
     Item *const_item= item_equal->get_const();
     if (const_item)
@@ -5033,7 +5034,8 @@
         return this;
       return const_item;
     }
-    Item_field *subst= (Item_field *)(item_equal->get_first(this));
+    Item_field *subst= 
+      (Item_field *)(item_equal->get_first(param->context_tab, this));
     if (subst)
       subst= (Item_field *) (subst->real_item());
     if (subst && !field->eq(subst->field))

=== modified file 'sql/item.h'
--- a/sql/item.h	2011-12-19 07:38:37 +0000
+++ b/sql/item.h	2012-01-19 22:11:53 +0000
@@ -440,6 +440,16 @@
 class sp_rcontext;
 
 
+class Item_equal;
+
+struct st_join_table* const NO_PARTICULAR_TAB= (struct st_join_table*)0x1;
+
+typedef struct replace_equal_field_arg 
+{
+  Item_equal *item_equal;
+  struct st_join_table *context_tab;
+} REPLACE_EQUAL_FIELD_ARG;
+
 class Settable_routine_parameter
 {
 public:
@@ -1153,6 +1163,7 @@
 
   virtual Item *equal_fields_propagator(uchar * arg) { return this; }
   virtual bool set_no_const_sub(uchar *arg) { return FALSE; }
+  /* arg points to REPLACE_EQUAL_FIELD_ARG object */
   virtual Item *replace_equal_field(uchar * arg) { return this; }
   /*
     Check if an expression value has allowed arguments, like DATE/DATETIME

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2011-12-11 09:34:44 +0000
+++ b/sql/item_cmpfunc.cc	2012-01-19 22:11:53 +0000
@@ -5716,7 +5716,7 @@
 
 void Item_equal::fix_length_and_dec()
 {
-  Item *item= get_first(NULL);
+  Item *item= get_first(NO_PARTICULAR_TAB, NULL);
   eval_item= cmp_item::get_comparator(item->cmp_type(), item,
                                       item->collation.collation);
 }
@@ -5816,7 +5816,7 @@
   @retval 0 if no field found.
 */
 
-Item* Item_equal::get_first(Item *field_item)
+Item* Item_equal::get_first(JOIN_TAB *context, Item *field_item)
 {
   Item_equal_fields_iterator it(*this);
   Item *item;
@@ -5844,7 +5844,11 @@
     in presense of SJM nests.
   */
 
-  TABLE_LIST *emb_nest= field->table->pos_in_table_list->embedding;
+  TABLE_LIST *emb_nest;
+  if (context != NO_PARTICULAR_TAB)
+    emb_nest= context->emb_sj_nest;
+  else
+    emb_nest= field->table->pos_in_table_list->embedding;
 
   if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used)
   {

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2011-12-11 09:34:44 +0000
+++ b/sql/item_cmpfunc.h	2012-01-19 22:11:53 +0000
@@ -1712,7 +1712,7 @@
   /** Add a non-constant item to the multiple equality */
   void add(Item *f) { equal_items.push_back(f); }
   bool contains(Field *field);
-  Item* get_first(Item *field);
+  Item* get_first(struct st_join_table *context, Item *field);
   /** 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);

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2012-01-18 11:31:20 +0000
+++ b/sql/sql_select.cc	2012-01-19 22:11:53 +0000
@@ -110,7 +110,8 @@
                                COND_EQUAL *inherited,
                                List<TABLE_LIST> *join_list,
                                COND_EQUAL **cond_equal_ref);
-static COND* substitute_for_best_equal_field(COND *cond,
+static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab,
+                                             COND *cond,
                                              COND_EQUAL *cond_equal,
                                              void *table_join_idx);
 static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
@@ -1225,7 +1226,8 @@
   */
   if (conds)
   {
-    conds= substitute_for_best_equal_field(conds, cond_equal, map2table);
+    conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds, 
+                                           cond_equal, map2table);
     conds->update_used_tables();
     DBUG_EXECUTE("where",
                  print_where(conds,
@@ -1242,7 +1244,8 @@
   {
     if (*tab->on_expr_ref)
     {
-      *tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref,
+      *tab->on_expr_ref= substitute_for_best_equal_field(NO_PARTICULAR_TAB,
+                                                         *tab->on_expr_ref,
                                                          tab->cond_equal,
                                                          map2table);
       (*tab->on_expr_ref)->update_used_tables();
@@ -1265,7 +1268,7 @@
         continue;
       COND_EQUAL *equals= tab->first_inner ? tab->first_inner->cond_equal : 
                                              cond_equal;
-      ref_item= substitute_for_best_equal_field(ref_item, equals, map2table);
+      ref_item= substitute_for_best_equal_field(tab, ref_item, equals, map2table);
       ref_item->update_used_tables();
       if (*ref_item_ptr != ref_item)
       {
@@ -11446,7 +11449,7 @@
   else
   {
     TABLE_LIST *emb_nest;
-    head= item_equal->get_first(NULL);
+    head= item_equal->get_first(NO_PARTICULAR_TAB, NULL);
     it++;
     if ((emb_nest= embedding_sjm(head)))
     {
@@ -11555,6 +11558,7 @@
   return cond;
 }
 
+
 /**
   Substitute every field reference in a condition by the best equal field
   and eliminate all multiple equality predicates.
@@ -11569,6 +11573,9 @@
     After this the function retrieves all other conjuncted
     predicates substitute every field reference by the field reference
     to the first equal field or equal constant if there are any.
+
+  @param context_tab     Join tab that 'cond' will be attached to, or 
+                         NO_PARTICULAR_TAB. See notes above.
   @param cond            condition to process
   @param cond_equal      multiple equalities to take into consideration
   @param table_join_idx  index to tables determining field preference
@@ -11579,11 +11586,37 @@
     new fields in multiple equality item of lower levels. We want
     the order in them to comply with the order of upper levels.
 
+    context_tab may be used to specify which join tab `cond` will be
+    attached to. There are two possible cases:
+
+    1. context_tab != NO_PARTICULAR_TAB
+       We're doing substitution for an Item which will be evaluated in the 
+       context of a particular item. For example, if the optimizer does a 
+       ref access on "tbl1.key= expr" then
+        = equality substitution will be perfomed on 'expr'
+        = it is known in advance that 'expr' will be evaluated when 
+          table t1 is accessed.
+       Note that in this kind of substution we never have to replace Item_equal
+       objects. For example, for
+
+        t.key= func(col1=col2 AND col2=const)
+       
+       we will not build Item_equal or do equality substution (if we decide to,
+       this function will need to be fixed to handle it)
+
+    2. context_tab == NO_PARTICULAR_TAB
+       We're doing substitution in WHERE/ON condition, which is not yet 
+       attached to any particular join_tab. We will use information about the
+       chosen join order to make "optimal" substitions, i.e. those that allow
+       to apply filtering as soon as possible. See eliminate_item_equal() and 
+       Item_equal::get_first() for details.
+
   @return
     The transformed condition
 */
 
-static COND* substitute_for_best_equal_field(COND *cond,
+static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab,
+                                             COND *cond,
                                              COND_EQUAL *cond_equal,
                                              void *table_join_idx)
 {
@@ -11612,7 +11645,8 @@
     Item *item;
     while ((item= li++))
     {
-      Item *new_item= substitute_for_best_equal_field(item, cond_equal,
+      Item *new_item= substitute_for_best_equal_field(context_tab,
+                                                      item, cond_equal,
                                                       table_join_idx);
       /*
         This works OK with PS/SP re-execution as changes are made to
@@ -11659,7 +11693,8 @@
       List_iterator_fast<Item_equal> it(cond_equal->current_level);
       while((item_equal= it++))
       {
-        cond= cond->transform(&Item::replace_equal_field, (uchar *) item_equal);
+        REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab};
+        cond= cond->transform(&Item::replace_equal_field, (uchar *) &arg);
       }
       cond_equal= cond_equal->upper_levels;
     }



More information about the commits mailing list