summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Petrunya <psergey@askmonty.org>2011-10-01 00:10:03 +0400
committerSergey Petrunya <psergey@askmonty.org>2011-10-01 00:10:03 +0400
commitf5987a0c3ef089439d400217a5784c10fd991f52 (patch)
treed31ab1abebc1342fb6f35126aae1cf8a853547f8
parent134e417895f741223b2a8b68c0b674d14920d316 (diff)
downloadmariadb-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.result26
-rw-r--r--mysql-test/r/subselect_sj2_mat.result2
-rw-r--r--mysql-test/r/subselect_sj_mat.result26
-rw-r--r--mysql-test/t/subselect_sj_mat.test34
-rw-r--r--sql/opt_subselect.cc125
-rw-r--r--sql/sql_select.cc20
-rw-r--r--sql/sql_select.h1
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()
{