From 7ddd5418d01e60dba2ae69a668e7c9f811613451 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 1 Jun 2012 14:10:15 +0300 Subject: [PATCH] Fixed bug MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin Analysis: The memory leak was a result of the interaction of semi-join optimization with early optimization of constant subqueries. The function: setup_jtbm_semi_joins() created a dummy temporary table "dummy_table" in order to make some JOIN_TAB objects complete. Normally, such temporary tables are freed inside JOIN_TAB::cleanup. However, the inner-most subquery is pre-optimized, which allows the optimization fo the MAX subquery to determine that its WHERE is TRUE, and thus to compute the result of the MAX during optimization. This ultimately allows the optimize phase of the outer query to find that it WHERE clause is FALSE. Once JOIN::optimize finds that the result set is empty, it sets zero_result_cause, and returns *before* it ever reached make_join_statistics(). As a result the query plan has no JOIN_TABs at all. Since the temporary table is supposed to be cleanup via JOIN_TAB::cleanup, this never happens because there is no JOIN_TAB for this table. Hence we get a memory leak. Solution: Whenever there are no JOIN_TABs, iterate over all table reference in JOIN::join_list, and free the ones that contain semi-join temporary tables. --- mysql-test/r/subselect.result | 16 ++++++++++++++++ mysql-test/r/subselect_no_mat.result | 16 ++++++++++++++++ mysql-test/r/subselect_no_opts.result | 16 ++++++++++++++++ mysql-test/r/subselect_no_scache.result | 16 ++++++++++++++++ mysql-test/r/subselect_no_semijoin.result | 16 ++++++++++++++++ mysql-test/t/subselect.test | 17 +++++++++++++++++ sql/sql_select.cc | 16 ++++++++++++++++ 7 files changed, 113 insertions(+) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index c90596315e1..bce5afaec43 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -6700,5 +6700,21 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 16 drop table t1, t2, t3; +# +# MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +a +drop table t1,t2; # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 0722ca7ba88..4ffb4600f77 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -6698,6 +6698,22 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 16 drop table t1, t2, t3; +# +# MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +a +drop table t1,t2; # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; set optimizer_switch=default; diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 27cf1f70864..c8251f67d2a 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -6695,6 +6695,22 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 16 drop table t1, t2, t3; +# +# MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +a +drop table t1,t2; # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; set @optimizer_switch_for_subselect_test=null; diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index 9994ede79de..07652189bfe 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -6706,6 +6706,22 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 16 drop table t1, t2, t3; +# +# MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +a +drop table t1,t2; # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; set optimizer_switch=default; diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 1973262cdaf..ecef51beb07 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -6695,6 +6695,22 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 16 drop table t1, t2, t3; +# +# MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +a +drop table t1,t2; # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; set @optimizer_switch_for_subselect_test=null; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1cbb15b0b95..1074416cea6 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -5624,5 +5624,22 @@ show status like '%Handler_read%'; drop table t1, t2, t3; +--echo # +--echo # MDEV-288 CHEAP SQ: Valgrind warnings "Memory lost" with IN and EXISTS nested subquery, materialization+semijoin +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(8); + +CREATE TABLE t2 (b INT PRIMARY KEY); +INSERT INTO t2 VALUES (1),(2); + +EXPLAIN +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); +SELECT * FROM t1 WHERE 4 IN (SELECT MAX(b) FROM t2 WHERE EXISTS (SELECT * FROM t1)); + +drop table t1,t2; + + --echo # return optimizer switch changed in the beginning of this test set optimizer_switch=@subselect_tmp; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 10c841639a9..865f3a9d425 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10595,6 +10595,22 @@ void JOIN::cleanup(bool full) tmp_join->tmp_table_param.save_copy_field= 0; } tmp_table_param.cleanup(); + + if (!join_tab) + { + List_iterator li(*join_list); + TABLE_LIST *table_ref; + while ((table_ref= li++)) + { + if (table_ref->table && + table_ref->jtbm_subselect && + table_ref->jtbm_subselect->is_jtbm_const_tab) + { + free_tmp_table(thd, table_ref->table); + table_ref->table= NULL; + } + } + } } DBUG_VOID_RETURN; }