mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
MDEV-31150 Crash on 2nd execution of update using mergeable derived table
This bug caused crashes of the server on the second execution of update statements that used mergeable derived tables. The crashes happened in the function multi_update_check_table_access() when the code tried to dereference the pointer stored in field TABLE_LIST::TABLE for a mergeable derived table. The fact is this field is set to NULL after the first execution of the query. At the same any action performed by the function is actually not needed for derived tables. Approved by Oleksandr Byelkin <sanja@mariadb.com>
This commit is contained in:
parent
c6ac1e39b6
commit
adc13e2c16
3 changed files with 421 additions and 0 deletions
|
@ -1384,3 +1384,279 @@ c1 c2 c3
|
|||
12 5 8
|
||||
drop table t1,t2,t3,t;
|
||||
# End of 10.4 tests
|
||||
#
|
||||
# MDEV-31150: 2nd execution of multi-update with
|
||||
# mergeable derived table in WHERE
|
||||
#
|
||||
CREATE TABLE t1 (a int);
|
||||
INSERT INTO t1 VALUES (3),(7),(1),(3);
|
||||
CREATE TABLE t2 (b int);
|
||||
INSERT INTO t2 VALUES (3),(4),(1);
|
||||
CREATE TABLE t3 (c int);
|
||||
INSERT INTO t3 VALUES (3),(4),(1),(5),(1);
|
||||
CREATE PROCEDURE restore_t1()
|
||||
BEGIN
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES (3),(7),(1),(3);
|
||||
END|
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
EXPLAIN UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 ALL NULL NULL NULL NULL 4
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1)
|
||||
1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
|
||||
UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
PREPARE stmt FROM "UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (SELECT * FROM (SELECT b FROM t2) dt)";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
CREATE PROCEDURE p1() UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
EXPLAIN UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 ALL NULL NULL NULL NULL 4
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1)
|
||||
1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
|
||||
UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
PREPARE stmt FROM "UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte)";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
CREATE PROCEDURE p1() UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
EXPLAIN UPDATE t1 SET a = NULL
|
||||
WHERE a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 ALL NULL NULL NULL NULL 4
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1)
|
||||
UPDATE t1 SET a = NULL
|
||||
WHERE a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
PREPARE stmt FROM "UPDATE t1 SET a = NULL
|
||||
WHERE a IN (SELECT * FROM (SELECT b FROM t2) dt)";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
CREATE PROCEDURE p1() UPDATE t1 SET a = NULL
|
||||
WHERE a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
EXPLAIN UPDATE t1 SET a = NULL
|
||||
WHERE a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 ALL NULL NULL NULL NULL 4
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1)
|
||||
UPDATE t1 SET a = NULL
|
||||
WHERE a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
PREPARE stmt FROM "UPDATE t1 SET a = NULL
|
||||
WHERE a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte)";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
CREATE PROCEDURE p1() UPDATE t1 SET a = NULL
|
||||
WHERE a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
3
|
||||
7
|
||||
1
|
||||
3
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
NULL
|
||||
7
|
||||
NULL
|
||||
NULL
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
DROP TABLE t1,t2,t3;
|
||||
DROP PROCEDURE restore_t1;
|
||||
# End of 11.1 tests
|
||||
|
|
|
@ -1201,3 +1201,140 @@ select * from t1;
|
|||
drop table t1,t2,t3,t;
|
||||
|
||||
--echo # End of 10.4 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-31150: 2nd execution of multi-update with
|
||||
--echo # mergeable derived table in WHERE
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (a int);
|
||||
INSERT INTO t1 VALUES (3),(7),(1),(3);
|
||||
CREATE TABLE t2 (b int);
|
||||
INSERT INTO t2 VALUES (3),(4),(1);
|
||||
CREATE TABLE t3 (c int);
|
||||
INSERT INTO t3 VALUES (3),(4),(1),(5),(1);
|
||||
|
||||
--delimiter |
|
||||
CREATE PROCEDURE restore_t1()
|
||||
BEGIN
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES (3),(7),(1),(3);
|
||||
END|
|
||||
--delimiter ;
|
||||
|
||||
let $q1=
|
||||
UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
|
||||
SELECT * FROM t1;
|
||||
eval EXPLAIN $q1;
|
||||
eval $q1;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
eval PREPARE stmt FROM "$q1";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
eval CREATE PROCEDURE p1() $q1;
|
||||
SELECT * FROM t1;
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
|
||||
let $q2=
|
||||
UPDATE t1,t3 SET a = NULL
|
||||
WHERE a=c AND a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
|
||||
SELECT * FROM t1;
|
||||
eval EXPLAIN $q2;
|
||||
eval $q2;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
eval PREPARE stmt FROM "$q2";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
eval CREATE PROCEDURE p1() $q2;
|
||||
SELECT * FROM t1;
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
|
||||
let $q3=
|
||||
UPDATE t1 SET a = NULL
|
||||
WHERE a IN (SELECT * FROM (SELECT b FROM t2) dt);
|
||||
|
||||
SELECT * FROM t1;
|
||||
eval EXPLAIN $q3;
|
||||
eval $q3;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
eval PREPARE stmt FROM "$q3";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
eval CREATE PROCEDURE p1() $q3;
|
||||
SELECT * FROM t1;
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
|
||||
let $q4=
|
||||
UPDATE t1 SET a = NULL
|
||||
WHERE a IN (WITH cte AS (SELECT b FROM t2) SELECT * FROM cte);
|
||||
|
||||
SELECT * FROM t1;
|
||||
eval EXPLAIN $q4;
|
||||
eval $q4;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
eval PREPARE stmt FROM "$q4";
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
EXECUTE stmt;
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
eval CREATE PROCEDURE p1() $q4;
|
||||
SELECT * FROM t1;
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
CALL p1();
|
||||
SELECT * FROM t1;
|
||||
CALL restore_t1();
|
||||
DROP procedure p1;
|
||||
|
||||
DROP TABLE t1,t2,t3;
|
||||
DROP PROCEDURE restore_t1;
|
||||
|
||||
--echo # End of 11.1 tests
|
||||
|
|
|
@ -1497,6 +1497,14 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table,
|
|||
else
|
||||
{
|
||||
/* Must be a base or derived table. */
|
||||
/*
|
||||
Derived tables do not need the check below.
|
||||
Besides one have take into account that for mergeable derived tables
|
||||
TABLE_LIST::TABLE is set to NULL after the first execution of the query.
|
||||
*/
|
||||
if (table->is_derived())
|
||||
return false;
|
||||
|
||||
const bool updated= table->table->map & tables_for_update;
|
||||
if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table,
|
||||
FALSE, 1, FALSE))
|
||||
|
|
Loading…
Reference in a new issue