diff options
-rw-r--r-- | mysql-test/r/subselect_mat.result | 8 | ||||
-rw-r--r-- | mysql-test/r/subselect_sj_mat.result | 8 | ||||
-rw-r--r-- | mysql-test/t/subselect_sj_mat.test | 8 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 42 | ||||
-rw-r--r-- | sql/opt_subselect.h | 1 | ||||
-rw-r--r-- | sql/sql_select.cc | 6 |
6 files changed, 73 insertions, 0 deletions
diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index 088d09c6dde..e1c9df3b00b 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -2029,6 +2029,14 @@ SELECT * FROM t1 WHERE 8 IN ( SELECT MIN(pk) FROM t1 ) AND ( pk = a OR pk = b ); pk a b drop table t1; SET optimizer_switch=@save_optimizer_switch; +# +# MDEV-5011: ERROR Plugin 'MEMORY' has ref_count=1 after shutdown for SJM queries +# +CREATE TABLE t1 (pk INT, a INT, b INT, PRIMARY KEY(pk)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,3,5),(2,4,6); +SELECT * FROM t1 WHERE 8 IN (SELECT MIN(pk) FROM t1) AND (pk = a OR pk = b); +pk a b +DROP TABLE t1; # End of 5.3 tests set @subselect_mat_test_optimizer_switch_value=null; set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index 6f1694944d8..bcdd82b790c 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -2069,4 +2069,12 @@ SELECT * FROM t1 WHERE 8 IN ( SELECT MIN(pk) FROM t1 ) AND ( pk = a OR pk = b ); pk a b drop table t1; SET optimizer_switch=@save_optimizer_switch; +# +# MDEV-5011: ERROR Plugin 'MEMORY' has ref_count=1 after shutdown for SJM queries +# +CREATE TABLE t1 (pk INT, a INT, b INT, PRIMARY KEY(pk)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,3,5),(2,4,6); +SELECT * FROM t1 WHERE 8 IN (SELECT MIN(pk) FROM t1) AND (pk = a OR pk = b); +pk a b +DROP TABLE t1; # End of 5.3 tests diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test index 935c0e76937..e9226b8884f 100644 --- a/mysql-test/t/subselect_sj_mat.test +++ b/mysql-test/t/subselect_sj_mat.test @@ -1717,4 +1717,12 @@ SELECT * FROM t1 WHERE 8 IN ( SELECT MIN(pk) FROM t1 ) AND ( pk = a OR pk = b ); drop table t1; SET optimizer_switch=@save_optimizer_switch; +--echo # +--echo # MDEV-5011: ERROR Plugin 'MEMORY' has ref_count=1 after shutdown for SJM queries +--echo # +CREATE TABLE t1 (pk INT, a INT, b INT, PRIMARY KEY(pk)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,3,5),(2,4,6); +SELECT * FROM t1 WHERE 8 IN (SELECT MIN(pk) FROM t1) AND (pk = a OR pk = b); +DROP TABLE t1; + --echo # End of 5.3 tests diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 85df9a47152..c9bc0289ba9 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -5231,6 +5231,12 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, DBUG_RETURN(1); table->table= dummy_table; table->table->pos_in_table_list= table; + /* + Note: the table created above may be freed by: + 1. JOIN_TAB::cleanup(), when the parent join is a regular join. + 2. cleanup_empty_jtbm_semi_joins(), when the parent join is a + degenerate join (e.g. one with "Impossible where"). + */ setup_table_map(table->table, table, table->jtbm_table_no); } else @@ -5263,6 +5269,42 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, } +/* + Cleanup non-merged semi-joins (JBMs) that have empty. + + This function is to cleanups for a special case: + Consider a query like + + select * from t1 where 1=2 AND t1.col IN (select max(..) ... having 1=2) + + For this query, optimization of subquery will short-circuit, and + setup_jtbm_semi_joins() will call create_dummy_tmp_table() so that we have + empty, constant temp.table to stand in as materialized temp. table. + + Now, suppose that the upper join is also found to be degenerate. In that + case, no JOIN_TAB array will be produced, and hence, JOIN::cleanup() will + have a problem with cleaning up empty JTBMs (non-empty ones are cleaned up + through Item::cleanup() calls). +*/ + +void cleanup_empty_jtbm_semi_joins(JOIN *join) +{ + List_iterator<TABLE_LIST> li(*join->join_list); + TABLE_LIST *table; + while ((table= li++)) + { + if ((table->jtbm_subselect && table->jtbm_subselect->is_jtbm_const_tab)) + { + if (table->table) + { + free_tmp_table(join->thd, table->table); + table->table= NULL; + } + } + } +} + + /** Choose an optimal strategy to execute an IN/ALL/ANY subquery predicate based on cost. diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 7b8f3142851..1e8b4cc50a1 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -28,6 +28,7 @@ int pull_out_semijoin_tables(JOIN *join); bool optimize_semijoin_nests(JOIN *join, table_map all_table_map); bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where); +void cleanup_empty_jtbm_semi_joins(JOIN *join); // used by Loose_scan_opt ulonglong get_bound_sj_equalities(TABLE_LIST *sj_nest, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b7ba2fb7b58..b5ecaecda89 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10314,6 +10314,11 @@ void JOIN_TAB::cleanup() { if (table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab) { + /* + Set this to NULL so that cleanup_empty_jtbm_semi_joins() doesn't + attempt to make another free_tmp_table call. + */ + table->pos_in_table_list->table= NULL; free_tmp_table(join->thd, table); table= NULL; } @@ -10727,6 +10732,7 @@ void JOIN::cleanup(bool full) } if (full) { + cleanup_empty_jtbm_semi_joins(this); /* Ensure that the following delete_elements() would not be called twice for the same list. |