diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2011-10-01 00:10:03 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2011-10-01 00:10:03 +0400 |
commit | f5987a0c3ef089439d400217a5784c10fd991f52 (patch) | |
tree | d31ab1abebc1342fb6f35126aae1cf8a853547f8 | |
parent | 134e417895f741223b2a8b68c0b674d14920d316 (diff) | |
download | mariadb-git-f5987a0c3ef089439d400217a5784c10fd991f52.tar.gz |
BUG#860553: Crash in create_ref_for_key with semijoin + materialization
- The problem was that JOIN::save/restore_query_plan() did not save/restore parts of
the query plan that are located inside SJ_MATERIALIZATION_INFO structures. This could
cause parts of one plan to be used with another, which led get_best_combination() to
constructing non-sensical join plans (and crash).
Fixed by saving/restoring SJM parts of the query plans.
- check_and_do_in_subquery_rewrites() will not set SUBS_MATERIALIZATION flag when it
records that the subquery predicate is to be converted into semi-join.
If convert_join_subqueries_to_semijoins() later decides not to convert to semi-join,
let it set SUBS_MATERIALIZATION flag, if appropriate.
-rw-r--r-- | mysql-test/r/subselect_mat.result | 26 | ||||
-rw-r--r-- | mysql-test/r/subselect_sj2_mat.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_sj_mat.result | 26 | ||||
-rw-r--r-- | mysql-test/t/subselect_sj_mat.test | 34 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 125 | ||||
-rw-r--r-- | sql/sql_select.cc | 20 | ||||
-rw-r--r-- | sql/sql_select.h | 1 |
7 files changed, 183 insertions, 51 deletions
diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index 00c87e83657..f0f317a52da 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -1656,7 +1656,6 @@ a 0 0 DROP TABLE t2,t3,t4,t5; -set optimizer_switch=@subselect_sj_mat_tmp; # # BUG#860300: Second crash with get_fanout_with_deps() with semijoin + materialization # @@ -1703,6 +1702,31 @@ f3 7 DROP TABLE t1, t2, t3, t4; set optimizer_switch=@tmp_860535; +# +# BUG#860553: Crash in create_ref_for_key with semijoin + materialization +# +CREATE TABLE t1 (f1 int) ; +CREATE TABLE t2 (f5 varchar(52) NOT NULL) ; +CREATE TABLE t3 (f1 varchar(3), f4 varchar(52) , KEY (f4), PRIMARY KEY (f1)); +CREATE TABLE t4 (f3 int, KEY (f3)); +INSERT INTO t4 VALUES (17),(20); +CREATE TABLE t5 (f2 int); +INSERT INTO t5 VALUES (0),(0); +SELECT * +FROM t1 +JOIN t2 +ON ( t2.f5 ) IN ( +SELECT t3.f4 +FROM t3 +WHERE ( 1 ) IN ( +SELECT t4.f3 +FROM t4 , t5 +) +); +f1 f5 +DROP TABLE t1, t2, t3, t4, t5; +# This must be at the end: +set optimizer_switch=@subselect_sj_mat_tmp; set @subselect_mat_test_optimizer_switch_value=null; set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; set optimizer_switch='mrr=on,mrr_sort_keys=on,index_condition_pushdown=on'; diff --git a/mysql-test/r/subselect_sj2_mat.result b/mysql-test/r/subselect_sj2_mat.result index b5e15946037..da5796296e7 100644 --- a/mysql-test/r/subselect_sj2_mat.result +++ b/mysql-test/r/subselect_sj2_mat.result @@ -593,7 +593,7 @@ select * from t1 left join t2 on (t2.a= t1.a and t2.a in (select pk from t3)); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where -2 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY PRIMARY 4 func 1 Using index +2 SUBQUERY t3 index PRIMARY PRIMARY 4 NULL 10 Using index drop table t0, t1, t2, t3; create table t1 (a int); insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index 2c22d6a4b5d..a6c7ced3e00 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -1696,7 +1696,6 @@ a 0 0 DROP TABLE t2,t3,t4,t5; -set optimizer_switch=@subselect_sj_mat_tmp; # # BUG#860300: Second crash with get_fanout_with_deps() with semijoin + materialization # @@ -1743,3 +1742,28 @@ f3 7 DROP TABLE t1, t2, t3, t4; set optimizer_switch=@tmp_860535; +# +# BUG#860553: Crash in create_ref_for_key with semijoin + materialization +# +CREATE TABLE t1 (f1 int) ; +CREATE TABLE t2 (f5 varchar(52) NOT NULL) ; +CREATE TABLE t3 (f1 varchar(3), f4 varchar(52) , KEY (f4), PRIMARY KEY (f1)); +CREATE TABLE t4 (f3 int, KEY (f3)); +INSERT INTO t4 VALUES (17),(20); +CREATE TABLE t5 (f2 int); +INSERT INTO t5 VALUES (0),(0); +SELECT * +FROM t1 +JOIN t2 +ON ( t2.f5 ) IN ( +SELECT t3.f4 +FROM t3 +WHERE ( 1 ) IN ( +SELECT t4.f3 +FROM t4 , t5 +) +); +f1 f5 +DROP TABLE t1, t2, t3, t4, t5; +# This must be at the end: +set optimizer_switch=@subselect_sj_mat_tmp; diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test index 2ba075a047d..ef4a40172e2 100644 --- a/mysql-test/t/subselect_sj_mat.test +++ b/mysql-test/t/subselect_sj_mat.test @@ -1349,8 +1349,6 @@ WHERE t2.a = ALL ( DROP TABLE t2,t3,t4,t5; -set optimizer_switch=@subselect_sj_mat_tmp; - --echo # --echo # BUG#860300: Second crash with get_fanout_with_deps() with semijoin + materialization --echo # @@ -1399,3 +1397,35 @@ SELECT * FROM t4; DROP TABLE t1, t2, t3, t4; set optimizer_switch=@tmp_860535; +--echo # +--echo # BUG#860553: Crash in create_ref_for_key with semijoin + materialization +--echo # +CREATE TABLE t1 (f1 int) ; +CREATE TABLE t2 (f5 varchar(52) NOT NULL) ; + +CREATE TABLE t3 (f1 varchar(3), f4 varchar(52) , KEY (f4), PRIMARY KEY (f1)); + +CREATE TABLE t4 (f3 int, KEY (f3)); +INSERT INTO t4 VALUES (17),(20); + +CREATE TABLE t5 (f2 int); +INSERT INTO t5 VALUES (0),(0); + +SELECT * +FROM t1 +JOIN t2 +ON ( t2.f5 ) IN ( + SELECT t3.f4 + FROM t3 + WHERE ( 1 ) IN ( + SELECT t4.f3 + FROM t4 , t5 + ) +); + +DROP TABLE t1, t2, t3, t4, t5; + + +--echo # This must be at the end: +set optimizer_switch=@subselect_sj_mat_tmp; + diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 7ac09eaa434..1c317cea808 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -210,6 +210,74 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); /* + Check if Materialization strategy is allowed for given subquery predicate. + + @param thd Thread handle + @param in_subs The subquery predicate + @param child_select The select inside predicate (the function will + check it is the only one) + + @return TRUE - Materialization is applicable + FALSE - Otherwise +*/ + +bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, + st_select_lex *child_select) +{ + st_select_lex_unit* parent_unit= child_select->master_unit(); + /* + Check if the subquery predicate can be executed via materialization. + The required conditions are: + 0. The materialization optimizer switch was set. + 1. Subquery is a single SELECT (not a UNION). + TODO: this is a limitation that can be fixed + 2. Subquery is not a table-less query. In this case there is no + point in materializing. + 2A The upper query is not a table-less SELECT ... FROM DUAL. We + can't do materialization for SELECT .. FROM DUAL because it + does not call setup_subquery_materialization(). We could make + SELECT ... FROM DUAL call that function but that doesn't seem + to be the case that is worth handling. + 3. Either the subquery predicate is a top-level predicate, or at + least one partial match strategy is enabled. If no partial match + strategy is enabled, then materialization cannot be used for + non-top-level queries because it cannot handle NULLs correctly. + 4. Subquery is non-correlated + TODO: + This condition is too restrictive (limitation). It can be extended to: + (Subquery is non-correlated || + Subquery is correlated to any query outer to IN predicate || + (Subquery is correlated to the immediate outer query && + Subquery !contains {GROUP BY, ORDER BY [LIMIT], + aggregate functions}) && subquery predicate is not under "NOT IN")) + + (*) The subquery must be part of a SELECT statement. The current + condition also excludes multi-table update statements. + A note about prepared statements: we want the if-branch to be taken on + PREPARE and each EXECUTE. The rewrites are only done once, but we need + select_lex->sj_subselects list to be populated for every EXECUTE. + + */ + if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 + !child_select->is_part_of_union() && // 1 + parent_unit->first_select()->leaf_tables.elements && // 2 + thd->lex->sql_command == SQLCOM_SELECT && // * + child_select->outer_select()->leaf_tables.elements && // 2A + subquery_types_allow_materialization(in_subs) && + (in_subs->is_top_level_item() || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 + !in_subs->is_correlated) //4 + { + return TRUE; + } + return FALSE; +} + + +/* Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them SYNOPSIS @@ -381,52 +449,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) */ if (in_subs) { - /* - Check if the subquery predicate can be executed via materialization. - The required conditions are: - 0. The materialization optimizer switch was set. - 1. Subquery is a single SELECT (not a UNION). - TODO: this is a limitation that can be fixed - 2. Subquery is not a table-less query. In this case there is no - point in materializing. - 2A The upper query is not a table-less SELECT ... FROM DUAL. We - can't do materialization for SELECT .. FROM DUAL because it - does not call setup_subquery_materialization(). We could make - SELECT ... FROM DUAL call that function but that doesn't seem - to be the case that is worth handling. - 3. Either the subquery predicate is a top-level predicate, or at - least one partial match strategy is enabled. If no partial match - strategy is enabled, then materialization cannot be used for - non-top-level queries because it cannot handle NULLs correctly. - 4. Subquery is non-correlated - TODO: - This condition is too restrictive (limitation). It can be extended to: - (Subquery is non-correlated || - Subquery is correlated to any query outer to IN predicate || - (Subquery is correlated to the immediate outer query && - Subquery !contains {GROUP BY, ORDER BY [LIMIT], - aggregate functions}) && subquery predicate is not under "NOT IN")) - - (*) The subquery must be part of a SELECT statement. The current - condition also excludes multi-table update statements. - A note about prepared statements: we want the if-branch to be taken on - PREPARE and each EXECUTE. The rewrites are only done once, but we need - select_lex->sj_subselects list to be populated for every EXECUTE. - - */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 - !select_lex->is_part_of_union() && // 1 - parent_unit->first_select()->leaf_tables.elements && // 2 - thd->lex->sql_command == SQLCOM_SELECT && // * - select_lex->outer_select()->leaf_tables.elements && // 2A - subquery_types_allow_materialization(in_subs) && - (in_subs->is_top_level_item() || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 - !in_subs->is_correlated) //4 - { + if (is_materialization_applicable(thd, in_subs, select_lex)) + { in_subs->in_strategy|= SUBS_MATERIALIZATION; /* @@ -914,6 +938,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) check_and_do_in_subquery_rewrites. */ in_subq->in_strategy= SUBS_IN_TO_EXISTS; + if (is_materialization_applicable(thd, in_subq, + in_subq->unit->first_select())) + { + in_subq->in_strategy|= SUBS_MATERIALIZATION; + } + in_subq= li++; } @@ -1766,6 +1796,9 @@ int pull_out_semijoin_tables(JOIN *join) All obtained information is saved and will be used by the main join optimization pass. + + NOTES + Because of Join::reoptimize(), this function may be called multiple times. RETURN FALSE Ok diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3d9698d6247..7e32c750ab9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21576,6 +21576,15 @@ void JOIN::save_query_plan(Join_plan_state *save_to) memcpy((uchar*) save_to->best_positions, (uchar*) best_positions, sizeof(POSITION) * (table_count + 1)); memset(best_positions, 0, sizeof(POSITION) * (table_count + 1)); + + /* Save SJM nests */ + List_iterator<TABLE_LIST> it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= save_to->sj_mat_info; + while ((tlist= it++)) + { + *(p_info++)= tlist->sj_mat_info; + } } @@ -21616,6 +21625,14 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) } memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions, sizeof(POSITION) * (table_count + 1)); + /* Restore SJM nests */ + List_iterator<TABLE_LIST> it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= restore_from->sj_mat_info; + while ((tlist= it++)) + { + tlist->sj_mat_info= *(p_info++); + } } @@ -21705,6 +21722,9 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, return REOPT_ERROR; optimize_keyuse(this, &keyuse); + if (optimize_semijoin_nests(this, join_tables)) + return REOPT_ERROR; + /* Re-run the join optimizer to compute a new query plan. */ if (choose_plan(this, join_tables)) return REOPT_ERROR; diff --git a/sql/sql_select.h b/sql/sql_select.h index 676cc7452f4..0a416966995 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -666,6 +666,7 @@ protected: KEYUSE *join_tab_keyuse[MAX_TABLES]; /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ key_map join_tab_checked_keys[MAX_TABLES]; + SJ_MATERIALIZATION_INFO *sj_mat_info[MAX_TABLES]; public: Join_plan_state() { |