MDEV-10709 Expressions as parameters to Dynamic SQL
This commit is contained in:
parent
8ea2e143f0
commit
4c45b820aa
@ -4093,3 +4093,201 @@ deallocate prepare stmt;
|
|||||||
SET SESSION sql_mode = @save_sql_mode;
|
SET SESSION sql_mode = @save_sql_mode;
|
||||||
DROP TABLE t1,t2;
|
DROP TABLE t1,t2;
|
||||||
# End of 10.0 tests
|
# End of 10.0 tests
|
||||||
|
#
|
||||||
|
# Start of 10.2 tests
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Using a simple expressions as an EXECUTE parameter
|
||||||
|
#
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
?
|
||||||
|
10
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING TO_BASE64('xxx');
|
||||||
|
?
|
||||||
|
eHh4
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
PREPARE stmt FROM 'SELECT ?+? FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10, 10 + 10;
|
||||||
|
?+?
|
||||||
|
30
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
PREPARE stmt FROM 'SELECT CONCAT(?,?) FROM DUAL';
|
||||||
|
EXECUTE stmt USING 'xxx', CONCAT('yyy','zzz');
|
||||||
|
CONCAT(?,?)
|
||||||
|
xxxyyyzzz
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
#
|
||||||
|
# Testing disallowed expressions in USING
|
||||||
|
#
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING (SELECT 1);
|
||||||
|
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT 1)' at line 1
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
CREATE FUNCTION f1() RETURNS VARCHAR(10) RETURN 'test';
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING f1();
|
||||||
|
ERROR 42000: EXECUTE..USING does not support subqueries or stored functions
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
#
|
||||||
|
# Testing erroneous expressions in USING
|
||||||
|
#
|
||||||
|
PREPARE stmt FROM 'SELECT ?';
|
||||||
|
EXECUTE stmt USING _latin1'a'=_latin2'a';
|
||||||
|
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation '='
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
PREPARE stmt FROM 'SELECT ?';
|
||||||
|
EXECUTE stmt USING ROW(1,2);
|
||||||
|
ERROR 21000: Operand should contain 1 column(s)
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
#
|
||||||
|
# Creating tables from EXECUTE parameters
|
||||||
|
#
|
||||||
|
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? AS c1 FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` bigint(21) NOT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING 10.123;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` decimal(5,3) DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING 10.123e0;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` double NOT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_DATE;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` date DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` datetime DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(3);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` datetime(3) DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(6);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` datetime(6) DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` time DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(3);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` time(3) DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(6);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`c1` time(6) DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
#
|
||||||
|
# Using a user variable as an EXECUTE..USING out parameter
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
SET a:= 10;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
SET @a=1;
|
||||||
|
CALL p1(@a);
|
||||||
|
SELECT @a;
|
||||||
|
@a
|
||||||
|
10
|
||||||
|
SET @a=2;
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING @a;
|
||||||
|
SELECT @a;
|
||||||
|
@a
|
||||||
|
10
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Using an SP variable as an EXECUTE..USING out parameter
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1 (OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
SET a=10;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
CREATE PROCEDURE p2 (OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING a;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
SET @a= 1;
|
||||||
|
CALL p2(@a);
|
||||||
|
SELECT @a;
|
||||||
|
@a
|
||||||
|
10
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Testing re-prepare on a table metadata update between PREPARE and EXECUTE
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
CREATE PROCEDURE p1(a INT)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO t1 VALUES (a);
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
10
|
||||||
|
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=NEW.a+1;
|
||||||
|
EXECUTE stmt USING 20;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
10
|
||||||
|
21
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# End of MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# End of 10.2 tests
|
||||||
|
#
|
||||||
|
@ -27,3 +27,79 @@ master-bin.000001 # Gtid # # BEGIN GTID #-#-#
|
|||||||
master-bin.000001 # Query # # use `test`; insert into t1 select 100 limit 100
|
master-bin.000001 # Query # # use `test`; insert into t1 select 100 limit 100
|
||||||
master-bin.000001 # Query # # COMMIT
|
master-bin.000001 # Query # # COMMIT
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
#
|
||||||
|
# MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
#
|
||||||
|
FLUSH LOGS;
|
||||||
|
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-02 10:20:30.123456');
|
||||||
|
CREATE TABLE t1 (a DECIMAL(30,8));
|
||||||
|
PREPARE stmt FROM 'INSERT INTO t1 VALUES (?)';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
EXECUTE stmt USING 11e0;
|
||||||
|
EXECUTE stmt USING 12.1;
|
||||||
|
EXECUTE stmt USING '13';
|
||||||
|
EXECUTE stmt USING CURRENT_DATE;
|
||||||
|
EXECUTE stmt USING MAKETIME(10,20,30);
|
||||||
|
EXECUTE stmt USING CURRENT_TIME;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(3);
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(6);
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(3);
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(6);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
10.00000000
|
||||||
|
11.00000000
|
||||||
|
12.10000000
|
||||||
|
13.00000000
|
||||||
|
20010102.00000000
|
||||||
|
102030.00000000
|
||||||
|
102030.00000000
|
||||||
|
102030.12300000
|
||||||
|
102030.12345600
|
||||||
|
20010102102030.00000000
|
||||||
|
20010102102030.12300000
|
||||||
|
20010102102030.12345600
|
||||||
|
include/show_binlog_events.inc
|
||||||
|
Log_name Pos Event_type Server_id End_log_pos Info
|
||||||
|
master-bin.000002 # Binlog_checkpoint # # master-bin.000002
|
||||||
|
master-bin.000002 # Gtid # # GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; CREATE TABLE t1 (a DECIMAL(30,8))
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (10)
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (11)
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (12.1)
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES ('13')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (DATE'2001-01-02')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIME'10:20:30')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIME'10:20:30')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIME'10:20:30.123')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIME'10:20:30.123456')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIMESTAMP'2001-01-02 10:20:30')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIMESTAMP'2001-01-02 10:20:30.123')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
|
||||||
|
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (TIMESTAMP'2001-01-02 10:20:30.123456')
|
||||||
|
master-bin.000002 # Query # # COMMIT
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET TIMESTAMP=DEFAULT;
|
||||||
|
@ -28,3 +28,29 @@ prepare s from "insert into t1 select 100 limit ?";
|
|||||||
set @a=100; execute s using @a;
|
set @a=100; execute s using @a;
|
||||||
source include/show_binlog_events.inc;
|
source include/show_binlog_events.inc;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
FLUSH LOGS;
|
||||||
|
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-02 10:20:30.123456');
|
||||||
|
CREATE TABLE t1 (a DECIMAL(30,8));
|
||||||
|
PREPARE stmt FROM 'INSERT INTO t1 VALUES (?)';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
EXECUTE stmt USING 11e0;
|
||||||
|
EXECUTE stmt USING 12.1;
|
||||||
|
EXECUTE stmt USING '13';
|
||||||
|
EXECUTE stmt USING CURRENT_DATE;
|
||||||
|
EXECUTE stmt USING MAKETIME(10,20,30);
|
||||||
|
EXECUTE stmt USING CURRENT_TIME;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(3);
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(6);
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(3);
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(6);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
--let $binlog_file = LAST
|
||||||
|
source include/show_binlog_events.inc;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET TIMESTAMP=DEFAULT;
|
||||||
|
@ -3672,3 +3672,174 @@ DROP TABLE t1,t2;
|
|||||||
|
|
||||||
|
|
||||||
--echo # End of 10.0 tests
|
--echo # End of 10.0 tests
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Start of 10.2 tests
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Using a simple expressions as an EXECUTE parameter
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
EXECUTE stmt USING TO_BASE64('xxx');
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ?+? FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10, 10 + 10;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT CONCAT(?,?) FROM DUAL';
|
||||||
|
EXECUTE stmt USING 'xxx', CONCAT('yyy','zzz');
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Testing disallowed expressions in USING
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
--error ER_PARSE_ERROR
|
||||||
|
EXECUTE stmt USING (SELECT 1);
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
CREATE FUNCTION f1() RETURNS VARCHAR(10) RETURN 'test';
|
||||||
|
PREPARE stmt FROM 'SELECT ? FROM DUAL';
|
||||||
|
--error ER_SUBQUERIES_NOT_SUPPORTED
|
||||||
|
EXECUTE stmt USING f1();
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Testing erroneous expressions in USING
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ?';
|
||||||
|
--error ER_CANT_AGGREGATE_2COLLATIONS
|
||||||
|
EXECUTE stmt USING _latin1'a'=_latin2'a';
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'SELECT ?';
|
||||||
|
--error ER_OPERAND_COLUMNS
|
||||||
|
EXECUTE stmt USING ROW(1,2);
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Creating tables from EXECUTE parameters
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
PREPARE stmt FROM 'CREATE TABLE t1 AS SELECT ? AS c1 FROM DUAL';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING 10.123;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING 10.123e0;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_DATE;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(3);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIMESTAMP(6);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME;
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(3);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
EXECUTE stmt USING CURRENT_TIME(6);
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Using a user variable as an EXECUTE..USING out parameter
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER /;
|
||||||
|
CREATE PROCEDURE p1(OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
SET a:= 10;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
DELIMITER ;/
|
||||||
|
SET @a=1;
|
||||||
|
CALL p1(@a);
|
||||||
|
SELECT @a;
|
||||||
|
SET @a=2;
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING @a;
|
||||||
|
SELECT @a;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Using an SP variable as an EXECUTE..USING out parameter
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER /;
|
||||||
|
CREATE PROCEDURE p1 (OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
SET a=10;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
CREATE PROCEDURE p2 (OUT a INT)
|
||||||
|
BEGIN
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING a;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
DELIMITER ;/
|
||||||
|
SET @a= 1;
|
||||||
|
CALL p2(@a);
|
||||||
|
SELECT @a;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Testing re-prepare on a table metadata update between PREPARE and EXECUTE
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
DELIMITER /;
|
||||||
|
CREATE PROCEDURE p1(a INT)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO t1 VALUES (a);
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
DELIMITER ;/
|
||||||
|
PREPARE stmt FROM 'CALL p1(?)';
|
||||||
|
EXECUTE stmt USING 10;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=NEW.a+1;
|
||||||
|
EXECUTE stmt USING 20;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of MDEV-10709 Expressions as parameters to Dynamic SQL
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of 10.2 tests
|
||||||
|
--echo #
|
||||||
|
73
sql/item.cc
73
sql/item.cc
@ -3439,10 +3439,10 @@ bool Item_param::CONVERSION_INFO::convert(THD *thd, String *str)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Set parameter value from user variable value.
|
Set parameter value from Item.
|
||||||
|
|
||||||
@param thd Current thread
|
@param thd Current thread
|
||||||
@param entry User variable structure (NULL means use NULL value)
|
@param item Item
|
||||||
|
|
||||||
@retval
|
@retval
|
||||||
0 OK
|
0 OK
|
||||||
@ -3450,34 +3450,44 @@ bool Item_param::CONVERSION_INFO::convert(THD *thd, String *str)
|
|||||||
1 Out of memory
|
1 Out of memory
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
|
bool Item_param::set_from_item(THD *thd, Item *item)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Item_param::set_from_user_var");
|
DBUG_ENTER("Item_param::set_from_item");
|
||||||
if (entry && entry->value)
|
if (limit_clause_param)
|
||||||
{
|
{
|
||||||
unsigned_flag= entry->unsigned_flag;
|
longlong val= item->val_int();
|
||||||
if (limit_clause_param)
|
if (item->null_value)
|
||||||
{
|
{
|
||||||
bool unused;
|
set_null();
|
||||||
set_int(entry->val_int(&unused), MY_INT64_NUM_DECIMAL_DIGITS);
|
DBUG_RETURN(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned_flag= item->unsigned_flag;
|
||||||
|
set_int(val, MY_INT64_NUM_DECIMAL_DIGITS);
|
||||||
item_type= Item::INT_ITEM;
|
item_type= Item::INT_ITEM;
|
||||||
set_handler_by_result_type(entry->type);
|
set_handler_by_result_type(item->result_type());
|
||||||
DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
|
DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
switch (entry->type) {
|
}
|
||||||
|
struct st_value tmp;
|
||||||
|
if (!item->store(&tmp, 0))
|
||||||
|
{
|
||||||
|
unsigned_flag= item->unsigned_flag;
|
||||||
|
switch (item->cmp_type()) {
|
||||||
case REAL_RESULT:
|
case REAL_RESULT:
|
||||||
set_double(*(double*)entry->value);
|
set_double(tmp.value.m_double);
|
||||||
item_type= Item::REAL_ITEM;
|
item_type= Item::REAL_ITEM;
|
||||||
set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
|
set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
|
||||||
break;
|
break;
|
||||||
case INT_RESULT:
|
case INT_RESULT:
|
||||||
set_int(*(longlong*)entry->value, MY_INT64_NUM_DECIMAL_DIGITS);
|
set_int(tmp.value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS);
|
||||||
item_type= Item::INT_ITEM;
|
item_type= Item::INT_ITEM;
|
||||||
set_handler_by_field_type(MYSQL_TYPE_LONGLONG);
|
set_handler_by_field_type(MYSQL_TYPE_LONGLONG);
|
||||||
break;
|
break;
|
||||||
case STRING_RESULT:
|
case STRING_RESULT:
|
||||||
{
|
{
|
||||||
value.cs_info.set(thd, entry->charset());
|
value.cs_info.set(thd, item->collation.collation);
|
||||||
/*
|
/*
|
||||||
Exact value of max_length is not known unless data is converted to
|
Exact value of max_length is not known unless data is converted to
|
||||||
charset of connection, so we have to set it later.
|
charset of connection, so we have to set it later.
|
||||||
@ -3485,13 +3495,13 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
|
|||||||
item_type= Item::STRING_ITEM;
|
item_type= Item::STRING_ITEM;
|
||||||
set_handler_by_field_type(MYSQL_TYPE_VARCHAR);
|
set_handler_by_field_type(MYSQL_TYPE_VARCHAR);
|
||||||
|
|
||||||
if (set_str((const char *)entry->value, entry->length))
|
if (set_str(tmp.m_string.ptr(), tmp.m_string.length()))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DECIMAL_RESULT:
|
case DECIMAL_RESULT:
|
||||||
{
|
{
|
||||||
const my_decimal *ent_value= (const my_decimal *)entry->value;
|
const my_decimal *ent_value= &tmp.m_decimal;
|
||||||
my_decimal2decimal(ent_value, &decimal_value);
|
my_decimal2decimal(ent_value, &decimal_value);
|
||||||
state= DECIMAL_VALUE;
|
state= DECIMAL_VALUE;
|
||||||
decimals= ent_value->frac;
|
decimals= ent_value->frac;
|
||||||
@ -3502,8 +3512,17 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
|
|||||||
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
|
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ROW_RESULT:
|
|
||||||
case TIME_RESULT:
|
case TIME_RESULT:
|
||||||
|
{
|
||||||
|
value.time= tmp.value.m_time;
|
||||||
|
state= TIME_VALUE;
|
||||||
|
max_length= item->max_length;
|
||||||
|
decimals= item->decimals;
|
||||||
|
item_type= Item::DATE_ITEM;
|
||||||
|
set_handler(item->type_handler());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ROW_RESULT:
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
set_null();
|
set_null();
|
||||||
}
|
}
|
||||||
@ -3742,18 +3761,34 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
|
|||||||
break;
|
break;
|
||||||
case TIME_VALUE:
|
case TIME_VALUE:
|
||||||
{
|
{
|
||||||
|
static const uint32 typelen= 9; // "TIMESTAMP" is the longest type name
|
||||||
char *buf, *ptr;
|
char *buf, *ptr;
|
||||||
str->length(0);
|
str->length(0);
|
||||||
/*
|
/*
|
||||||
TODO: in case of error we need to notify replication
|
TODO: in case of error we need to notify replication
|
||||||
that binary log contains wrong statement
|
that binary log contains wrong statement
|
||||||
*/
|
*/
|
||||||
if (str->reserve(MAX_DATE_STRING_REP_LENGTH+3))
|
if (str->reserve(MAX_DATE_STRING_REP_LENGTH + 3 + typelen))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Create date string inplace */
|
/* Create date string inplace */
|
||||||
|
switch (value.time.time_type) {
|
||||||
|
case MYSQL_TIMESTAMP_DATE:
|
||||||
|
str->append(C_STRING_WITH_LEN("DATE"));
|
||||||
|
break;
|
||||||
|
case MYSQL_TIMESTAMP_TIME:
|
||||||
|
str->append(C_STRING_WITH_LEN("TIME"));
|
||||||
|
break;
|
||||||
|
case MYSQL_TIMESTAMP_DATETIME:
|
||||||
|
str->append(C_STRING_WITH_LEN("TIMESTAMP"));
|
||||||
|
break;
|
||||||
|
case MYSQL_TIMESTAMP_ERROR:
|
||||||
|
case MYSQL_TIMESTAMP_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DBUG_ASSERT(str->length() <= typelen);
|
||||||
buf= str->c_ptr_quick();
|
buf= str->c_ptr_quick();
|
||||||
ptr= buf;
|
ptr= buf + str->length();
|
||||||
*ptr++= '\'';
|
*ptr++= '\'';
|
||||||
ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals);
|
ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals);
|
||||||
*ptr++= '\'';
|
*ptr++= '\'';
|
||||||
|
76
sql/item.h
76
sql/item.h
@ -30,6 +30,29 @@
|
|||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
#include <ma_dyncol.h>
|
#include <ma_dyncol.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
A prototype for a C-compatible structure to store a value of any data type.
|
||||||
|
Currently it has to stay in /sql, as it depends on String and my_decimal.
|
||||||
|
We'll do the following changes:
|
||||||
|
1. add pure C "struct st_string" and "struct st_my_decimal"
|
||||||
|
2. change type of m_string to struct st_string and move inside the union
|
||||||
|
3. change type of m_decmal to struct st_my_decimal and move inside the union
|
||||||
|
4. move the definition to some file in /include
|
||||||
|
*/
|
||||||
|
struct st_value
|
||||||
|
{
|
||||||
|
enum enum_dynamic_column_type m_type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
longlong m_longlong;
|
||||||
|
double m_double;
|
||||||
|
MYSQL_TIME m_time;
|
||||||
|
} value;
|
||||||
|
String m_string;
|
||||||
|
my_decimal m_decimal;
|
||||||
|
};
|
||||||
|
|
||||||
C_MODE_END
|
C_MODE_END
|
||||||
|
|
||||||
const char *dbug_print_item(Item *item);
|
const char *dbug_print_item(Item *item);
|
||||||
@ -742,6 +765,57 @@ public:
|
|||||||
supposed to be applied recursively.
|
supposed to be applied recursively.
|
||||||
*/
|
*/
|
||||||
virtual inline void quick_fix_field() { fixed= 1; }
|
virtual inline void quick_fix_field() { fixed= 1; }
|
||||||
|
|
||||||
|
bool store(struct st_value *value, ulonglong fuzzydate)
|
||||||
|
{
|
||||||
|
switch (cmp_type()) {
|
||||||
|
case INT_RESULT:
|
||||||
|
{
|
||||||
|
value->m_type= unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
|
||||||
|
value->value.m_longlong= val_int();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REAL_RESULT:
|
||||||
|
{
|
||||||
|
value->m_type= DYN_COL_DOUBLE;
|
||||||
|
value->value.m_double= val_real();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DECIMAL_RESULT:
|
||||||
|
{
|
||||||
|
value->m_type= DYN_COL_DECIMAL;
|
||||||
|
my_decimal *dec= val_decimal(&value->m_decimal);
|
||||||
|
if (dec != &value->m_decimal && !null_value)
|
||||||
|
my_decimal2decimal(dec, &value->m_decimal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STRING_RESULT:
|
||||||
|
{
|
||||||
|
value->m_type= DYN_COL_STRING;
|
||||||
|
String *str= val_str(&value->m_string);
|
||||||
|
if (str != &value->m_string && !null_value)
|
||||||
|
value->m_string.set(str->ptr(), str->length(), str->charset());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TIME_RESULT:
|
||||||
|
{
|
||||||
|
value->m_type= DYN_COL_DATETIME;
|
||||||
|
get_date(&value->value.m_time, fuzzydate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ROW_RESULT:
|
||||||
|
DBUG_ASSERT(false);
|
||||||
|
null_value= true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (null_value)
|
||||||
|
{
|
||||||
|
value->m_type= DYN_COL_NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Function returns 1 on overflow and -1 on fatal errors */
|
/* Function returns 1 on overflow and -1 on fatal errors */
|
||||||
int save_in_field_no_warnings(Field *field, bool no_conversions);
|
int save_in_field_no_warnings(Field *field, bool no_conversions);
|
||||||
virtual int save_in_field(Field *field, bool no_conversions);
|
virtual int save_in_field(Field *field, bool no_conversions);
|
||||||
@ -2816,7 +2890,7 @@ public:
|
|||||||
bool set_str(const char *str, ulong length);
|
bool set_str(const char *str, ulong length);
|
||||||
bool set_longdata(const char *str, ulong length);
|
bool set_longdata(const char *str, ulong length);
|
||||||
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
|
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
|
||||||
bool set_from_user_var(THD *thd, const user_var_entry *entry);
|
bool set_from_item(THD *thd, Item *item);
|
||||||
void reset();
|
void reset();
|
||||||
/*
|
/*
|
||||||
Assign placeholder value from bind data.
|
Assign placeholder value from bind data.
|
||||||
|
@ -1306,33 +1306,27 @@ bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
|
|||||||
thd->lex->prepared_stmt_params.elements);
|
thd->lex->prepared_stmt_params.elements);
|
||||||
|
|
||||||
List_iterator_fast<Item_param> item_param_it(*sp_params);
|
List_iterator_fast<Item_param> item_param_it(*sp_params);
|
||||||
List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params);
|
List_iterator_fast<Item> param_it(thd->lex->prepared_stmt_params);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Item_param *item_param= item_param_it++;
|
Item_param *item_param= item_param_it++;
|
||||||
LEX_STRING *user_var_name= user_var_name_it++;
|
Item *param= param_it++;
|
||||||
|
Settable_routine_parameter *sparam;
|
||||||
|
|
||||||
if (!item_param || !user_var_name)
|
if (!item_param || !param)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!item_param->get_out_param_info())
|
if (!item_param->get_out_param_info())
|
||||||
continue; // It's an IN-parameter.
|
continue; // It's an IN-parameter.
|
||||||
|
|
||||||
Item_func_set_user_var *suv=
|
if (!(sparam= param->get_settable_routine_parameter()))
|
||||||
new (thd->mem_root) Item_func_set_user_var(thd, *user_var_name, item_param);
|
{
|
||||||
/*
|
DBUG_ASSERT(0);
|
||||||
Item_func_set_user_var is not fixed after construction, call
|
continue;
|
||||||
fix_fields().
|
}
|
||||||
*/
|
|
||||||
if (suv->fix_fields(thd, NULL))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (suv->check(FALSE))
|
sparam->set_value(thd, thd->spcont, reinterpret_cast<Item **>(&item_param));
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (suv->update())
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -2728,7 +2728,7 @@ public:
|
|||||||
/* If true, prepared_stmt_code is a name of variable that holds the query */
|
/* If true, prepared_stmt_code is a name of variable that holds the query */
|
||||||
bool prepared_stmt_code_is_varref;
|
bool prepared_stmt_code_is_varref;
|
||||||
/* Names of user variables holding parameters (in EXECUTE) */
|
/* Names of user variables holding parameters (in EXECUTE) */
|
||||||
List<LEX_STRING> prepared_stmt_params;
|
List<Item> prepared_stmt_params;
|
||||||
sp_head *sphead;
|
sp_head *sphead;
|
||||||
sp_name *spname;
|
sp_name *spname;
|
||||||
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
|
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
|
||||||
|
@ -186,9 +186,9 @@ public:
|
|||||||
#else
|
#else
|
||||||
bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
|
bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
|
||||||
#endif
|
#endif
|
||||||
bool (*set_params_from_vars)(Prepared_statement *stmt,
|
bool (*set_params_from_actual_params)(Prepared_statement *stmt,
|
||||||
List<LEX_STRING>& varnames,
|
List<Item> &list,
|
||||||
String *expanded_query);
|
String *expanded_query);
|
||||||
public:
|
public:
|
||||||
Prepared_statement(THD *thd_arg);
|
Prepared_statement(THD *thd_arg);
|
||||||
virtual ~Prepared_statement();
|
virtual ~Prepared_statement();
|
||||||
@ -1141,31 +1141,27 @@ swap_parameter_array(Item_param **param_array_dst,
|
|||||||
Assign prepared statement parameters from user variables.
|
Assign prepared statement parameters from user variables.
|
||||||
|
|
||||||
@param stmt Statement
|
@param stmt Statement
|
||||||
@param varnames List of variables. Caller must ensure that number
|
@param params A list of parameters. Caller must ensure that number
|
||||||
of variables in the list is equal to number of statement
|
of parameters in the list is equal to number of statement
|
||||||
parameters
|
parameters
|
||||||
@param query Ignored
|
@param query Ignored
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool insert_params_from_vars(Prepared_statement *stmt,
|
static bool
|
||||||
List<LEX_STRING>& varnames,
|
insert_params_from_actual_params(Prepared_statement *stmt,
|
||||||
String *query __attribute__((unused)))
|
List<Item> ¶ms,
|
||||||
|
String *query __attribute__((unused)))
|
||||||
{
|
{
|
||||||
Item_param **begin= stmt->param_array;
|
Item_param **begin= stmt->param_array;
|
||||||
Item_param **end= begin + stmt->param_count;
|
Item_param **end= begin + stmt->param_count;
|
||||||
user_var_entry *entry;
|
List_iterator<Item> param_it(params);
|
||||||
LEX_STRING *varname;
|
DBUG_ENTER("insert_params_from_actual_params");
|
||||||
List_iterator<LEX_STRING> var_it(varnames);
|
|
||||||
DBUG_ENTER("insert_params_from_vars");
|
|
||||||
|
|
||||||
for (Item_param **it= begin; it < end; ++it)
|
for (Item_param **it= begin; it < end; ++it)
|
||||||
{
|
{
|
||||||
Item_param *param= *it;
|
Item_param *param= *it;
|
||||||
varname= var_it++;
|
Item *ps_param= param_it++;
|
||||||
entry= (user_var_entry*)my_hash_search(&stmt->thd->user_vars,
|
if (param->set_from_item(stmt->thd, ps_param) ||
|
||||||
(uchar*) varname->str,
|
|
||||||
varname->length);
|
|
||||||
if (param->set_from_user_var(stmt->thd, entry) ||
|
|
||||||
param->convert_str_value(stmt->thd))
|
param->convert_str_value(stmt->thd))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
@ -1174,45 +1170,41 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Do the same as insert_params_from_vars but also construct query text for
|
Do the same as insert_params_from_actual_params
|
||||||
binary log.
|
but also construct query text for binary log.
|
||||||
|
|
||||||
@param stmt Prepared statement
|
@param stmt Prepared statement
|
||||||
@param varnames List of variables. Caller must ensure that number of
|
@param params A list of parameters. Caller must ensure that number of
|
||||||
variables in the list is equal to number of statement
|
parameters in the list is equal to number of statement
|
||||||
parameters
|
parameters
|
||||||
@param query The query with parameter markers replaced with corresponding
|
@param query The query with parameter markers replaced with corresponding
|
||||||
user variables that were used to execute the query.
|
user variables that were used to execute the query.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
static bool
|
||||||
List<LEX_STRING>& varnames,
|
insert_params_from_actual_params_with_log(Prepared_statement *stmt,
|
||||||
String *query)
|
List<Item> ¶ms,
|
||||||
|
String *query)
|
||||||
{
|
{
|
||||||
Item_param **begin= stmt->param_array;
|
Item_param **begin= stmt->param_array;
|
||||||
Item_param **end= begin + stmt->param_count;
|
Item_param **end= begin + stmt->param_count;
|
||||||
user_var_entry *entry;
|
List_iterator<Item> param_it(params);
|
||||||
LEX_STRING *varname;
|
|
||||||
List_iterator<LEX_STRING> var_it(varnames);
|
|
||||||
THD *thd= stmt->thd;
|
THD *thd= stmt->thd;
|
||||||
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
|
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
|
||||||
|
|
||||||
DBUG_ENTER("insert_params_from_vars_with_log");
|
DBUG_ENTER("insert_params_from_actual_params_with_log");
|
||||||
|
|
||||||
for (Item_param **it= begin; it < end; ++it)
|
for (Item_param **it= begin; it < end; ++it)
|
||||||
{
|
{
|
||||||
Item_param *param= *it;
|
Item_param *param= *it;
|
||||||
varname= var_it++;
|
Item *ps_param= param_it++;
|
||||||
|
|
||||||
entry= (user_var_entry *) my_hash_search(&thd->user_vars, (uchar*)
|
|
||||||
varname->str, varname->length);
|
|
||||||
/*
|
/*
|
||||||
We have to call the setup_one_conversion_function() here to set
|
We have to call the setup_one_conversion_function() here to set
|
||||||
the parameter's members that might be needed further
|
the parameter's members that might be needed further
|
||||||
(e.g. value.cs_info.character_set_client is used in the query_val_str()).
|
(e.g. value.cs_info.character_set_client is used in the query_val_str()).
|
||||||
*/
|
*/
|
||||||
setup_one_conversion_function(thd, param, param->field_type());
|
setup_one_conversion_function(thd, param, param->field_type());
|
||||||
if (param->set_from_user_var(thd, entry))
|
if (param->set_from_item(thd, ps_param))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
if (acc.append(param))
|
if (acc.append(param))
|
||||||
@ -3042,7 +3034,49 @@ void mysql_sql_stmt_execute(THD *thd)
|
|||||||
|
|
||||||
DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
|
DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
|
||||||
|
|
||||||
|
// Fix all Items in the USING list
|
||||||
|
List_iterator_fast<Item> param_it(lex->prepared_stmt_params);
|
||||||
|
while (Item *param= param_it++)
|
||||||
|
{
|
||||||
|
if (param->fix_fields(thd, 0) || param->check_cols(1))
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
thd->free_list can already have some Items.
|
||||||
|
|
||||||
|
Example queries:
|
||||||
|
- SET STATEMENT var=expr FOR EXECUTE stmt;
|
||||||
|
- EXECUTE stmt USING expr;
|
||||||
|
|
||||||
|
E.g. for a query like this:
|
||||||
|
PREPARE stmt FROM 'INSERT INTO t1 VALUES (@@max_sort_length)';
|
||||||
|
SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt;
|
||||||
|
thd->free_list contains a pointer to Item_int corresponding to 2048.
|
||||||
|
|
||||||
|
If Prepared_statement::execute() notices that the table metadata for "t1"
|
||||||
|
has changed since PREPARE, it returns an error asking the calling
|
||||||
|
Prepared_statement::execute_loop() to re-prepare the statement.
|
||||||
|
Before returning the error, Prepared_statement::execute()
|
||||||
|
calls Prepared_statement::cleanup_stmt(),
|
||||||
|
which calls thd->cleanup_after_query(),
|
||||||
|
which calls Query_arena::free_items().
|
||||||
|
|
||||||
|
We hide "external" Items, e.g. those created while parsing the
|
||||||
|
"SET STATEMENT" or "USING" parts of the query,
|
||||||
|
so they don't get freed in case of re-prepare.
|
||||||
|
See MDEV-10702 Crash in SET STATEMENT FOR EXECUTE
|
||||||
|
*/
|
||||||
|
Item *free_list_backup= thd->free_list;
|
||||||
|
thd->free_list= NULL; // Hide the external (e.g. "SET STATEMENT") Items
|
||||||
(void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
|
(void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
|
||||||
|
thd->free_items(); // Free items created by execute_loop()
|
||||||
|
/*
|
||||||
|
Now restore the "external" (e.g. "SET STATEMENT") Item list.
|
||||||
|
It will be freed normaly in THD::cleanup_after_query().
|
||||||
|
*/
|
||||||
|
thd->free_list= free_list_backup;
|
||||||
|
|
||||||
stmt->lex->restore_set_statement_var();
|
stmt->lex->restore_set_statement_var();
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -3490,7 +3524,7 @@ void Prepared_statement::setup_set_params()
|
|||||||
|
|
||||||
if (replace_params_with_values)
|
if (replace_params_with_values)
|
||||||
{
|
{
|
||||||
set_params_from_vars= insert_params_from_vars_with_log;
|
set_params_from_actual_params= insert_params_from_actual_params_with_log;
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
set_params= insert_params_with_log;
|
set_params= insert_params_with_log;
|
||||||
#else
|
#else
|
||||||
@ -3499,7 +3533,7 @@ void Prepared_statement::setup_set_params()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set_params_from_vars= insert_params_from_vars;
|
set_params_from_actual_params= insert_params_from_actual_params;
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
set_params= insert_params;
|
set_params= insert_params;
|
||||||
#else
|
#else
|
||||||
@ -3809,8 +3843,8 @@ Prepared_statement::set_parameters(String *expanded_query,
|
|||||||
if (is_sql_ps)
|
if (is_sql_ps)
|
||||||
{
|
{
|
||||||
/* SQL prepared statement */
|
/* SQL prepared statement */
|
||||||
res= set_params_from_vars(this, thd->lex->prepared_stmt_params,
|
res= set_params_from_actual_params(this, thd->lex->prepared_stmt_params,
|
||||||
expanded_query);
|
expanded_query);
|
||||||
}
|
}
|
||||||
else if (param_count)
|
else if (param_count)
|
||||||
{
|
{
|
||||||
|
@ -2255,7 +2255,14 @@ execute:
|
|||||||
|
|
||||||
execute_using:
|
execute_using:
|
||||||
/* nothing */
|
/* nothing */
|
||||||
| USING execute_var_list
|
| USING { Lex->expr_allows_subselect= false; }
|
||||||
|
execute_var_list
|
||||||
|
{
|
||||||
|
if (Lex->table_or_sp_used())
|
||||||
|
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
|
||||||
|
"EXECUTE..USING"));
|
||||||
|
Lex->expr_allows_subselect= true;
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
execute_var_list:
|
execute_var_list:
|
||||||
@ -2264,12 +2271,9 @@ execute_var_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
execute_var_ident:
|
execute_var_ident:
|
||||||
'@' ident_or_text
|
expr
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
if (Lex->prepared_stmt_params.push_back($1, thd->mem_root))
|
||||||
LEX_STRING *lexstr= (LEX_STRING*)thd->memdup(&$2, sizeof(LEX_STRING));
|
|
||||||
if (!lexstr || lex->prepared_stmt_params.push_back(lexstr,
|
|
||||||
thd->mem_root))
|
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user