Skip to content

Commit

Permalink
MDEV-15703: Crash in EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a…
Browse files Browse the repository at this point in the history
… INT DEFAULT ?)' USING DEFAULT

This patch fixes the issue with passing the DEFAULT or IGNORE values to
positional parameters for some kind of SQL statements to be executed
as prepared statements.

The main idea of the patch is to associate an actual value being passed
by the USING clause with the positional parameter represented by
the Item_param class. Such association must be performed on execution of
UPDATE statement in PS/SP mode. Other corner cases that results in
server crash is on handling CREATE TABLE when positional parameter
placed after the DEFAULT clause or CALL statement and passing either
the value DEFAULT or IGNORE as an actual value for the positional parameter.
This case is fixed by checking whether an error is set in diagnostics
area at the function pack_vcols() on return from the function pack_expression()
  • Loading branch information
dmitryshulga authored and sanja-byelkin committed Feb 8, 2024
1 parent 6b2cd78 commit e48bd47
Show file tree
Hide file tree
Showing 16 changed files with 418 additions and 17 deletions.
118 changes: 118 additions & 0 deletions mysql-test/main/ps.result
Original file line number Diff line number Diff line change
Expand Up @@ -5815,5 +5815,123 @@ GROUP_CONCAT(@x)
0
DROP TABLE t;
#
# MDEV-15703: Crash in EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING DEFAULT
#
PREPARE stmt FROM 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)';
EXECUTE stmt USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
DEALLOCATE PREPARE stmt;
PREPARE stmt FROM 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)';
EXECUTE stmt USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
DEALLOCATE PREPARE stmt;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'BEGIN NOT ATOMIC DECLARE a INT DEFAULT ?; END' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'BEGIN NOT ATOMIC DECLARE a INT DEFAULT ?; END' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
CREATE PROCEDURE p1(a INT) SELECT 1;
EXECUTE IMMEDIATE 'CALL p1(?)' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'CALL p1(?)' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
DROP PROCEDURE p1;
EXECUTE IMMEDIATE 'SELECT ? UNION SELECT 1' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'SELECT ? UNION SELECT 1' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION ALL SELECT 1) AS derived' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION ALL SELECT 1) AS derived' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION DISTINCT SELECT 1) AS derived' USING DEFAULT;
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION DISTINCT SELECT 1) AS derived' USING IGNORE;
ERROR HY000: Default/ignore value is not supported for such parameter usage
# multi-update and DEFAULT
CREATE TABLE t1 (a INT, b INT DEFAULT a);
INSERT into t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT DEFAULT a);
INSERT INTO t2 VALUES (1,10),(2,30);
UPDATE t1,t2 SET t1.b = DEFAULT, t2.b = DEFAULT WHERE t1.a=t2.a;
SELECT * FROM t1;
a b
1 1
2 2
SELECT * FROM t2;
a b
1 1
2 2
# re-check the case for Prepared Statement with parameters
TRUNCATE TABLE t1;
TRUNCATE TABLE t2;
INSERT INTO t1 VALUES (1,2),(2,3);
INSERT INTO t2 VALUES (1,10),(2,30);
EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING DEFAULT, DEFAULT;
SELECT * FROM t1;
a b
1 1
2 2
SELECT * FROM t2;
a b
1 1
2 2
DROP TABLE t1, t2;
# multi-update and IGNORE
CREATE TABLE t1 (a INT, b INT default a);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT default a);
INSERT INTO t2 VALUES (1,10),(2,30);
UPDATE t1,t2 SET t1.b = IGNORE, t2.b = IGNORE WHERE t1.a=t2.a;
SELECT * FROM t1;
a b
1 2
2 3
SELECT * FROM t2;
a b
1 NULL
2 NULL
# re-check the case for Prepared Statement with parameters
TRUNCATE TABLE t1;
TRUNCATE TABLE t2;
INSERT INTO t1 VALUES (1,2),(2,3);
INSERT INTO t2 VALUES (1,10),(2,30);
EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING IGNORE, IGNORE;
SELECT * FROM t1;
a b
1 2
2 3
SELECT * FROM t2;
a b
1 10
2 30
DROP TABLE t1, t2;
# multi-update and DEFAULT parameter (no default)
CREATE TABLE t1 (a INT, b INT NOT NULL);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT NOT NULL);
INSERT INTO t2 VALUES (1,10),(2,30);
EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING DEFAULT, DEFAULT;
ERROR HY000: Field 'b' doesn't have a default value
DROP TABLE t1, t2;
# multi-update and IGNORE parameter (no default)
CREATE TABLE t1 (a INT, b INT NOT NULL);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT NOT NULL);
INSERT INTO t2 VALUES (1,10),(2,30);
EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING IGNORE, IGNORE;
SELECT * FROM t1;
a b
1 2
2 3
SELECT * FROM t2;
a b
1 10
2 30
DROP TABLE t1, t2;
#
# End of 10.4 tests
#
119 changes: 119 additions & 0 deletions mysql-test/main/ps.test
Original file line number Diff line number Diff line change
Expand Up @@ -5243,6 +5243,125 @@ EXECUTE IMMEDIATE 'SELECT GROUP_CONCAT(@x) FROM t GROUP BY @x := f';

DROP TABLE t;

--echo #
--echo # MDEV-15703: Crash in EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING DEFAULT
--echo #

PREPARE stmt FROM 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)';
--error ER_INVALID_DEFAULT_PARAM
EXECUTE stmt USING DEFAULT;
DEALLOCATE PREPARE stmt;

PREPARE stmt FROM 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)';
--error ER_INVALID_DEFAULT_PARAM
EXECUTE stmt USING IGNORE;
DEALLOCATE PREPARE stmt;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING DEFAULT;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a INT DEFAULT ?)' USING IGNORE;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'BEGIN NOT ATOMIC DECLARE a INT DEFAULT ?; END' USING DEFAULT;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'BEGIN NOT ATOMIC DECLARE a INT DEFAULT ?; END' USING IGNORE;

CREATE PROCEDURE p1(a INT) SELECT 1;
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'CALL p1(?)' USING DEFAULT;
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'CALL p1(?)' USING IGNORE;
DROP PROCEDURE p1;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT ? UNION SELECT 1' USING DEFAULT;
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT ? UNION SELECT 1' USING IGNORE;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION ALL SELECT 1) AS derived' USING DEFAULT;
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION ALL SELECT 1) AS derived' USING IGNORE;

--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION DISTINCT SELECT 1) AS derived' USING DEFAULT;
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'SELECT * FROM (SELECT ? UNION DISTINCT SELECT 1) AS derived' USING IGNORE;

--echo # multi-update and DEFAULT
CREATE TABLE t1 (a INT, b INT DEFAULT a);
INSERT into t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT DEFAULT a);
INSERT INTO t2 VALUES (1,10),(2,30);

UPDATE t1,t2 SET t1.b = DEFAULT, t2.b = DEFAULT WHERE t1.a=t2.a;
SELECT * FROM t1;
SELECT * FROM t2;

--echo # re-check the case for Prepared Statement with parameters
TRUNCATE TABLE t1;
TRUNCATE TABLE t2;
INSERT INTO t1 VALUES (1,2),(2,3);
INSERT INTO t2 VALUES (1,10),(2,30);

EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING DEFAULT, DEFAULT;
SELECT * FROM t1;
SELECT * FROM t2;

# Cleanup
DROP TABLE t1, t2;

--echo # multi-update and IGNORE
CREATE TABLE t1 (a INT, b INT default a);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT default a);
INSERT INTO t2 VALUES (1,10),(2,30);

UPDATE t1,t2 SET t1.b = IGNORE, t2.b = IGNORE WHERE t1.a=t2.a;
SELECT * FROM t1;
SELECT * FROM t2;

--echo # re-check the case for Prepared Statement with parameters
TRUNCATE TABLE t1;
TRUNCATE TABLE t2;
INSERT INTO t1 VALUES (1,2),(2,3);
INSERT INTO t2 VALUES (1,10),(2,30);

EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING IGNORE, IGNORE;
SELECT * FROM t1;
SELECT * FROM t2;

# Cleanup
DROP TABLE t1, t2;

--echo # multi-update and DEFAULT parameter (no default)
CREATE TABLE t1 (a INT, b INT NOT NULL);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT NOT NULL);
INSERT INTO t2 VALUES (1,10),(2,30);

--error ER_NO_DEFAULT_FOR_FIELD
EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING DEFAULT, DEFAULT;

# Cleanup
DROP TABLE t1, t2;

--echo # multi-update and IGNORE parameter (no default)
CREATE TABLE t1 (a INT, b INT NOT NULL);
INSERT INTO t1 VALUES (1,2),(2,3);
CREATE TABLE t2 (a INT, b INT NOT NULL);
INSERT INTO t2 VALUES (1,10),(2,30);

EXECUTE IMMEDIATE 'UPDATE t1,t2 SET t1.b = ?, t2.b = ? WHERE t1.a=t2.a' USING IGNORE, IGNORE;
SELECT * FROM t1;
SELECT * FROM t2;

# Cleanup
DROP TABLE t1, t2;

--echo #
--echo # End of 10.4 tests
--echo #
4 changes: 2 additions & 2 deletions mysql-test/main/table_value_constr.result
Original file line number Diff line number Diff line change
Expand Up @@ -2594,9 +2594,9 @@ ERROR HY000: 'ignore' is not allowed in this context
VALUES (DEFAULT);
ERROR HY000: 'default' is not allowed in this context
EXECUTE IMMEDIATE 'VALUES (?)' USING IGNORE;
ERROR HY000: 'ignore' is not allowed in this context
ERROR HY000: Default/ignore value is not supported for such parameter usage
EXECUTE IMMEDIATE 'VALUES (?)' USING DEFAULT;
ERROR HY000: 'default' is not allowed in this context
ERROR HY000: Default/ignore value is not supported for such parameter usage
#
# MDEV-24675: TVC using subqueries
#
Expand Down
4 changes: 2 additions & 2 deletions mysql-test/main/table_value_constr.test
Original file line number Diff line number Diff line change
Expand Up @@ -1349,9 +1349,9 @@ DELIMITER ;$$
VALUES (IGNORE);
--error ER_UNKNOWN_ERROR
VALUES (DEFAULT);
--error ER_UNKNOWN_ERROR
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'VALUES (?)' USING IGNORE;
--error ER_UNKNOWN_ERROR
--error ER_INVALID_DEFAULT_PARAM
EXECUTE IMMEDIATE 'VALUES (?)' USING DEFAULT;

--echo #
Expand Down
27 changes: 27 additions & 0 deletions sql/field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,9 @@ bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
if (!(expr_item= thd->sp_prepare_func_item(value, 1)))
goto error;

if (expr_item->check_is_evaluable_expression_or_error())
goto error;

/*
expr_item is now fixed, it's safe to call cmp_type()
*/
Expand Down Expand Up @@ -11424,6 +11427,30 @@ bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record)
}


/**
Find which reaction should be for IGNORE value.
*/

ignore_value_reaction find_ignore_reaction(THD *thd)
{
enum_sql_command com= thd->lex->sql_command;

// All insert-like commands
if (com == SQLCOM_INSERT || com == SQLCOM_REPLACE ||
com == SQLCOM_INSERT_SELECT || com == SQLCOM_REPLACE_SELECT ||
com == SQLCOM_LOAD)
{
return IGNORE_MEANS_DEFAULT;
}
// Update commands
if (com == SQLCOM_UPDATE || com == SQLCOM_UPDATE_MULTI)
{
return IGNORE_MEANS_FIELD_VALUE;
}
return IGNORE_MEANS_ERROR;
}


bool Field::save_in_field_default_value(bool view_error_processing)
{
THD *thd= table->in_use;
Expand Down
9 changes: 9 additions & 0 deletions sql/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ enum enum_check_fields
CHECK_FIELD_ERROR_FOR_NULL,
};

enum ignore_value_reaction
{
IGNORE_MEANS_ERROR,
IGNORE_MEANS_DEFAULT,
IGNORE_MEANS_FIELD_VALUE
};

ignore_value_reaction find_ignore_reaction(THD *thd);

/*
Common declarations for Field and Item
*/
Expand Down
Loading

0 comments on commit e48bd47

Please sign in to comment.