Commit 46dc7bdf authored by Alexander Barkov's avatar Alexander Barkov

MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions

MDEV-10867 PREPARE..EXECUTE is not consistent about non-ASCII characters
parent e1a212eb
SET TIMESTAMP=10000;
create table t2 (c char(30)) charset=ucs2;
set @v=convert('abc' using ucs2);
reset master;
insert into t2 values (@v);
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # User var # # @`v`=_ucs2 X'006100620063' COLLATE ucs2_general_ci
master-bin.000001 # Query # # use `test`; insert into t2 values (@v)
master-bin.000001 # Query # # COMMIT
flush logs;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
ROLLBACK/*!*/;
BEGIN
/*!*/;
SET @`v`:=_ucs2 X'006100620063' COLLATE `ucs2_general_ci`/*!*/;
use `test`/*!*/;
SET TIMESTAMP=10000/*!*/;
SET @@session.pseudo_thread_id=999999999/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
SET @@session.sql_mode=1342177280/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C latin1 *//*!*/;
SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
insert into t2 values (@v)
/*!*/;
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
drop table t2;
#
# Start of 10.2 tests
#
#
# MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
FLUSH LOGS;
SET NAMES utf8;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8);
EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (''ä(i1)'')';
EXECUTE IMMEDIATE CONVERT('INSERT INTO t1 VALUES (''ä(i2)'')' USING ucs2);
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(i3)'')' USING ucs2);
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (''ä(p1)'')';
EXECUTE stmt;
PREPARE stmt FROM CONVERT('INSERT INTO t1 VALUES (''ä(p2)'')' USING ucs2);
EXECUTE stmt;
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(p3)'')' USING ucs2);
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT * FROM t1;
a
ä(i1)
ä(i2)
ä(i3)
ä(p1)
ä(p2)
ä(p3)
DROP TABLE t1;
FLUSH LOGS;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Start: binlog v 4, server v #.##.## created 700101 6:46:40
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Gtid list [#-#-#]
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000002
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000003
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
/*!100001 SET @@session.gtid_domain_id=#*//*!*/;
/*!100001 SET @@session.server_id=#*//*!*/;
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
use `test`/*!*/;
SET TIMESTAMP=10000/*!*/;
SET @@session.pseudo_thread_id=#/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
SET @@session.sql_mode=1342177280/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8)
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i1)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i2)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i3)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p1)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p2)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p3)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
DROP TABLE `t1` /* generated by server */
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Rotate to master-bin.000004 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
#
# End of 10.2 tests
#
......@@ -20,3 +20,40 @@ let $MYSQLD_DATADIR= `select @@datadir`;
drop table t2;
# End of 4.1 tests
--echo #
--echo # Start of 10.2 tests
--echo #
--echo #
--echo # MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #
FLUSH LOGS;
SET NAMES utf8;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8);
EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (''ä(i1)'')';
EXECUTE IMMEDIATE CONVERT('INSERT INTO t1 VALUES (''ä(i2)'')' USING ucs2);
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(i3)'')' USING ucs2);
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (''ä(p1)'')';
EXECUTE stmt;
PREPARE stmt FROM CONVERT('INSERT INTO t1 VALUES (''ä(p2)'')' USING ucs2);
EXECUTE stmt;
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(p3)'')' USING ucs2);
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT * FROM t1;
DROP TABLE t1;
FLUSH LOGS;
let $MYSQLD_DATADIR= `select @@datadir`;
--replace_regex /# at [0-9]*/# at #/ /(exec_time=|end_log_pos |Xid = |thread_id=|server id |table id |mapped to number )[0-9]+/\1#/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Gtid list [[][0-9]+-[0-9]+-[0-9]+[\]]/Gtid list [#-#-#]/ /session[.](gtid_domain_id|server_id|gtid_seq_no)=[0-9]+/session.\1=#/
--exec $MYSQL_BINLOG --base64-output=decode-rows -vv $MYSQLD_DATADIR/master-bin.000003
--echo #
--echo # End of 10.2 tests
--echo #
......@@ -6230,7 +6230,60 @@ COLLATION("a")
ucs2_general_ci
SET @stmt='SELECT COLLATION("a")';
EXECUTE IMMEDIATE @stmt;
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 '' at line 1
COLLATION("a")
ucs2_general_ci
#
# MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
SET NAMES utf8, collation_connection=ucs2_bin;
SET @stmt='SELECT COLLATION(''a'')';
EXECUTE IMMEDIATE @stmt;
COLLATION('a')
ucs2_bin
SET NAMES utf8, character_set_connection=ucs2;
SET @stmt='SELECT COLLATION(''a'')';
EXECUTE IMMEDIATE @stmt;
COLLATION('a')
ucs2_general_ci
EXECUTE IMMEDIATE CONCAT('SELECT ''a'' FROM DUAL');
a
a
SELECT HEX('aä') FROM DUAL;
HEX('aä')
006100E4
EXECUTE IMMEDIATE 'SELECT HEX(''aä'') FROM DUAL';
HEX('aä')
006100E4
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM DUAL');
HEX('aä')
006100E4
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', 'DUAL');
HEX('aä')
006100E4
PREPARE stmt FROM 'SELECT HEX(''aä'') FROM DUAL';
EXECUTE stmt;
HEX('aä')
006100E4
DEALLOCATE PREPARE stmt;
SET @table='DUAL';
SELECT HEX(@table);
HEX(@table)
004400550041004C
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', @table);
HEX('aä')
006100E4
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', CONVERT(@table USING utf8));
HEX('aä')
006100E4
SET @stmt='SELECT HEX(''aä'') FROM DUAL';
EXECUTE IMMEDIATE @stmt;
HEX('aä')
006100E4
PREPARE stmt FROM @stmt;
EXECUTE stmt;
HEX('aä')
006100E4
DEALLOCATE PREPARE stmt;
#
# End of 10.2 tests
#
......@@ -4014,5 +4014,27 @@ DROP TABLE t1;
#
SET STORAGE_ENGINE=Default;
#
# MDEV-10867 PREPARE..EXECUTE is not consistent about non-ASCII characters
#
SET NAMES utf8mb4;
SELECT '😎' AS c;
c
😎
SET @src='SELECT ''😎'' AS c';
PREPARE stmt FROM @src;
EXECUTE stmt;
c
😎
EXECUTE IMMEDIATE @src;
c
😎
PREPARE stmt FROM 'SELECT ''😎'' AS c';
EXECUTE stmt;
c
😎
EXECUTE IMMEDIATE 'SELECT ''😎'' AS c';
c
😎
#
# End of 10.2 tests
#
......@@ -4502,5 +4502,150 @@ DROP FUNCTION get_status_var;
# End of MDEV-10585 EXECUTE IMMEDIATE statement
#
#
# MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
#
# Testing erroneous and diallowed prepare source
#
EXECUTE IMMEDIATE CONCAT(_latin1'SELECT 1 AS c FROM ', _latin2 'DUAL');
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat'
PREPARE stmt FROM CONCAT(_latin1'SELECT 1 AS c FROM ', _latin2 'DUAL');
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat'
EXECUTE IMMEDIATE (SELECT '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 'SELECT 1')' at line 1
PREPARE stmt FROM (SELECT '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 'SELECT 1')' at line 1
EXECUTE IMMEDIATE a;
ERROR 42S22: Unknown column 'a' in 'field list'
PREPARE stmt FROM a;
ERROR 42S22: Unknown column 'a' in 'field list'
EXECUTE IMMEDIATE NULL;
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 'NULL' at line 1
PREPARE stmt FROM NULL;
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 'NULL' at line 1
EXECUTE IMMEDIATE CONCAT(NULL);
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 'NULL' at line 1
PREPARE stmt FROM CONCAT(NULL);
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 'NULL' at line 1
EXECUTE IMMEDIATE ? 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 '? USING 'SELECT 1'' at line 1
EXECUTE IMMEDIATE 10;
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 '10' at line 1
EXECUTE IMMEDIATE TIME'10:20:30';
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 '10:20:30' at line 1
EXECUTE IMMEDIATE ROW('SELECT 1','SELECT 2');
ERROR 21000: Operand should contain 1 column(s)
EXECUTE IMMEDIATE MAX('SELECT 1 AS c');
ERROR HY000: Invalid use of group function
EXECUTE IMMEDIATE DEFAULT(a);
ERROR 42S22: Unknown column 'a' in 'field list'
EXECUTE IMMEDIATE VALUES(a);
ERROR 42S22: Unknown column 'a' in 'field list'
CREATE FUNCTION f1() RETURNS VARCHAR(64) RETURN 't1';
EXECUTE IMMEDIATE f1();
ERROR 42000: EXECUTE IMMEDIATE does not support subqueries or stored functions
PREPARE stmt FROM f1();
ERROR 42000: PREPARE..FROM does not support subqueries or stored functions
DROP FUNCTION f1;
EXECUTE IMMEDIATE non_existent();
ERROR 42000: EXECUTE IMMEDIATE does not support subqueries or stored functions
#
# Testing literals in prepare source
#
EXECUTE IMMEDIATE N'SELECT 1 AS c';
c
1
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c';
c
1
EXECUTE IMMEDIATE 'SELECT ' '1' ' AS c' ' FROM ' 'DUAL';
c
1
EXECUTE IMMEDIATE 0x53454C4543542031 /*This is 'SELECT 1'*/;
1
1
#
# Testing user variables in prepare source
#
SET @stmt='SELECT 1 AS c FROM DUAL';
EXECUTE IMMEDIATE @stmt;
c
1
PREPARE stmt FROM @stmt;
EXECUTE stmt;
c
1
DEALLOCATE PREPARE stmt;
SET @table_name='DUAL';
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS a FROM ', @table_name);
a
1
PREPARE stmt FROM CONCAT('SELECT 1 AS a FROM ', @table_name);
EXECUTE stmt;
a
1
DEALLOCATE PREPARE stmt;
#
# Testing SP parameters and variables in prepare source
#
CREATE PROCEDURE p1(table_name VARCHAR(64))
BEGIN
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS c FROM ', table_name);
END;
$$
CALL p1('DUAL');
c
1
DROP PROCEDURE p1;
CREATE PROCEDURE p1()
BEGIN
DECLARE table_name VARCHAR(64) DEFAULT 'DUAL';
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS c FROM ', table_name);
END;
$$
CALL p1();
c
1
DROP PROCEDURE p1;
#
# Testing complex expressions
#
EXECUTE IMMEDIATE CONVERT('SELECT 1 AS c' USING utf8);
c
1
EXECUTE IMMEDIATE CAST('SELECT 1 AS c' AS CHAR);
c
1
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c' COLLATE latin1_bin;
c
1
EXECUTE IMMEDIATE (((('SELECT 1 AS c'))));
c
1
EXECUTE IMMEDIATE CASE WHEN 1>2 THEN 'SELECT 1 AS c' ELSE 'SELECT 2 AS c' END;
c
2
EXECUTE IMMEDIATE TRIM('SELECT 1 AS c');
c
1
EXECUTE IMMEDIATE SUBSTRING('SELECT 1 AS c' FROM 1);
c
1
EXECUTE IMMEDIATE COALESCE(NULL, 'SELECT 1 AS c');
c
1
#
# Testing SET STATEMENT and system variables
#
CREATE TABLE t1 (a INT);
SET STATEMENT max_sort_length=1025 FOR EXECUTE IMMEDIATE CONCAT('INSERT INTO t1 VALUES (', @@max_sort_length, ')');
SELECT * FROM t1;
a
1025
DROP TABLE t1;
#
# End of MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
#
# End of 10.2 tests
#
......@@ -34,3 +34,186 @@ ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
drop table t2;
#
# Start of 10.2 tests
#
#
# MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
FLUSH LOGS;
SET NAMES utf8;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8);
EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (''ä(i1)'')';
EXECUTE IMMEDIATE CONVERT('INSERT INTO t1 VALUES (''ä(i2)'')' USING ucs2);
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(i3)'')' USING ucs2);
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (''ä(p1)'')';
EXECUTE stmt;
PREPARE stmt FROM CONVERT('INSERT INTO t1 VALUES (''ä(p2)'')' USING ucs2);
EXECUTE stmt;
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(p3)'')' USING ucs2);
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT * FROM t1;
a
ä(i1)
ä(i2)
ä(i3)
ä(p1)
ä(p2)
ä(p3)
DROP TABLE t1;
FLUSH LOGS;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Start: binlog v 4, server v #.##.## created 700101 6:46:40
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Gtid list [#-#-#]
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000002
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000003
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
/*!100001 SET @@session.gtid_domain_id=#*//*!*/;
/*!100001 SET @@session.server_id=#*//*!*/;
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
use `test`/*!*/;
SET TIMESTAMP=10000/*!*/;
SET @@session.pseudo_thread_id=#/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
SET @@session.sql_mode=1342177280/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8)
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(i1)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(i2)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(i3)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(p1)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(p2)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number #
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='ä(p3)' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
DROP TABLE `t1` /* generated by server */
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Rotate to master-bin.000004 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
#
# End of 10.2 tests
#
......@@ -39,3 +39,174 @@ ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
drop table t2;
#
# Start of 10.2 tests
#
#
# MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
#
FLUSH LOGS;
SET NAMES utf8;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8);
EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (''ä(i1)'')';
EXECUTE IMMEDIATE CONVERT('INSERT INTO t1 VALUES (''ä(i2)'')' USING ucs2);
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(i3)'')' USING ucs2);
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (''ä(p1)'')';
EXECUTE stmt;
PREPARE stmt FROM CONVERT('INSERT INTO t1 VALUES (''ä(p2)'')' USING ucs2);
EXECUTE stmt;
SET @stmt=CONVERT('INSERT INTO t1 VALUES (''ä(p3)'')' USING ucs2);
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT * FROM t1;
a
ä(i1)
ä(i2)
ä(i3)
ä(p1)
ä(p2)
ä(p3)
DROP TABLE t1;
FLUSH LOGS;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Start: binlog v 4, server v #.##.## created 700101 6:46:40
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Gtid list [#-#-#]
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000002
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000003
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
/*!100001 SET @@session.gtid_domain_id=#*//*!*/;
/*!100001 SET @@session.server_id=#*//*!*/;
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
use `test`/*!*/;
SET TIMESTAMP=10000/*!*/;
SET @@session.pseudo_thread_id=#/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
SET @@session.sql_mode=1342177280/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8)
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i1)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i2)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(i3)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p1)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p2)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-#
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
BEGIN
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
INSERT INTO t1 VALUES ('ä(p3)')
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
COMMIT
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX GTID #-#-# ddl
/*!100001 SET @@session.gtid_seq_no=#*//*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0
SET TIMESTAMP=10000/*!*/;
DROP TABLE `t1` /* generated by server */
/*!*/;
# at #
#700101 6:46:40 server id # end_log_pos # CRC32 XXX Rotate to master-bin.000004 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
#
# End of 10.2 tests
#
......@@ -980,12 +980,40 @@ let $coll_pad='ucs2_bin';
SET character_set_connection=ucs2;
EXECUTE IMMEDIATE 'SELECT COLLATION("a")';
# We don't support character sets with mbminlen>1 in the parser yet
# Returning "syntax error" is fine
SET @stmt='SELECT COLLATION("a")';
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE @stmt;
--echo #
--echo # MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #
SET NAMES utf8, collation_connection=ucs2_bin;
SET @stmt='SELECT COLLATION(''a'')';
EXECUTE IMMEDIATE @stmt;
SET NAMES utf8, character_set_connection=ucs2;
SET @stmt='SELECT COLLATION(''a'')';
EXECUTE IMMEDIATE @stmt;
EXECUTE IMMEDIATE CONCAT('SELECT ''a'' FROM DUAL');
SELECT HEX('aä') FROM DUAL;
EXECUTE IMMEDIATE 'SELECT HEX(''aä'') FROM DUAL';
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM DUAL');
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', 'DUAL');
PREPARE stmt FROM 'SELECT HEX(''aä'') FROM DUAL';
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @table='DUAL';
SELECT HEX(@table);
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', @table);
EXECUTE IMMEDIATE CONCAT('SELECT HEX(''aä'') FROM ', CONVERT(@table USING utf8));
SET @stmt='SELECT HEX(''aä'') FROM DUAL';
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--echo #
--echo # End of 10.2 tests
--echo #
......@@ -1964,6 +1964,21 @@ let $coll='utf8mb4_nopad_bin';
let $coll_pad='utf8mb4_bin';
--source include/ctype_pad_all_engines.inc
--echo #
--echo # MDEV-10867 PREPARE..EXECUTE is not consistent about non-ASCII characters
--echo #
SET NAMES utf8mb4;
SELECT '😎' AS c;
SET @src='SELECT ''😎'' AS c';
PREPARE stmt FROM @src;
EXECUTE stmt;
EXECUTE IMMEDIATE @src;
PREPARE stmt FROM 'SELECT ''😎'' AS c';
EXECUTE stmt;
EXECUTE IMMEDIATE 'SELECT ''😎'' AS c';
--echo #
--echo # End of 10.2 tests
--echo #
......@@ -4045,6 +4045,146 @@ DROP FUNCTION get_status_var;
--echo # End of MDEV-10585 EXECUTE IMMEDIATE statement
--echo #
--echo #
--echo # MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #
--echo #
--echo # Testing erroneous and diallowed prepare source
--echo #
--error ER_CANT_AGGREGATE_2COLLATIONS
EXECUTE IMMEDIATE CONCAT(_latin1'SELECT 1 AS c FROM ', _latin2 'DUAL');
--error ER_CANT_AGGREGATE_2COLLATIONS
PREPARE stmt FROM CONCAT(_latin1'SELECT 1 AS c FROM ', _latin2 'DUAL');
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE (SELECT 'SELECT 1');
--error ER_PARSE_ERROR
PREPARE stmt FROM (SELECT 'SELECT 1');
--error ER_BAD_FIELD_ERROR
EXECUTE IMMEDIATE a;
--error ER_BAD_FIELD_ERROR
PREPARE stmt FROM a;
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE NULL;
--error ER_PARSE_ERROR
PREPARE stmt FROM NULL;
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE CONCAT(NULL);
--error ER_PARSE_ERROR
PREPARE stmt FROM CONCAT(NULL);
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE ? USING 'SELECT 1';
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE 10;
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE TIME'10:20:30';
--error ER_OPERAND_COLUMNS
EXECUTE IMMEDIATE ROW('SELECT 1','SELECT 2');
--error ER_INVALID_GROUP_FUNC_USE
EXECUTE IMMEDIATE MAX('SELECT 1 AS c');
--error ER_BAD_FIELD_ERROR
EXECUTE IMMEDIATE DEFAULT(a);
--error ER_BAD_FIELD_ERROR
EXECUTE IMMEDIATE VALUES(a);
CREATE FUNCTION f1() RETURNS VARCHAR(64) RETURN 't1';
--error ER_SUBQUERIES_NOT_SUPPORTED
EXECUTE IMMEDIATE f1();
--error ER_SUBQUERIES_NOT_SUPPORTED
PREPARE stmt FROM f1();
DROP FUNCTION f1;
--error ER_SUBQUERIES_NOT_SUPPORTED
EXECUTE IMMEDIATE non_existent();
--echo #
--echo # Testing literals in prepare source
--echo #
EXECUTE IMMEDIATE N'SELECT 1 AS c';
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c';
EXECUTE IMMEDIATE 'SELECT ' '1' ' AS c' ' FROM ' 'DUAL';
EXECUTE IMMEDIATE 0x53454C4543542031 /*This is 'SELECT 1'*/;
--echo #
--echo # Testing user variables in prepare source
--echo #
SET @stmt='SELECT 1 AS c FROM DUAL';
EXECUTE IMMEDIATE @stmt;
PREPARE stmt FROM @stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @table_name='DUAL';
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS a FROM ', @table_name);
PREPARE stmt FROM CONCAT('SELECT 1 AS a FROM ', @table_name);
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--echo #
--echo # Testing SP parameters and variables in prepare source
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(table_name VARCHAR(64))
BEGIN
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS c FROM ', table_name);
END;
$$
DELIMITER ;$$
CALL p1('DUAL');
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
BEGIN
DECLARE table_name VARCHAR(64) DEFAULT 'DUAL';
EXECUTE IMMEDIATE CONCAT('SELECT 1 AS c FROM ', table_name);
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # Testing complex expressions
--echo #
EXECUTE IMMEDIATE CONVERT('SELECT 1 AS c' USING utf8);
EXECUTE IMMEDIATE CAST('SELECT 1 AS c' AS CHAR);
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c' COLLATE latin1_bin;
EXECUTE IMMEDIATE (((('SELECT 1 AS c'))));
EXECUTE IMMEDIATE CASE WHEN 1>2 THEN 'SELECT 1 AS c' ELSE 'SELECT 2 AS c' END;
EXECUTE IMMEDIATE TRIM('SELECT 1 AS c');
EXECUTE IMMEDIATE SUBSTRING('SELECT 1 AS c' FROM 1);
EXECUTE IMMEDIATE COALESCE(NULL, 'SELECT 1 AS c');
--echo #
--echo # Testing SET STATEMENT and system variables
--echo #
CREATE TABLE t1 (a INT);
SET STATEMENT max_sort_length=1025 FOR EXECUTE IMMEDIATE CONCAT('INSERT INTO t1 VALUES (', @@max_sort_length, ')');
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #
--echo #
--echo # End of 10.2 tests
--echo #
......@@ -2720,13 +2720,8 @@ struct LEX: public Query_tables_list
TABLE_LIST *create_last_non_select_table;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
/*
Prepared statement query text or name of variable that holds the
prepared statement (in PREPARE ... queries)
*/
LEX_STRING prepared_stmt_code;
/* If true, prepared_stmt_code is a name of variable that holds the query */
bool prepared_stmt_code_is_varref;
/* PREPARE or EXECUTE IMMEDIATE source expression */
Item *prepared_stmt_code;
/* Names of user variables holding parameters (in EXECUTE) */
List<Item> prepared_stmt_params;
sp_head *sphead;
......@@ -3007,6 +3002,7 @@ struct LEX: public Query_tables_list
void set_last_field_type(const Lex_field_type_st &type);
bool set_bincmp(CHARSET_INFO *cs, bool bin);
bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer);
bool prepared_stmt_params_fix_fields(THD *thd)
{
// Fix Items in the EXECUTE..USING list
......
......@@ -704,6 +704,7 @@ void init_update_queries(void)
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_EXECUTE_IMMEDIATE]= CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_COMPOUND]= CF_CAN_GENERATE_ROW_EVENTS;
/*
......
......@@ -2601,95 +2601,99 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
}
/**
Get an SQL statement text from a user variable or from plain text.
Get an SQL statement from an item in lex->prepared_stmt_code.
If the statement is plain text, just assign the
pointers, otherwise allocate memory in thd->mem_root and copy
the contents of the variable, possibly with character
set conversion.
This function can return pointers to very different memory classes:
- a static string "NULL", if the item returned NULL
- the result of prepare_stmt_code->val_str(), if no conversion was needed
- a thd->mem_root allocated string with the result of
prepare_stmt_code->val_str() converted to @@collation_connection,
if conversion was needed
@param[in] lex main lex
@param[out] query_len length of the SQL statement (is set only
in case of success)
The caller must dispose the result before the life cycle of "buffer" ends.
As soon as buffer's destructor is called, the value is not valid any more!
@retval
non-zero success
@retval
0 in case of error (out of memory)
mysql_sql_stmt_prepare() and mysql_sql_stmt_execute_immediate()
call get_dynamic_sql_string() and then call respectively
Prepare_statement::prepare() and Prepare_statment::execute_immediate(),
who store the returned result into its permanent location using
alloc_query(). "buffer" is still not destructed at that time.
@param[out] dst the result is stored here
@param[inout] buffer
@retval false on success
@retval true on error (out of memory)
*/
static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer)
{
THD *thd= lex->thd;
char *query_str= 0;
if (prepared_stmt_code->fix_fields(thd, NULL) ||
prepared_stmt_code->check_cols(1))
return true;
if (lex->prepared_stmt_code_is_varref)
const String *str= prepared_stmt_code->val_str(buffer);
if (prepared_stmt_code->null_value)
{
/* This is PREPARE stmt FROM or EXECUTE IMMEDIATE @var. */
String str;
CHARSET_INFO *to_cs= thd->variables.collation_connection;
bool needs_conversion;
user_var_entry *entry;
String *var_value= &str;
uint32 unused, len;
/*
Convert @var contents to string in connection character set. Although
it is known that int/real/NULL value cannot be a valid query we still
convert it for error messages to be uniform.
*/
if ((entry=
(user_var_entry*)my_hash_search(&thd->user_vars,
(uchar*)lex->prepared_stmt_code.str,
lex->prepared_stmt_code.length))
&& entry->value)
{
bool is_var_null;
var_value= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
/*
NULL value of variable checked early as entry->value so here
we can't get NULL in normal conditions
*/
DBUG_ASSERT(!is_var_null);
if (!var_value)
goto end;
}
else
{
/*
variable absent or equal to NULL, so we need to set variable to
Prepare source was NULL, so we need to set "str" to
something reasonable to get a readable error message during parsing
*/
str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
dst->str= "NULL";
dst->length= 4;
return false;
}
needs_conversion= String::needs_conversion(var_value->length(),
var_value->charset(), to_cs,
&unused);
len= (needs_conversion ? var_value->length() * to_cs->mbmaxlen :
var_value->length());
if (!(query_str= (char*) alloc_root(thd->mem_root, len+1)))
goto end;
/*
Character set conversion notes:
1) When PREPARE or EXECUTE IMMEDIATE are used with string literals:
PREPARE stmt FROM 'SELECT ''str''';
EXECUTE IMMEDIATE 'SELECT ''str''';
it's very unlikely that any conversion will happen below, because
@@character_set_client and @@collation_connection are normally
set to the same CHARSET_INFO pointer.
In tricky environments when @@collation_connection is set to something
different from @@character_set_client, double conversion may happen:
- When the parser scans the string literal
(sql_yacc.yy rules "prepare_src" -> "expr" -> ... -> "text_literal")
it will convert 'str' from @@character_set_client to
@@collation_connection.
- Then in the code below will convert 'str' from @@collation_connection
back to @@character_set_client.
2) When PREPARE or EXECUTE IMMEDIATE is used with a user variable,
it should work about the same way, because user variables are usually
assigned like this:
SET @str='str';
and thus have the same character set with string literals.
3) When PREPARE or EXECUTE IMMEDIATE is used with some
more complex expression, conversion will depend on this expression.
For example, a concatenation of string literals:
EXECUTE IMMEDIATE 'SELECT * FROM'||'t1';
should work the same way with just a single literal,
so no conversion normally.
*/
CHARSET_INFO *to_cs= thd->variables.character_set_client;
if (needs_conversion)
uint32 unused;
if (String::needs_conversion(str->length(), str->charset(), to_cs, &unused))
{
uint dummy_errors;
len= copy_and_convert(query_str, len, to_cs, var_value->ptr(),
var_value->length(), var_value->charset(),
&dummy_errors);
}
else
memcpy(query_str, var_value->ptr(), var_value->length());
query_str[len]= '\0'; // Safety (mostly for debug)
*query_len= len;
}
else
if (!(dst->str= sql_strmake_with_convert(thd, str->ptr(), str->length(),
str->charset(), UINT_MAX32,
to_cs, &dst->length)))
{
query_str= lex->prepared_stmt_code.str;
*query_len= lex->prepared_stmt_code.length;
dst->length= 0;
return true;
}
end:
return query_str;
DBUG_ASSERT(dst->length <= UINT_MAX32);
return false;
}
dst->str= str->ptr();
dst->length= str->length();
return false;
}
......@@ -2712,8 +2716,7 @@ void mysql_sql_stmt_prepare(THD *thd)
LEX *lex= thd->lex;
LEX_STRING *name= &lex->prepared_stmt_name;
Prepared_statement *stmt;
const char *query;
uint query_len= 0;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_prepare");
if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
......@@ -2731,7 +2734,12 @@ void mysql_sql_stmt_prepare(THD *thd)
stmt->deallocate();
}
if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
/*
It's important for "buffer" not to be destructed before stmt->prepare()!
See comments in get_dynamic_sql_string().
*/
StringBuffer<256> buffer;
if (lex->get_dynamic_sql_string(&query, &buffer) ||
! (stmt= new Prepared_statement(thd)))
{
DBUG_VOID_RETURN; /* out of memory */
......@@ -2752,7 +2760,7 @@ void mysql_sql_stmt_prepare(THD *thd)
DBUG_VOID_RETURN;
}
if (stmt->prepare(query, query_len))
if (stmt->prepare(query.str, (uint) query.length))
{
/* Statement map deletes the statement on erase */
thd->stmt_map.erase(stmt);
......@@ -2771,8 +2779,7 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
{
LEX *lex= thd->lex;
Prepared_statement *stmt;
const char *query;
uint query_len= 0;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_execute_immediate");
if (lex->prepared_stmt_params_fix_fields(thd))
......@@ -2781,15 +2788,20 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
/*
Prepared_statement is quite large,
let's allocate it on the heap rather than on the stack.
It's important for "buffer" not to be destructed
before stmt->execute_immediate().
See comments in get_dynamic_sql_string().
*/
if (!(query= get_dynamic_sql_string(lex, &query_len)) ||
StringBuffer<256> buffer;
if (lex->get_dynamic_sql_string(&query, &buffer) ||
!(stmt= new Prepared_statement(thd)))
DBUG_VOID_RETURN; // out of memory
// See comments on thd->free_list in mysql_sql_stmt_execute()
Item *free_list_backup= thd->free_list;
thd->free_list= NULL;
(void) stmt->execute_immediate(query, query_len);
(void) stmt->execute_immediate(query.str, (uint) query.length);
thd->free_items();
thd->free_list= free_list_backup;
......
......@@ -2223,23 +2223,20 @@ prepare:
PREPARE_SYM ident FROM prepare_src
{
LEX *lex= thd->lex;
if (lex->table_or_sp_used())
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
"PREPARE..FROM"));
lex->sql_command= SQLCOM_PREPARE;
lex->prepared_stmt_name= $2;
}
;
prepare_src:
TEXT_STRING_sys
{
LEX *lex= thd->lex;
lex->prepared_stmt_code= $1;
lex->prepared_stmt_code_is_varref= FALSE;
}
| '@' ident_or_text
{ Lex->expr_allows_subselect= false; }
expr
{
LEX *lex= thd->lex;
lex->prepared_stmt_code= $2;
lex->prepared_stmt_code_is_varref= TRUE;
Lex->prepared_stmt_code= $2;
Lex->expr_allows_subselect= true;
}
;
......@@ -2254,6 +2251,9 @@ execute:
{}
| EXECUTE_SYM IMMEDIATE_SYM prepare_src
{
if (Lex->table_or_sp_used())
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0),
"EXECUTE IMMEDIATE"));
Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE;
}
execute_using
......
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