Commit a83f5b18 authored by Gleb Shchepa's avatar Gleb Shchepa

Bug#38499: flush tables and multitable table update with

           derived table cause crash

When a multi-UPDATE command fails to lock some table, and
subsequently succeeds, the tables need to be reopened if
they were altered. But the reopening procedure failed for
derived tables.

Extra cleanup has been added.


mysql-test/r/lock_multi.result:
  Added test case for bug #38499.
mysql-test/t/lock_multi.test:
  Added test case for bug #38499.
sql/sql_union.cc:
  Bug#38499: flush tables and multitable table update with
             derived table cause crash
  
  Obsolete assertion has been removed.
sql/sql_update.cc:
  Bug#38499: flush tables and multitable table update with
             derived table cause crash
  
  Extra cleanup for derived tables has been added:
  1) unit.cleanup(),
  2) unit->reinit_exec_mechanism().
parent 7b628ade
...@@ -115,3 +115,21 @@ CREATE TABLE t3 SELECT * FROM t1; ...@@ -115,3 +115,21 @@ CREATE TABLE t3 SELECT * FROM t1;
# normal mode # normal mode
# PS mode # PS mode
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
CREATE TABLE t1( a INT, b INT );
INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4);
# 1. test regular tables
# 1.1. test altering of columns that multiupdate doesn't use
# 1.1.1. normal mode
# 1.1.2. PS mode
# 1.2. test altering of columns that multiupdate uses
# 1.2.1. normal mode
# 1.2.2. PS mode
ALTER TABLE t1 ADD COLUMN a INT;
# 2. test UNIONs
# 2.1. test altering of columns that multiupdate doesn't use
# 2.1.1. normal mode
# 2.1.2. PS mode
# 2.2. test altering of columns that multiupdate uses
# 2.2.1. normal mode
# 2.2.2. PS mode
DROP TABLE t1;
...@@ -400,4 +400,206 @@ while ($i) { ...@@ -400,4 +400,206 @@ while ($i) {
--connection default --connection default
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
#
# Bug#38499: flush tables and multitable table update with derived table cause
# crash
#
CREATE TABLE t1( a INT, b INT );
INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4);
--echo # 1. test regular tables
--echo # 1.1. test altering of columns that multiupdate doesn't use
--echo # 1.1.1. normal mode
--disable_query_log
let $i = 100;
while ($i) {
--dec $i
--connection writer
send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0;
--connection locker
ALTER TABLE t1 ADD COLUMN (c INT);
ALTER TABLE t1 DROP COLUMN c;
--connection writer
--reap
}
--echo # 1.1.2. PS mode
--connection writer
PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0';
let $i = 100;
while ($i) {
--dec $i
--connection writer
--send EXECUTE stmt
--connection locker
ALTER TABLE t1 ADD COLUMN (c INT);
ALTER TABLE t1 DROP COLUMN c;
--connection writer
--reap
}
--enable_query_log
--echo # 1.2. test altering of columns that multiupdate uses
--echo # 1.2.1. normal mode
--connection default
--disable_query_log
let $i = 100;
while ($i) {
dec $i;
--connection locker
--error 0,1060
ALTER TABLE t1 ADD COLUMN a int(11) unsigned default NULL;
UPDATE t1 SET a=b;
--connection writer
--send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0;
--connection locker
--error 0,1091
ALTER TABLE t1 DROP COLUMN a;
--connection writer
--error 0,1054 # unknown column error
--reap
}
--enable_query_log
--echo # 1.2.2. PS mode
--disable_query_log
let $i = 100;
while ($i) {
dec $i;
--connection locker
--error 0,1060
ALTER TABLE t1 ADD COLUMN a INT;
UPDATE t1 SET a=b;
--connection writer
PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0';
--send EXECUTE stmt
--connection locker
--error 0,1091
ALTER TABLE t1 DROP COLUMN a;
--connection writer
--error 0,1054 # Unknown column 'a' in 'field list'
--reap
}
--enable_query_log
--connection default
ALTER TABLE t1 ADD COLUMN a INT;
--echo # 2. test UNIONs
--echo # 2.1. test altering of columns that multiupdate doesn't use
--echo # 2.1.1. normal mode
--disable_query_log
let $i = 100;
while ($i) {
--dec $i
--connection writer
send UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0;
--connection locker
ALTER TABLE t1 ADD COLUMN (c INT);
ALTER TABLE t1 DROP COLUMN c;
--connection writer
--reap
}
--echo # 2.1.2. PS mode
--connection writer
PREPARE stmt FROM 'UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0';
let $i = 100;
while ($i) {
--dec $i
--connection writer
--send EXECUTE stmt
--connection locker
ALTER TABLE t1 ADD COLUMN (c INT);
ALTER TABLE t1 DROP COLUMN c;
--connection writer
--reap
}
--enable_query_log
--echo # 2.2. test altering of columns that multiupdate uses
--echo # 2.2.1. normal mode
--connection default
--disable_query_log
let $i = 100;
while ($i) {
dec $i;
--connection locker
--error 0,1060
ALTER TABLE t1 ADD COLUMN a int(11) unsigned default NULL;
UPDATE t1 SET a=b;
--connection writer
--send UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0;
--connection locker
--error 0,1091
ALTER TABLE t1 DROP COLUMN a;
--connection writer
--error 0,1054 # Unknown column 'a' in 'field list'
--reap
}
--enable_query_log
--echo # 2.2.2. PS mode
--disable_query_log
let $i = 100;
while ($i) {
dec $i;
--connection locker
--error 0,1060
ALTER TABLE t1 ADD COLUMN a INT;
UPDATE t1 SET a=b;
--connection writer
PREPARE stmt FROM 'UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0';
--send EXECUTE stmt
--connection locker
--error 0,1091
ALTER TABLE t1 DROP COLUMN a;
--connection writer
--error 0,1054 # Unknown column 'a' in 'field list'
--reap
}
--enable_query_log
--connection default
DROP TABLE t1;
# End of 5.0 tests # End of 5.0 tests
...@@ -399,7 +399,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -399,7 +399,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
} }
else else
{ {
DBUG_ASSERT(!thd->stmt_arena->is_conventional());
/* /*
We're in execution of a prepared statement or stored procedure: We're in execution of a prepared statement or stored procedure:
reset field items to point at fields from the created temporary table. reset field items to point at fields from the created temporary table.
......
...@@ -875,6 +875,26 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -875,6 +875,26 @@ int mysql_multi_update_prepare(THD *thd)
for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
tbl->cleanup_items(); tbl->cleanup_items();
/*
To not to hog memory (as a result of the
unit->reinit_exec_mechanism() call below):
*/
lex->unit.cleanup();
for (SELECT_LEX *sl= lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
SELECT_LEX_UNIT *unit= sl->master_unit();
unit->reinit_exec_mechanism(); // reset unit->prepared flags
/*
Reset 'clean' flag back to force normal execution of
unit->cleanup() in Prepared_statement::cleanup_stmt()
(call to lex->unit.cleanup() above sets this flag to TRUE).
*/
unit->unclean();
}
/* /*
Also we need to cleanup Natural_join_column::table_field items. Also we need to cleanup Natural_join_column::table_field items.
To not to traverse a join tree we will cleanup whole To not to traverse a join tree we will cleanup whole
......
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