List:Commits« Previous MessageNext Message »
From:Guilhem Bichot Date:May 4 2010 8:14pm
Subject:bzr commit into mysql-6.0-codebase-bugfixing branch (guilhem:3863) Bug#53298
View as plain text  
#At file:///home/mysql_src/bzrrepos/mysql-6.0-codebase-bugfixing2/ based on revid:alik@ibmvm-20100504122520-kzqe0rufxb054x9s

 3863 Guilhem Bichot	2010-05-04
      Fix for BUG#53298 "wrong result with semijoin (no semijoin strategy chosen)".
      Reviewers: please see questions marked with "QQ"
     @ mysql-test/r/subselect3.result
        fix causes slight plan change
     @ mysql-test/r/subselect4.result
        before the code fix, the final select would return two rows (1 and 1).
     @ mysql-test/r/subselect_sj2.result
        Before the code fix, EXPLAIN showed a wrong plan: adding two Australian towns to table t1, each of them
        having a population greater than 5MB, led the SELECT to return Australia twice (duplicate);
        after this fix, Australia would be returned once.
     @ mysql-test/t/subselect4.test
        test for bug
     @ sql/sql_select.cc
        In the bug's scenario, t3 and t4 are inner tables of the semijoin. t4 is pulled out
        (functionally dependent), but t3 is not. As we thus have one inner table left,
        we need a semijoin strategy; without semijoin strategy, the semijoin just becomes
        a plain join, leading to duplicate rows. Why didn't we have a semijoin strategy:
        - plan search would first choose this order: t4,t3,t1,t2, with duplicate weedout
        as semijoin strategy. As this handles the semijoin fully, join->cur_dups_producing_tables
        is 0 (as a result of choosing duplicates weedout in advance_sj_state() for plan's last
        table t2).
        - then plan search evaluates plan t4,t3,t2,t1: it first computes its cost, then in
        avdance_sj_state(), as join->cur_dups_producing_tables is still 0 (the bug),
        it believes that semijoin inner tables have been handled already, so doesn't feel
        obliged to choose a semijoin strategy, so this plan has no strategy, and as its
        cost is lower than that of t4,t3,t1,t2, it is chosen. A plan without a strategy,
        thus duplicates.
        The fix: as in advance_sj_state() we modify certain JOIN members, we need to
        restore them when we back-track to other plans. backout_nj_sj_state() already
        restored cur_sj_inner_tables, but forgot to restore cur_dups_producing_tables.
        We here make a class which contains all that backout_nj_sj_state() should
        restore.

    modified:
      mysql-test/r/subselect3.result
      mysql-test/r/subselect3_jcl6.result
      mysql-test/r/subselect4.result
      mysql-test/r/subselect_sj2.result
      mysql-test/r/subselect_sj2_jcl6.result
      mysql-test/t/subselect4.test
      sql/sql_select.cc
      sql/sql_select.h
=== modified file 'mysql-test/r/subselect3.result'
--- a/mysql-test/r/subselect3.result	2010-04-28 19:42:00 +0000
+++ b/mysql-test/r/subselect3.result	2010-05-04 20:14:40 +0000
@@ -1063,8 +1063,8 @@ t22.a in (select t12.a from t11, t12 whe
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t11	ALL	NULL	NULL	NULL	NULL	8	Using where; Using temporary; Using filesort; Start materialize; Scan
 1	PRIMARY	t12	ALL	NULL	NULL	NULL	NULL	8	Using where; End materialize; Using join buffer
-1	PRIMARY	t21	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
 1	PRIMARY	t22	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
+1	PRIMARY	t21	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
 select t21.* from t21,t22 where t21.a = t22.a and 
 t22.a in (select t12.a from t11, t12 where t11.a in(255,256) and t11.a = t12.a and t11.c is null) and t22.c is null order by t21.a;
 a	b	c

=== modified file 'mysql-test/r/subselect3_jcl6.result'
--- a/mysql-test/r/subselect3_jcl6.result	2010-04-28 19:42:00 +0000
+++ b/mysql-test/r/subselect3_jcl6.result	2010-05-04 20:14:40 +0000
@@ -1067,8 +1067,8 @@ t22.a in (select t12.a from t11, t12 whe
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t11	ALL	NULL	NULL	NULL	NULL	8	Using where; Using temporary; Using filesort; Start materialize; Scan
 1	PRIMARY	t12	ALL	NULL	NULL	NULL	NULL	8	Using where; End materialize; Using join buffer
-1	PRIMARY	t21	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
 1	PRIMARY	t22	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
+1	PRIMARY	t21	ALL	NULL	NULL	NULL	NULL	26	Using where; Using join buffer
 select t21.* from t21,t22 where t21.a = t22.a and 
 t22.a in (select t12.a from t11, t12 where t11.a in(255,256) and t11.a = t12.a and t11.c is null) and t22.c is null order by t21.a;
 a	b	c

=== modified file 'mysql-test/r/subselect4.result'
--- a/mysql-test/r/subselect4.result	2010-04-28 14:55:48 +0000
+++ b/mysql-test/r/subselect4.result	2010-05-04 20:14:40 +0000
@@ -500,3 +500,28 @@ SUBQUERY1_t1.col_varchar) ) ) 
 ;
 pk
 drop table t1;
+#
+# BUG#53298 "wrong result with semijoin (no semijoin strategy chosen)"
+#
+create table t1 (uid int, fid int);
+insert into t1 values (1,1), (3,1);
+create table t2 (uid int, name varchar(128));
+insert into t2 values (1, "A"), (2, "B");
+create table t3 (uid int, fid int, index(uid));
+insert into t3 values (1,3), (1,3);
+create table t4 (uid int);
+insert into t4 values (3);
+explain select t2.uid from t2, t1 
+where t1.uid in (select t4.uid from t4, t3 where t3.uid=1 and t4.uid=t3.fid)
+and t2.uid=t1.fid;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	t4	system	NULL	NULL	NULL	NULL	1	
+1	PRIMARY	t3	ref	uid	uid	5	const	1	Using where; Materialize; Scan
+1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using join buffer
+1	PRIMARY	t1	ALL	NULL	NULL	NULL	NULL	2	Using where; Using join buffer
+select t2.uid from t2, t1 
+where t1.uid in (select t4.uid from t4, t3 where t3.uid=1 and t4.uid=t3.fid)
+and t2.uid=t1.fid;
+uid
+1
+drop table t1,t2,t3,t4;

=== modified file 'mysql-test/r/subselect_sj2.result'
--- a/mysql-test/r/subselect_sj2.result	2010-01-20 10:11:29 +0000
+++ b/mysql-test/r/subselect_sj2.result	2010-05-04 20:14:40 +0000
@@ -303,9 +303,9 @@ t2.Code IN (SELECT Country FROM t3 
 WHERE Language='English' AND Percentage > 10 AND
 t2.Population > 100000);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t1	range	Population,Country	Population	4	NULL	1	Using index condition; Using MRR
-1	PRIMARY	t3	eq_ref	PRIMARY,Percentage	PRIMARY	33	test.t1.Country,const	1	Using index condition; Using where
-1	PRIMARY	t2	eq_ref	PRIMARY,Population	PRIMARY	3	test.t3.Country	1	Using index condition; Using where
+1	PRIMARY	t1	range	Population,Country	Population	4	NULL	1	Using index condition; Using MRR; Materialize; Scan
+1	PRIMARY	t2	eq_ref	PRIMARY,Population	PRIMARY	3	test.t1.Country	1	Using where
+1	PRIMARY	t3	eq_ref	PRIMARY,Percentage	PRIMARY	33	test.t2.Code,const	1	Using index condition; Using where
 DROP TABLE t1,t2,t3;
 CREATE TABLE t1 (
 Code char(3) NOT NULL DEFAULT '',

=== modified file 'mysql-test/r/subselect_sj2_jcl6.result'
--- a/mysql-test/r/subselect_sj2_jcl6.result	2010-01-20 10:11:29 +0000
+++ b/mysql-test/r/subselect_sj2_jcl6.result	2010-05-04 20:14:40 +0000
@@ -307,9 +307,9 @@ t2.Code IN (SELECT Country FROM t3 
 WHERE Language='English' AND Percentage > 10 AND
 t2.Population > 100000);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	t1	range	Population,Country	Population	4	NULL	1	Using index condition; Using MRR
-1	PRIMARY	t3	eq_ref	PRIMARY,Percentage	PRIMARY	33	test.t1.Country,const	1	Using index condition; Using where; Using join buffer
-1	PRIMARY	t2	eq_ref	PRIMARY,Population	PRIMARY	3	test.t3.Country	1	Using index condition(BKA); Using where; Using join buffer
+1	PRIMARY	t1	range	Population,Country	Population	4	NULL	1	Using index condition; Using MRR; Materialize; Scan
+1	PRIMARY	t2	eq_ref	PRIMARY,Population	PRIMARY	3	test.t1.Country	1	Using where; Using join buffer
+1	PRIMARY	t3	eq_ref	PRIMARY,Percentage	PRIMARY	33	test.t2.Code,const	1	Using index condition(BKA); Using where; Using join buffer
 DROP TABLE t1,t2,t3;
 CREATE TABLE t1 (
 Code char(3) NOT NULL DEFAULT '',

=== modified file 'mysql-test/t/subselect4.test'
--- a/mysql-test/t/subselect4.test	2010-04-28 14:55:48 +0000
+++ b/mysql-test/t/subselect4.test	2010-05-04 20:14:40 +0000
@@ -465,3 +465,31 @@ WHERE ( 1, 2 ) IN ( SELECT SUBQUERY1_t1.
 ;
 
 drop table t1;
+
+--echo #
+--echo # BUG#53298 "wrong result with semijoin (no semijoin strategy chosen)"
+--echo #
+
+create table t1 (uid int, fid int);
+insert into t1 values (1,1), (3,1);
+
+create table t2 (uid int, name varchar(128));
+insert into t2 values (1, "A"), (2, "B");
+
+create table t3 (uid int, fid int, index(uid));
+insert into t3 values (1,3), (1,3);
+
+create table t4 (uid int);
+insert into t4 values (3);
+
+explain select t2.uid from t2, t1 
+  where t1.uid in (select t4.uid from t4, t3 where t3.uid=1 and t4.uid=t3.fid)
+        and t2.uid=t1.fid;
+
+--sorted_result
+select t2.uid from t2, t1 
+  where t1.uid in (select t4.uid from t4, t3 where t3.uid=1 and t4.uid=t3.fid)
+        and t2.uid=t1.fid;
+
+drop table t1,t2,t3,t4;
+

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2010-04-29 12:15:06 +0000
+++ b/sql/sql_select.cc	2010-05-04 20:14:40 +0000
@@ -141,9 +141,9 @@ static 
 void advance_sj_state(JOIN *join, const table_map remaining_tables, 
                       const JOIN_TAB *new_join_tab, uint idx, 
                       double *current_record_count, double *current_read_time,
-                      POSITION *loose_scan_pos);
-static void backout_nj_sj_state(const table_map remaining_tables,
-                                const JOIN_TAB *tab);
+                      POSITION *loose_scan_pos, JOIN::sj_maps *save_sj_maps);
+static void backout_nj_sj_state(const JOIN_TAB *tab,
+                                const JOIN::sj_maps *save_sj_maps);
 
 static COND *optimize_cond(JOIN *join, COND *conds,
                            List<TABLE_LIST> *join_list,
@@ -6223,7 +6223,7 @@ public:
         s->emb_sj_nest->sj_in_exprs < 64 && 
         ((remaining_tables & s->emb_sj_nest->sj_inner_tables) ==        // (2)
          s->emb_sj_nest->sj_inner_tables) &&                            // (2)
-        join->cur_sj_inner_tables == 0 &&                                  // (3)
+        join->m_sj_maps.cur_sj_inner_tables == 0 &&                     // (3)
         !(remaining_tables & 
           s->emb_sj_nest->nested_join->sj_corr_tables) &&               // (4)
         remaining_tables & s->emb_sj_nest->nested_join->sj_depends_on &&// (5)
@@ -7009,8 +7009,14 @@ choose_plan(JOIN *join, table_map join_t
   bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN);
   DBUG_ENTER("choose_plan");
 
+  /*
+    QQ I suspect that JOIN::cur_embedding_map should eventually migrate to
+    JOIN::m_sj_maps (it is inited here, it is restored in
+    backout_nj_sj_state(), looks familiar) but I don't understand enough of
+    nested joins to do it now, I can mark it @todo.
+  */
   join->cur_embedding_map= 0;
-  join->cur_dups_producing_tables= 0;
+  join->m_sj_maps.reset();
   reset_nj_counters(join->join_list);
   qsort2_cmp jtab_sort_func;
 
@@ -7036,7 +7042,6 @@ choose_plan(JOIN *join, table_map join_t
   my_qsort2(join->best_ref + join->const_tables,
             join->tables - join->const_tables, sizeof(JOIN_TAB*),
             jtab_sort_func, (void*)join->emb_sjm_nest);
-  join->cur_sj_inner_tables= 0;
 
   if (straight_join)
   {
@@ -7269,8 +7274,9 @@ optimize_straight_join(JOIN *join, table
     /* compute the cost of the new plan extended with 's' */
     record_count*= join->positions[idx].records_read;
     read_time+=    join->positions[idx].read_time;
+    JOIN::sj_maps dummy;
     advance_sj_state(join, join_tables, s, idx, &record_count, &read_time,
-                     &loose_scan_pos);
+                     &loose_scan_pos, &dummy);
 
     join_tables&= ~(s->table->map);
     ++idx;
@@ -7743,8 +7749,10 @@ best_extension_by_limited_search(JOIN   
       current_record_count= record_count * position->records_read;
       current_read_time=    read_time + position->read_time;
 
+      JOIN::sj_maps save_sj_maps, *save_sj_maps_ptr;
       if (has_sj)
       {
+        save_sj_maps_ptr= &save_sj_maps;
         /*
           Even if there are no semijoins, advance_sj_state() has a significant
           cost (takes 9% of time in a 20-table plan search), hence the if()
@@ -7753,10 +7761,13 @@ best_extension_by_limited_search(JOIN   
         */
         advance_sj_state(join, remaining_tables, s, idx,
                          &current_record_count, &current_read_time,
-                         &loose_scan_pos);
+                         &loose_scan_pos, save_sj_maps_ptr);
       }
       else
+      {
+        save_sj_maps_ptr= NULL;
         join->positions[idx].sj_strategy= SJ_OPT_NONE;
+      }
 
       /* Expand only partial plans with lower cost than the best QEP so far */
       if ((current_read_time +
@@ -7769,7 +7780,7 @@ best_extension_by_limited_search(JOIN   
                                         current_record_count / 
                                         (double) TIME_FOR_COMPARE),
                                        "prune_by_cost"););
-        backout_nj_sj_state(remaining_tables, s);
+        backout_nj_sj_state(s, save_sj_maps_ptr);
         continue;
       }
 
@@ -7801,7 +7812,7 @@ best_extension_by_limited_search(JOIN   
                                          read_time,
                                          current_read_time,
                                          "pruned_by_heuristic"););
-          backout_nj_sj_state(remaining_tables, s);
+          backout_nj_sj_state(s, save_sj_maps_ptr);
           continue;
         }
       }
@@ -7842,7 +7853,7 @@ best_extension_by_limited_search(JOIN   
                                        current_read_time,
                                        "full_plan"););
       }
-      backout_nj_sj_state(remaining_tables, s);
+      backout_nj_sj_state(s, save_sj_maps_ptr);
     }
   }
   DBUG_RETURN(FALSE);
@@ -7907,8 +7918,9 @@ find_best(JOIN *join,table_map rest_tabl
       */
       double current_record_count=record_count*records;
       double current_read_time=read_time+best;
+      JOIN::sj_maps save_sj_maps;
       advance_sj_state(join, rest_tables, s, idx, &current_record_count, 
-                       &current_read_time, &loose_scan_pos);
+                       &current_read_time, &loose_scan_pos, &save_sj_maps);
 
       if (best_record_count > current_record_count ||
 	  best_read_time > current_read_time ||
@@ -7927,7 +7939,7 @@ find_best(JOIN *join,table_map rest_tabl
           DBUG_RETURN(TRUE);
 	swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
       }
-      backout_nj_sj_state(rest_tables, s);
+      backout_nj_sj_state(s, &save_sj_maps);
       if (join->select_options & SELECT_STRAIGHT_JOIN)
 	break;				// Don't test all combinations
     }
@@ -8205,7 +8217,7 @@ static void fix_semijoin_strategies_for_
         rem_tables |= join->best_positions[i].table->table->map;
 
       POSITION dummy;
-      join->cur_sj_inner_tables= 0;
+      join->m_sj_maps.cur_sj_inner_tables= 0;
       for (i= first + sjm->tables; i <= tablenr; i++)
       {
         best_access_path(join, join->best_positions[i].table, rem_tables, i, FALSE,
@@ -8233,7 +8245,7 @@ static void fix_semijoin_strategies_for_
         Re-run best_access_path to produce best access methods that do not use
         join buffering
       */ 
-      join->cur_sj_inner_tables= 0;
+      join->m_sj_maps.cur_sj_inner_tables= 0;
       for (idx= first; idx <= tablenr; idx++)
       {
         if (join->best_positions[idx].use_join_buffer)
@@ -8262,7 +8274,7 @@ static void fix_semijoin_strategies_for_
         Re-run best_access_path to produce best access methods that do not use
         join buffering
       */ 
-      join->cur_sj_inner_tables= 0;
+      join->m_sj_maps.cur_sj_inner_tables= 0;
       for (idx= first; idx <= tablenr; idx++)
       {
         if (join->best_positions[idx].use_join_buffer || (idx == first))
@@ -13285,6 +13297,9 @@ void optimize_wo_join_buffering(JOIN *jo
       loose_scan_pos       IN     A POSITION with LooseScan plan to access 
                                   table new_join_tab
                                   (produced by the last best_access_path call)
+      save_sj_maps         OUT    A backup copy of join's
+                                  m_sj_maps will be put there before doing
+                                  changes
 
   DESCRIPTION
     Update semi-join optimization state after we've added another tab (table 
@@ -13322,10 +13337,19 @@ static 
 void advance_sj_state(JOIN *join, table_map remaining_tables, 
                       const JOIN_TAB *new_join_tab, uint idx, 
                       double *current_record_count, double *current_read_time, 
-                      POSITION *loose_scan_pos)
+                      POSITION *loose_scan_pos,
+                      JOIN::sj_maps *save_sj_maps)
 {
   TABLE_LIST *emb_sj_nest;
   POSITION *pos= join->positions + idx;
+  /*
+    QQ compared to writing one assignment per member, this prevents
+    a grep like 'grep cur_sj_inner_tables *' from being reliable:
+    such grep wouldn't find the copy below. On the other hand it looks more
+    "encapsulated", and I imagine the grep-er would think about grepping for
+    m_sj_maps too?
+  */
+  *save_sj_maps= join->m_sj_maps;
   remaining_tables &= ~new_join_tab->table->map;
 
   pos->prefix_cost.convert_from_cost(*current_read_time);
@@ -13389,7 +13413,7 @@ void advance_sj_state(JOIN *join, table_
           are in the join prefix
        4. All inner tables are still part of remaining_tables.
     */
-    if (!join->cur_sj_inner_tables &&              // (2)
+    if (!join->m_sj_maps.cur_sj_inner_tables &&    // (2)
         !(remaining_tables & outer_corr_tables) && // (3)
         (sj_inner_tables ==                        // (4)
          ((remaining_tables | new_join_tab->table->map) & sj_inner_tables)))
@@ -13519,15 +13543,15 @@ void advance_sj_state(JOIN *join, table_
   */
   if ((emb_sj_nest= new_join_tab->emb_sj_nest))
   {
-    join->cur_sj_inner_tables |= emb_sj_nest->sj_inner_tables;
-    join->cur_dups_producing_tables |= emb_sj_nest->sj_inner_tables;
+    join->m_sj_maps.cur_sj_inner_tables |= emb_sj_nest->sj_inner_tables;
+    join->m_sj_maps.cur_dups_producing_tables |= emb_sj_nest->sj_inner_tables;
 
     /* Remove the sj_nest if all of its SJ-inner tables are in cur_table_map */
     if (!(remaining_tables &
           emb_sj_nest->sj_inner_tables & ~new_join_tab->table->map))
-      join->cur_sj_inner_tables &= ~emb_sj_nest->sj_inner_tables;
+      join->m_sj_maps.cur_sj_inner_tables &= ~emb_sj_nest->sj_inner_tables;
   }
-  join->cur_dups_producing_tables &= ~handled_by_fm_or_ls;
+  join->m_sj_maps.cur_dups_producing_tables &= ~handled_by_fm_or_ls;
 
   /* 4. MaterializeLookup and MaterializeScan strategy handler */
   const int sjm_strategy=
@@ -13581,7 +13605,8 @@ void advance_sj_state(JOIN *join, table_
     mat_read_time += mat_info->materialization_cost.total_cost() +
                      prefix_rec_count * mat_info->lookup_cost.total_cost();
 
-    if (mat_read_time < *current_read_time || join->cur_dups_producing_tables)
+    if (mat_read_time < *current_read_time ||
+        join->m_sj_maps.cur_dups_producing_tables)
     {
       /*
         NOTE: When we pick to use SJM[-Scan] we don't memcpy its POSITION
@@ -13592,7 +13617,7 @@ void advance_sj_state(JOIN *join, table_
       pos->sj_strategy= SJ_OPT_MATERIALIZE_LOOKUP;
       *current_read_time=    mat_read_time;
       *current_record_count= prefix_rec_count;
-      join->cur_dups_producing_tables&=
+      join->m_sj_maps.cur_dups_producing_tables &=
         ~new_join_tab->emb_sj_nest->sj_inner_tables;
     }
   }
@@ -13648,12 +13673,13 @@ void advance_sj_state(JOIN *join, table_
       comparing cost without semi-join duplicate removal with cost with
       duplicate removal is not an apples-to-apples comparison.
     */
-    if (prefix_cost < *current_read_time || join->cur_dups_producing_tables)
+    if (prefix_cost < *current_read_time ||
+        join->m_sj_maps.cur_dups_producing_tables)
     {
       pos->sj_strategy= SJ_OPT_MATERIALIZE_SCAN;
       *current_read_time=    prefix_cost;
       *current_record_count= prefix_rec_count;
-      join->cur_dups_producing_tables&= ~mat_nest->sj_inner_tables;
+      join->m_sj_maps.cur_dups_producing_tables &= ~mat_nest->sj_inner_tables;
 
     }
   }
@@ -13758,12 +13784,13 @@ void advance_sj_state(JOIN *join, table_
         to consider (it needs "the most" tables in the prefix) and we can't
         leave duplicate-producing tables not handled by any strategy.
       */
-      if (dups_cost < *current_read_time || join->cur_dups_producing_tables)
+      if (dups_cost < *current_read_time ||
+          join->m_sj_maps.cur_dups_producing_tables)
       {
         pos->sj_strategy= SJ_OPT_DUPS_WEEDOUT;
         *current_read_time= dups_cost;
         *current_record_count= prefix_rec_count * sj_outer_fanout;
-        join->cur_dups_producing_tables &= ~dups_removed_fanout;
+        join->m_sj_maps.cur_dups_producing_tables &= ~dups_removed_fanout;
       }
     }
   }
@@ -13776,17 +13803,16 @@ void advance_sj_state(JOIN *join, table_
   order and update the nested joins counters and join->cur_embedding_map. It
   is ok to call this for the first table in join order (for which
   check_interleaving_with_nj() has not been called).
-  - advance_sj_state(): removes the last table from join->cur_sj_inner_tables
-  bitmap.
+  - advance_sj_state(): restores the join's m_sj_maps
 
-  @param remaining_tables remaining tables to optimize, assumed to not contain
-                          tab (@todo but this assumption is violated in practice)
   @param tab              join table to remove, assumed to be the last in
                           current partial join order.
+  @param save_sj_maps_ptr if not NULL, pointer to what to copy back into the
+                          join's m_sj_maps
 */
 
-static void backout_nj_sj_state(const table_map remaining_tables,
-                                const JOIN_TAB *tab)
+static void backout_nj_sj_state(const JOIN_TAB *tab,
+                                const JOIN::sj_maps *save_sj_maps_ptr)
 {
   /* Restore the nested join state */
   TABLE_LIST *last_emb= tab->table->pos_in_table_list->embedding;
@@ -13810,16 +13836,8 @@ static void backout_nj_sj_state(const ta
   }
 
   /* Restore the semijoin state */
-  TABLE_LIST *emb_sj_nest;
-  if ((emb_sj_nest= tab->emb_sj_nest))
-  {
-    /* If we're removing the last SJ-inner table, remove the sj-nest */
-    if ((remaining_tables & emb_sj_nest->sj_inner_tables) ==
-        (emb_sj_nest->sj_inner_tables & ~tab->table->map))
-    {
-      tab->join->cur_sj_inner_tables &= ~emb_sj_nest->sj_inner_tables;
-    }
-  }
+  if (save_sj_maps_ptr != NULL)
+    tab->join->m_sj_maps= *save_sj_maps_ptr;
 }
 
 

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2010-04-16 14:20:09 +0000
+++ b/sql/sql_select.h	2010-05-04 20:14:40 +0000
@@ -1509,26 +1509,33 @@ public:
   
   /* Current join optimization state */
   POSITION positions[MAX_TABLES+1];
-  
+
   /*
-    Bitmap of nested joins embedding the position at the end of the current 
+    Bitmap of nested joins embedding the position at the end of the current
     partial join (valid only during join optimizer run).
   */
   nested_join_map cur_embedding_map;
-  
-  /*
-    Bitmap of inner tables of semi-join nests that have a proper subset of
-    their tables in the current join prefix. That is, of those semi-join
-    nests that have their tables both in and outside of the join prefix.
-  */
-  table_map cur_sj_inner_tables;
-  
-  /*
-    Bitmap of semi-join inner tables that are in the join prefix and for
-    which there's no provision for how to eliminate semi-join duplicates
-    they produce.
-  */
-  table_map cur_dups_producing_tables;
+
+  /** semijoin-related state backed out by backout_nj_sj_state() */
+  class sj_maps
+  {
+public:
+    /*
+      Bitmap of inner tables of semi-join nests that have a proper subset of
+      their tables in the current join prefix. That is, of those semi-join
+      nests that have their tables both in and outside of the join prefix.
+    */
+    table_map cur_sj_inner_tables;
+
+    /*
+      Bitmap of semi-join inner tables that are in the join prefix and for
+      which there's no provision for how to eliminate semi-join duplicates
+      they produce.
+    */
+    table_map cur_dups_producing_tables;
+    void reset(void) { cur_sj_inner_tables= cur_dups_producing_tables= 0; }
+  };
+  sj_maps m_sj_maps;
 
   /* We also maintain a stack of join optimization states in * join->positions[] */
 /******* Join optimization state members end *******/


Attachment: [text/bzr-bundle] bzr/guilhem@mysql.com-20100504201440-tof0evsj73fpd3oc.bundle
Thread
bzr commit into mysql-6.0-codebase-bugfixing branch (guilhem:3863) Bug#53298Guilhem Bichot4 May
  • Re: bzr commit into mysql-6.0-codebase-bugfixing branch (guilhem:3863)Bug#53298Olav Sandstaa11 May
    • Re: bzr commit into mysql-6.0-codebase-bugfixing branch (guilhem:3863)Bug#53298Guilhem Bichot14 May