Commit 0b38c93d authored by Gleb Shchepa's avatar Gleb Shchepa
Browse files

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.
parent 453bc6c1
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -115,3 +115,21 @@ CREATE TABLE t3 SELECT * FROM t1;
# normal mode
# PS mode
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;
+202 −0
Original line number Diff line number Diff line
@@ -400,4 +400,206 @@ while ($i) {
--connection default
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
+0 −1
Original line number Diff line number Diff line
@@ -399,7 +399,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
    }
    else
    {
      DBUG_ASSERT(!thd->stmt_arena->is_conventional());
      /*
        We're in execution of a prepared statement or stored procedure:
        reset field items to point at fields from the created temporary table.
+20 −0
Original line number Diff line number Diff line
@@ -875,6 +875,26 @@ int mysql_multi_update_prepare(THD *thd)
    for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
      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.
      To not to traverse a join tree we will cleanup whole