Commit 2c3e07df authored by Yuchen Pei's avatar Yuchen Pei

MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1

The memory leak happened on second execution of a prepared statement
that runs UPDATE statement with correlated subquery in right hand side of
the SET clause. In this case, invocation of the method
  table->stat_records()
could return the zero value that results in going into the 'if' branch
that handles impossible where condition. The issue is that this condition
branch missed saving of leaf tables that has to be performed as first
condition optimization activity. Later the PS statement memory root
is marked as read only on finishing first time execution of the prepared
statement. Next time the same statement is executed it hits the assertion
on attempt to allocate a memory on the PS memory root marked as read only.
This memory allocation takes place by the sequence of the following
invocations:
 Prepared_statement::execute
  mysql_execute_command
   Sql_cmd_dml::execute
    Sql_cmd_update::execute_inner
     Sql_cmd_update::update_single_table
      st_select_lex::save_leaf_tables
       List<TABLE_LIST>::push_back

To fix the issue, add the flag SELECT_LEX::leaf_tables_saved to control
whether the method SELECT_LEX::save_leaf_tables() has to be called or
it has been already invoked and no more invocation required.

Similar issue could take place on running the DELETE statement with
the LIMIT clause in PS/SP mode. The reason of memory leak is the same as for
UPDATE case and be fixed in the same way.
parent 8024b8e4
......@@ -89,3 +89,33 @@ f
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
# End of 10.4 tests
#
# MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1
#
CREATE TABLE t1 (id INT, value INT);
CREATE TABLE t2 (id INT);
PREPARE stmt FROM 'UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)';
EXECUTE stmt;
INSERT INTO t1 VALUES (1,10),(2,10),(3,10);
INSERT INTO t2 VALUES (1),(2);
EXECUTE stmt;
SELECT * FROM t1;
id value
1 1
2 1
3 NULL
DEALLOCATE PREPARE stmt;
DROP TABLE t1, t2;
# Memory leak also could take place on running the DELETE statement
# with the LIMIT clause. Check it.
CREATE TABLE t1 (c1 INT);
INSERT INTO t1 (c1) VALUES (1), (2), (3);
CREATE PROCEDURE p1(p1 INT)
DELETE FROM t1 LIMIT p1;
CALL p1(0);
CALL p1(1);
CALL p1(2);
# Clean up
DROP TABLE t1;
DROP PROCEDURE p1;
# End of 10.5 tests
......@@ -110,3 +110,36 @@ DEALLOCATE PREPARE stmt;
DROP TABLE t1;
--echo # End of 10.4 tests
--echo #
--echo # MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1
--echo #
CREATE TABLE t1 (id INT, value INT);
CREATE TABLE t2 (id INT);
PREPARE stmt FROM 'UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)';
EXECUTE stmt;
INSERT INTO t1 VALUES (1,10),(2,10),(3,10);
INSERT INTO t2 VALUES (1),(2);
EXECUTE stmt;
SELECT * FROM t1;
DEALLOCATE PREPARE stmt;
DROP TABLE t1, t2;
--echo # Memory leak also could take place on running the DELETE statement
--echo # with the LIMIT clause. Check it.
CREATE TABLE t1 (c1 INT);
INSERT INTO t1 (c1) VALUES (1), (2), (3);
CREATE PROCEDURE p1(p1 INT)
DELETE FROM t1 LIMIT p1;
CALL p1(0);
CALL p1(1);
CALL p1(2);
--echo # Clean up
DROP TABLE t1;
DROP PROCEDURE p1;
--echo # End of 10.5 tests
......@@ -530,6 +530,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
*/
if (unlikely(thd->is_error()))
DBUG_RETURN(TRUE);
if (!thd->lex->current_select->leaf_tables_saved)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->leaf_tables_saved= true;
}
my_ok(thd, 0);
DBUG_RETURN(0); // Nothing to delete
}
......@@ -902,10 +909,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query_cache_invalidate3(thd, table_list, 1);
}
if (thd->lex->current_select->first_cond_optimization)
if (!thd->lex->current_select->leaf_tables_saved)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
thd->lex->current_select->leaf_tables_saved= true;
}
delete deltempfile;
......
......@@ -1373,10 +1373,10 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
::my_ok(thd, info.copied + info.deleted + updated, id, buff);
}
thd->abort_on_warning= 0;
if (thd->lex->current_select->first_cond_optimization)
if (!thd->lex->current_select->leaf_tables_saved)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
thd->lex->current_select->leaf_tables_saved= true;
}
my_free(readbuff);
......
......@@ -3023,6 +3023,7 @@ void st_select_lex::init_query()
olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0;
having_fix_field_for_pushed_cond= 0;
leaf_tables_saved= false;
context.select_lex= this;
context.init();
cond_count= between_count= with_wild= 0;
......
......@@ -1182,6 +1182,10 @@ class st_select_lex: public st_select_lex_node
*/
bool have_merged_subqueries;
/**
Flag to guard against double initialization of leaf tables list
*/
bool leaf_tables_saved;
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
List<TABLE_LIST> leaf_tables_prep;
......
......@@ -2054,12 +2054,14 @@ JOIN::optimize_inner()
/* Convert all outer joins to inner joins if possible */
conds= simplify_joins(this, join_list, conds, TRUE, FALSE);
if (thd->is_error() || select_lex->save_leaf_tables(thd))
if (thd->is_error() ||
(!select_lex->leaf_tables_saved && select_lex->save_leaf_tables(thd)))
{
if (arena)
thd->restore_active_arena(arena, &backup);
DBUG_RETURN(1);
}
select_lex->leaf_tables_saved= true;
build_bitmap_for_nested_joins(join_list, 0);
sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
......
......@@ -598,6 +598,13 @@ int mysql_update(THD *thd,
{
DBUG_RETURN(1); // Error in where
}
if (!thd->lex->current_select->leaf_tables_saved)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->leaf_tables_saved= true;
}
my_ok(thd); // No matching records
DBUG_RETURN(0);
}
......@@ -1366,10 +1373,10 @@ int mysql_update(THD *thd,
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
thd->abort_on_warning= 0;
if (thd->lex->current_select->first_cond_optimization)
if (!thd->lex->current_select->leaf_tables_saved)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
thd->lex->current_select->leaf_tables_saved= true;
}
*found_return= found;
*updated_return= updated;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment