From 6adddca80ec5ec972067917c54465d051b7d1cf4 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 22 Jun 2011 01:57:28 +0400 Subject: [PATCH 1/4] Make semi-joins work with outer joins part #1: - Make make_outerjoin_info() correctly process semi-join nests - Make make_join_select() attach conditions to the right places. --- mysql-test/r/subselect_sj.result | 21 +++++ mysql-test/r/subselect_sj_jcl6.result | 21 +++++ mysql-test/r/subselect_sj_mat.result | 10 +-- mysql-test/t/subselect_sj.test | 20 +++++ sql/item_cmpfunc.h | 3 +- sql/opt_subselect.cc | 24 +++++- sql/opt_subselect.h | 4 +- sql/sql_select.cc | 120 ++++++++++++++++++-------- sql/sql_select.h | 10 ++- sql/table.cc | 4 +- sql/table.h | 1 + 11 files changed, 191 insertions(+), 47 deletions(-) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 364813030b2..51de4d6c2b3 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -1326,4 +1326,25 @@ x m c drop table t1,t2,t3,t4; +# +# BUG#795530 Wrong result with subquery semijoin materialization and outer join +# Simplified testcase that uses DuplicateElimination +# +create table t1 (a int); +create table t2 (a int, b char(10)); +insert into t1 values (1),(2); +insert into t2 values (1, 'one'), (3, 'three'); +create table t3 (b char(10)); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +explain select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Start temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where; End temporary; Using join buffer (flat, BNL join) +select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); +b +drop table t1, t2, t3; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index a3eca278db4..9e0ee0dc511 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -1334,6 +1334,27 @@ x m c drop table t1,t2,t3,t4; +# +# BUG#795530 Wrong result with subquery semijoin materialization and outer join +# Simplified testcase that uses DuplicateElimination +# +create table t1 (a int); +create table t2 (a int, b char(10)); +insert into t1 values (1),(2); +insert into t2 values (1, 'one'), (3, 'three'); +create table t3 (b char(10)); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +explain select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Start temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 8 Using where; End temporary; Using join buffer (incremental, BNL join) +select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); +b +drop table t1, t2, t3; set @@optimizer_switch=@save_optimizer_switch; # # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index f1e26561f4c..a3b19b38867 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -636,7 +636,7 @@ from t1_16 where a1 in (select substring(b1,1,16) from t2_16 where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_16 ALL NULL NULL NULL NULL 3 100.00 -1 PRIMARY eq_ref distinct_key distinct_key 20 func 1 100.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 20 func 1 100.00 2 SUBQUERY t2_16 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: Note 1003 select left(`test`.`t1_16`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_16`.`a2`,7) AS `left(a2,7)` from `test`.`t1_16` semi join (`test`.`t2_16`) where ((`test`.`t2_16`.`b1` > '0') and (`test`.`t1_16`.`a1` = substr(`test`.`t2_16`.`b1`,1,16))) @@ -751,7 +751,7 @@ from t1_512 where a1 in (select substring(b1,1,512) from t2_512 where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_512 ALL NULL NULL NULL NULL 3 100.00 -1 PRIMARY eq_ref distinct_key distinct_key 517 func 1 100.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 517 func 1 100.00 2 SUBQUERY t2_512 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: Note 1003 select left(`test`.`t1_512`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_512`.`a2`,7) AS `left(a2,7)` from `test`.`t1_512` semi join (`test`.`t2_512`) where ((`test`.`t2_512`.`b1` > '0') and (`test`.`t1_512`.`a1` = substr(`test`.`t2_512`.`b1`,1,512))) @@ -847,7 +847,7 @@ from t1_1024 where a1 in (select substring(b1,1,1024) from t2_1024 where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1024 ALL NULL NULL NULL NULL 3 100.00 -1 PRIMARY eq_ref NULL distinct_key 15 func,func 1 100.00 Using where +1 PRIMARY eq_ref NULL distinct_key 15 func,func 1 100.00 2 SUBQUERY t2_1024 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: Note 1003 select left(`test`.`t1_1024`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1024`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1024` semi join (`test`.`t2_1024`) where ((`test`.`t2_1024`.`b1` > '0') and (`test`.`t1_1024`.`a1` = substr(`test`.`t2_1024`.`b1`,1,1024))) @@ -941,7 +941,7 @@ from t1_1025 where a1 in (select substring(b1,1,1025) from t2_1025 where b1 > '0'); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1_1025 ALL NULL NULL NULL NULL 3 100.00 -1 PRIMARY eq_ref NULL distinct_key 15 func,func 1 100.00 Using where +1 PRIMARY eq_ref NULL distinct_key 15 func,func 1 100.00 2 SUBQUERY t2_1025 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: Note 1003 select left(`test`.`t1_1025`.`a1`,7) AS `left(a1,7)`,left(`test`.`t1_1025`.`a2`,7) AS `left(a2,7)` from `test`.`t1_1025` semi join (`test`.`t2_1025`) where ((`test`.`t2_1025`.`b1` > '0') and (`test`.`t1_1025`.`a1` = substr(`test`.`t2_1025`.`b1`,1,1025))) @@ -1219,7 +1219,7 @@ insert into t1 values ('aa', 'aaaa'); explain select a,b from t1 where b in (select a from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 -1 PRIMARY eq_ref distinct_key distinct_key 3 func 1 Using where +1 PRIMARY eq_ref distinct_key distinct_key 3 func 1 2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 select a,b from t1 where b in (select a from t1); a b diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 86ea719e8b0..407eeda9c1f 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -1212,5 +1212,25 @@ LEFT JOIN t2 JOIN t3 ON t3.f10 = t2.f10 ON t3.f11 != 0 ); drop table t1,t2,t3,t4; +--echo # +--echo # BUG#795530 Wrong result with subquery semijoin materialization and outer join +--echo # Simplified testcase that uses DuplicateElimination +--echo # +create table t1 (a int); +create table t2 (a int, b char(10)); + +insert into t1 values (1),(2); +insert into t2 values (1, 'one'), (3, 'three'); + +create table t3 (b char(10)); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +insert into t3 values('three'),( 'four'); +explain select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); +select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); + +drop table t1, t2, t3; + # The following command must be the last one the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 285367ba9d6..e587ffd0771 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1688,7 +1688,8 @@ public: friend class Item_equal_fields_iterator; friend Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, Item_equal *item_equal); - friend bool setup_sj_materialization(struct st_join_table *tab); + friend bool setup_sj_materialization_part1(struct st_join_table *tab); + friend bool setup_sj_materialization_part2(struct st_join_table *tab); }; class COND_EQUAL: public Sql_alloc diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 43fa4614ac1..9241a77b9d8 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2840,9 +2840,8 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) TRUE Error */ -bool setup_sj_materialization(JOIN_TAB *sjm_tab) +bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) { - uint i; DBUG_ENTER("setup_sj_materialization"); JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; @@ -2850,7 +2849,8 @@ bool setup_sj_materialization(JOIN_TAB *sjm_tab) THD *thd= tab->join->thd; /* First the calls come to the materialization function */ List &item_list= emb_sj_nest->sj_subq_pred->unit->first_select()->item_list; - + + DBUG_ASSERT(sjm->is_used); /* Set up the table to write to, do as select_union::create_result_table does */ @@ -2878,6 +2878,22 @@ bool setup_sj_materialization(JOIN_TAB *sjm_tab) sjm->materialized= FALSE; sjm_tab->table= sjm->table; + sjm->table->pos_in_table_list= emb_sj_nest;//???? psergey ??? + + DBUG_RETURN(FALSE); +} + + +bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) +{ + DBUG_ENTER("setup_sj_materialization_part2"); + JOIN_TAB *tab= sjm_tab->bush_children->start; + TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; + SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; + THD *thd= tab->join->thd; + uint i; + List &item_list= emb_sj_nest->sj_subq_pred->unit->first_select()->item_list; + List_iterator it(item_list); if (!sjm->is_sj_scan) { @@ -2992,7 +3008,7 @@ bool setup_sj_materialization(JOIN_TAB *sjm_tab) in the record buffers for the source tables. */ sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements]; - it.rewind(); + //it.rewind(); for (uint i=0; i < sjm->sjm_table_cols.elements; i++) { bool dummy; diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 4d89609e1a8..571fcbaa935 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -285,7 +285,9 @@ void restore_prev_sj_state(const table_map remaining_tables, const JOIN_TAB *tab, uint idx); void fix_semijoin_strategies_for_picked_join_order(JOIN *join); -bool setup_sj_materialization(JOIN_TAB *tab); + +bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab); +bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab); TABLE *create_duplicate_weedout_tmp_table(THD *thd, uint uniq_tuple_length_arg, SJ_TMP_TABLE *sjtbl); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 682a9b2a596..512b311c629 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -90,7 +90,7 @@ static store_key *get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, uchar *key_buff, uint maybe_null); -static void make_outerjoin_info(JOIN *join); +static bool make_outerjoin_info(JOIN *join); static Item* make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables, table_map sjm_tables); static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item); @@ -1164,7 +1164,10 @@ JOIN::optimize() } reset_nj_counters(this, join_list); - make_outerjoin_info(this); + if (make_outerjoin_info(this)) + { + DBUG_RETURN(1); + } /* Among the equal fields belonging to the same multiple equality @@ -7620,6 +7623,12 @@ static void add_not_null_conds(JOIN *join) nested outer join and so on until it reaches root_tab (root_tab can be 0). + In other words: + add_found_match_trig_cond(tab->first_inner_tab, y, 0) is the way one should + wrap parts of WHERE. The idea is that the part of WHERE should be only + evaluated after we've finished figuring out whether outer joins. + ^^^ is the above correct? + @param tab the first inner table for most nested outer join @param cond the predicate to be guarded (must be set) @param root_tab the first inner table to stop @@ -7647,6 +7656,12 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) } +bool TABLE_LIST::is_active_sjm() +{ + return sj_mat_info && sj_mat_info->is_used; +} + + /** Fill in outer join related info for the execution plan structure. @@ -7664,6 +7679,12 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) corresponding first inner table through the field t0->on_expr_ref. Here ti are structures of the JOIN_TAB type. + In other words, for each join tab, set + - first_inner + - last_inner + - first_upper + - on_expr_ref, cond_equal + EXAMPLE. For the query: @code SELECT * FROM t1 @@ -7689,20 +7710,33 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) has been chosen. */ -static void +static bool make_outerjoin_info(JOIN *join) { DBUG_ENTER("make_outerjoin_info"); - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; - tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) + + /* + Create temp. tables for merged SJ-Materialization nests. We need to do + this now, because further code relies on tab->table and + tab->table->pos_in_table_list being set. + */ + JOIN_TAB *tab; + for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { - TABLE *table=tab->table; - /* - psergey: The following is probably incorrect, fix it when we get - semi+outer joins processing to work: - */ - if (!table) - continue; + if (tab->bush_children) + { + if (setup_sj_materialization_part1(tab)) + DBUG_RETURN(TRUE); + tab->table->reginfo.join_tab= tab; + } + } + + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; + tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) + { + TABLE *table= tab->table; TABLE_LIST *tbl= table->pos_in_table_list; TABLE_LIST *embedding= tbl->embedding; @@ -7716,11 +7750,16 @@ make_outerjoin_info(JOIN *join) tab->last_inner= tab->first_inner= tab; tab->on_expr_ref= &tbl->on_expr; tab->cond_equal= tbl->cond_equal; - if (embedding) + if (embedding && !embedding->is_active_sjm()) tab->first_upper= embedding->nested_join->first_nested; } for ( ; embedding ; embedding= embedding->embedding) { + if (embedding->is_active_sjm()) + { + /* We're trying to walk out of an SJ-Materialization nest. Don't do this. */ + break; + } /* Ignore sj-nests: */ if (!(embedding->on_expr && embedding->outer_join)) continue; @@ -7752,7 +7791,7 @@ make_outerjoin_info(JOIN *join) } } } - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } @@ -8127,10 +8166,25 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) the null complemented row. */ - /* First push down constant conditions from on expressions */ - for (JOIN_TAB *join_tab= first_linear_tab(join, WITHOUT_CONST_TABLES); - join_tab; - join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS)) + /* + First push down constant conditions from ON expressions. + - Each pushed-down condition is wrapped into trigger which is + enabled only for non-NULL-complemented record + - The condition is attached to the first_inner_table. + + With regards to join nests: + - if we start at top level, don't walk into nests + - if we start inside a nest, stay within that nest. + */ + JOIN_TAB *start_from= tab->bush_root_tab? + tab->bush_root_tab->bush_children->start : + join->join_tab + join->const_tables; + JOIN_TAB *end_with= tab->bush_root_tab? + tab->bush_root_tab->bush_children->end : + join->join_tab + join->top_join_tab_count; + for (JOIN_TAB *join_tab= start_from; + join_tab != end_with; + join_tab++) { if (*join_tab->on_expr_ref) { @@ -8155,9 +8209,14 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } } + /* Push down non-constant conditions from ON expressions */ - //JOIN_TAB *first_tab= join->join_tab+join->const_tables; JOIN_TAB *last_tab= tab; + + /* + while we're inside of an outer join and last_tab is + the last of its tables ... + */ while (first_inner_tab && first_inner_tab->last_inner == last_tab) { /* @@ -8168,19 +8227,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) table_map used_tables2= (join->const_table_map | OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); - tab; - tab= (tab == last_tab)? NULL: next_linear_tab(join, tab, - WITH_BUSH_ROOTS)) + + start_from= tab->bush_root_tab? + tab->bush_root_tab->bush_children->start : + join->join_tab + join->const_tables; + for (JOIN_TAB *tab= start_from; tab <= last_tab; tab++) { - if (!tab->table) - { - /* - psergey-todo: this is probably incorrect, fix this when we get - correct processing for outer joins + semi joins - */ - continue; - } + DBUG_ASSERT(tab->table); current_map= tab->table->map; used_tables2|= current_map; /* @@ -9131,7 +9184,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) { if (tab->bush_children) { - if (setup_sj_materialization(tab)) + if (setup_sj_materialization_part2(tab)) return TRUE; } @@ -20538,8 +20591,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, examined_rows= tab->limit; else { - if (!tab->table->pos_in_table_list || - tab->table->is_filled_at_execution()) // temporary, is_filled_at_execution + if (tab->table->is_filled_at_execution()) { examined_rows= tab->records; } diff --git a/sql/sql_select.h b/sql/sql_select.h index abb9a98030e..ae21db849ff 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -204,7 +204,13 @@ typedef struct st_join_table { NULL means no index condition pushdown was performed. */ Item *pre_idx_push_select_cond; - Item **on_expr_ref; /**< pointer to the associated on expression */ + /* + Pointer to the associated ON expression. on_expr_ref=!NULL except for + degenerate joins. + *on_expr_ref!=NULL for tables that are first inner tables within an outer + join. + */ + Item **on_expr_ref; COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */ st_join_table *first_inner; /**< first inner table for including outerjoin */ bool found; /**< true after all matches or null complement */ @@ -478,6 +484,8 @@ typedef struct st_join_table { } double scan_time(); bool preread_init(); + + bool is_sjm_nest() { return test(bush_children); } } JOIN_TAB; diff --git a/sql/table.cc b/sql/table.cc index 8427ca6e112..f30ecaa3b7d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5408,9 +5408,11 @@ bool st_table::is_children_attached(void) bool st_table::is_filled_at_execution() { - return test(pos_in_table_list->jtbm_subselect); + return test(pos_in_table_list->jtbm_subselect || + pos_in_table_list->is_active_sjm()); } + /* Cleanup this table for re-execution. diff --git a/sql/table.h b/sql/table.h index 4205b81dc12..d57896dcbf6 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1765,6 +1765,7 @@ struct TABLE_LIST respectively. */ char *get_table_name() { return view != NULL ? view_name.str : table_name; } + bool is_active_sjm(); st_select_lex_unit *get_unit(); st_select_lex *get_single_select(); void wrap_into_nested_join(List &join_list); From 038be98979c643ff56deda415e03373ab4f2b0f1 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 22 Jun 2011 14:17:01 +0400 Subject: [PATCH 2/4] BUG#600958 RQG: Crash in optimize_semijoin_nests - Testcase. --- mysql-test/r/subselect_sj.result | 33 +++++++++++++++++++++++++ mysql-test/r/subselect_sj_jcl6.result | 34 +++++++++++++++++++++++++- mysql-test/t/subselect_sj.test | 35 ++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 51de4d6c2b3..080e911e7a1 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -1347,4 +1347,37 @@ id select_type table type possible_keys key key_len ref rows Extra select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); b drop table t1, t2, t3; +# +# BUG#600958 RQG: Crash in optimize_semijoin_nests +# +CREATE TABLE t1 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col_int_key int(11) DEFAULT NULL, +col_date_key date DEFAULT NULL, +col_varchar_key varchar(1) DEFAULT NULL, +PRIMARY KEY (pk), +KEY col_int_key (col_int_key), +KEY col_date_key (col_date_key), +KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES (10,8,'2002-02-21',NULL); +CREATE TABLE t2 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col_int_key int(11) DEFAULT NULL, +col_date_key date DEFAULT NULL, +col_varchar_key varchar(1) DEFAULT NULL, +PRIMARY KEY (pk), +KEY col_int_key (col_int_key), +KEY col_date_key (col_date_key), +KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; +INSERT INTO t2 VALUES (1,7,'1900-01-01','f'); +SELECT col_date_key FROM t1 +WHERE 5 IN ( +SELECT SUBQUERY3_t1 .col_int_key +FROM t2 SUBQUERY3_t1 +LEFT JOIN t1 SUBQUERY3_t2 ON SUBQUERY3_t1 .col_varchar_key +); +col_date_key +drop table t2, t1; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 9e0ee0dc511..4dee091bfcc 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -1355,7 +1355,39 @@ id select_type table type possible_keys key key_len ref rows Extra select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); b drop table t1, t2, t3; -set @@optimizer_switch=@save_optimizer_switch; +# +# BUG#600958 RQG: Crash in optimize_semijoin_nests +# +CREATE TABLE t1 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col_int_key int(11) DEFAULT NULL, +col_date_key date DEFAULT NULL, +col_varchar_key varchar(1) DEFAULT NULL, +PRIMARY KEY (pk), +KEY col_int_key (col_int_key), +KEY col_date_key (col_date_key), +KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES (10,8,'2002-02-21',NULL); +CREATE TABLE t2 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col_int_key int(11) DEFAULT NULL, +col_date_key date DEFAULT NULL, +col_varchar_key varchar(1) DEFAULT NULL, +PRIMARY KEY (pk), +KEY col_int_key (col_int_key), +KEY col_date_key (col_date_key), +KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; +INSERT INTO t2 VALUES (1,7,'1900-01-01','f'); +SELECT col_date_key FROM t1 +WHERE 5 IN ( +SELECT SUBQUERY3_t1 .col_int_key +FROM t2 SUBQUERY3_t1 +LEFT JOIN t1 SUBQUERY3_t2 ON SUBQUERY3_t1 .col_varchar_key +); +col_date_key +drop table t2, t1; # # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off # diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 407eeda9c1f..94bd2ff3f19 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -1229,8 +1229,41 @@ insert into t3 values('three'),( 'four'); insert into t3 values('three'),( 'four'); explain select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); select * from t3 where t3.b in (select t2.b from t1 left join t2 on t1.a=t2.a); - drop table t1, t2, t3; +--echo # +--echo # BUG#600958 RQG: Crash in optimize_semijoin_nests +--echo # +CREATE TABLE t1 ( + pk int(11) NOT NULL AUTO_INCREMENT, + col_int_key int(11) DEFAULT NULL, + col_date_key date DEFAULT NULL, + col_varchar_key varchar(1) DEFAULT NULL, + PRIMARY KEY (pk), + KEY col_int_key (col_int_key), + KEY col_date_key (col_date_key), + KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES (10,8,'2002-02-21',NULL); +CREATE TABLE t2 ( + pk int(11) NOT NULL AUTO_INCREMENT, + col_int_key int(11) DEFAULT NULL, + col_date_key date DEFAULT NULL, + col_varchar_key varchar(1) DEFAULT NULL, + PRIMARY KEY (pk), + KEY col_int_key (col_int_key), + KEY col_date_key (col_date_key), + KEY col_varchar_key (col_varchar_key,col_int_key) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; +INSERT INTO t2 VALUES (1,7,'1900-01-01','f'); + +SELECT col_date_key FROM t1 +WHERE 5 IN ( + SELECT SUBQUERY3_t1 .col_int_key + FROM t2 SUBQUERY3_t1 + LEFT JOIN t1 SUBQUERY3_t2 ON SUBQUERY3_t1 .col_varchar_key +); +drop table t2, t1; + # The following command must be the last one the file set @@optimizer_switch=@save_optimizer_switch; From eea95a15d3648fcf01a146813c6de398d08f5246 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 22 Jun 2011 14:33:11 +0400 Subject: [PATCH 3/4] Make semi-joins work with outer joins part #2: - Do make the DuplicateWeedout check for outer joins. --- mysql-test/r/subselect_sj.result | 37 ++++++++++++++++++++++++++ mysql-test/r/subselect_sj_jcl6.result | 38 +++++++++++++++++++++++++++ mysql-test/t/subselect_sj.test | 32 ++++++++++++++++++++++ sql/sql_select.cc | 11 ++++++++ 4 files changed, 118 insertions(+) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 080e911e7a1..47661961dc4 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -1380,4 +1380,41 @@ LEFT JOIN t1 SUBQUERY3_t2 ON SUBQUERY3_t1 .col_varchar_key ); col_date_key drop table t2, t1; +# +# No BUG#: Duplicate weedout check is not done for outer joins +# +create table t1 (a int); +create table t2 (a int); +insert into t1 values (1),(1),(2),(2); +insert into t2 values (1); +create table t0 (a int); +insert into t0 values (1),(2); +set @tmp_20110622= @@optimizer_switch; +set optimizer_switch='firstmatch=off,loosescan=off,materialization=off'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 Start temporary +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; End temporary +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 +# Try also without join buffer +set @tmp_jcl_20110622= @@join_cache_level; +set join_cache_level= 0; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Start temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; End temporary +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 +set @@join_cache_level=@tmp_jcl_20110622; +set @@optimizer_switch=@tmp_20110622; +drop table t0, t1, t2; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 4dee091bfcc..960ca6e1e06 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -1389,6 +1389,44 @@ LEFT JOIN t1 SUBQUERY3_t2 ON SUBQUERY3_t1 .col_varchar_key col_date_key drop table t2, t1; # +# No BUG#: Duplicate weedout check is not done for outer joins +# +create table t1 (a int); +create table t2 (a int); +insert into t1 values (1),(1),(2),(2); +insert into t2 values (1); +create table t0 (a int); +insert into t0 values (1),(2); +set @tmp_20110622= @@optimizer_switch; +set optimizer_switch='firstmatch=off,loosescan=off,materialization=off'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 Start temporary +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; End temporary; Using join buffer (incremental, BNL join) +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 +# Try also without join buffer +set @tmp_jcl_20110622= @@join_cache_level; +set join_cache_level= 0; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Start temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; End temporary +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 +set @@join_cache_level=@tmp_jcl_20110622; +set @@optimizer_switch=@tmp_20110622; +drop table t0, t1, t2; +set @@optimizer_switch=@save_optimizer_switch; +# # BUG#49129: Wrong result with IN-subquery with join_cache_level=6 and firstmatch=off # CREATE TABLE t0 (a INT); diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 94bd2ff3f19..4436373e2a9 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -1265,5 +1265,37 @@ WHERE 5 IN ( ); drop table t2, t1; + +--echo # +--echo # No BUG#: Duplicate weedout check is not done for outer joins +--echo # +create table t1 (a int); +create table t2 (a int); + +insert into t1 values (1),(1),(2),(2); +insert into t2 values (1); + +create table t0 (a int); +insert into t0 values (1),(2); + +set @tmp_20110622= @@optimizer_switch; +set optimizer_switch='firstmatch=off,loosescan=off,materialization=off'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); + +--echo # Try also without join buffer +set @tmp_jcl_20110622= @@join_cache_level; +set join_cache_level= 0; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); + +set @@join_cache_level=@tmp_jcl_20110622; +set @@optimizer_switch=@tmp_20110622; + +drop table t0, t1, t2; + + # The following command must be the last one the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 512b311c629..81413fda964 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -15211,6 +15211,17 @@ evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab) /* The row complemented by nulls satisfies all conditions attached to inner tables. + */ + if (join_tab->check_weed_out_table) + { + int res= do_sj_dups_weedout(join->thd, join_tab->check_weed_out_table); + if (res == -1) + return NESTED_LOOP_ERROR; + else if (res == 1) + return NESTED_LOOP_OK; + } + + /* Send the row complemented by nulls to be joined with the remaining tables. */ From ed54ec7eb5ee5747293155f50752a47c2c3f1ec7 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 22 Jun 2011 15:22:27 +0400 Subject: [PATCH 4/4] Make semi-joins work with outer joins: - evaluate_null_complemented_join_record() should perform FirstMatch checks. --- mysql-test/r/subselect_sj.result | 11 +++++++++++ mysql-test/r/subselect_sj_jcl6.result | 11 +++++++++++ mysql-test/t/subselect_sj.test | 6 ++++++ sql/sql_select.cc | 9 +++++++++ 4 files changed, 37 insertions(+) diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 47661961dc4..28ace3861a3 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -1414,6 +1414,17 @@ select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); a 1 2 +set optimizer_switch='firstmatch=on'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; FirstMatch(t0) +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 set @@join_cache_level=@tmp_jcl_20110622; set @@optimizer_switch=@tmp_20110622; drop table t0, t1, t2; diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 960ca6e1e06..735a1c272c9 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -1422,6 +1422,17 @@ select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); a 1 2 +set optimizer_switch='firstmatch=on'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t0 ALL NULL NULL NULL NULL 2 +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where; FirstMatch(t0) +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +a +1 +2 set @@join_cache_level=@tmp_jcl_20110622; set @@optimizer_switch=@tmp_20110622; drop table t0, t1, t2; diff --git a/mysql-test/t/subselect_sj.test b/mysql-test/t/subselect_sj.test index 4436373e2a9..515cc4c13d9 100644 --- a/mysql-test/t/subselect_sj.test +++ b/mysql-test/t/subselect_sj.test @@ -1291,6 +1291,12 @@ explain select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); + +set optimizer_switch='firstmatch=on'; +explain +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); +select * from t0 where a in (select t1.a from t1 left join t2 on t1.a=t2.a); + set @@join_cache_level=@tmp_jcl_20110622; set @@optimizer_switch=@tmp_20110622; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 81413fda964..28999e27ce1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -15220,6 +15220,15 @@ evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab) else if (res == 1) return NESTED_LOOP_OK; } + else if (join_tab->do_firstmatch) + { + /* + We should return to the join_tab->do_firstmatch after we have + enumerated all the suffixes for current prefix row combination + */ + if (join_tab->do_firstmatch < join->return_tab) + join->return_tab= join_tab->do_firstmatch; + } /* Send the row complemented by nulls to be joined with the