diff options
-rw-r--r-- | mysql-test/r/ps_ddl.result | 537 | ||||
-rw-r--r-- | mysql-test/t/ps_ddl.test | 584 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/sp_head.cc | 21 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 23 | ||||
-rw-r--r-- | sql/sql_parse.cc | 42 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 88 | ||||
-rw-r--r-- | sql/table.h | 4 |
9 files changed, 1256 insertions, 48 deletions
diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index fe7062716ea..3121650508d 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -663,6 +663,8 @@ a b c # Currently a different result from conventional statements. # A view is inlined once at prepare, later on view DDL # does not affect prepared statement and it is not re-prepared. +# This is reported in Bug#36002 Prepared statements: if a view +# used in a statement is replaced, bad data execute stmt; a b c 10 20 30 @@ -1497,6 +1499,541 @@ drop function f_12093; drop procedure p_12093; deallocate prepare stmt_sf; deallocate prepare stmt_sp; +===================================================================== +Ensure that metadata validation is performed for every type of +SQL statement where it is needed. +===================================================================== +drop table if exists t1; +create table t1 (a int); +# +# SQLCOM_SELECT +# +prepare stmt from "select 1 as res from dual where (1) in (select * from t1)"; +drop table t1; +create table t1 (x int); +execute stmt; +res +drop table t1; +deallocate prepare stmt; +call p_verify_reprepare_count(1); +SUCCESS + +# +# SQLCOM_CREATE_TABLE +# +drop table if exists t1; +drop table if exists t2; +create table t1 (a int); +prepare stmt from 'create table t2 as select * from t1'; +execute stmt; +drop table t2; +execute stmt; +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +SUCCESS + +execute stmt; +ERROR 42S01: Table 't2' already exists +call p_verify_reprepare_count(1); +SUCCESS + +execute stmt; +ERROR 42S01: Table 't2' already exists +call p_verify_reprepare_count(0); +SUCCESS + +drop table t2; +create temporary table t2 (a int); +execute stmt; +ERROR 42S01: Table 't2' already exists +call p_verify_reprepare_count(1); +SUCCESS + +execute stmt; +ERROR 42S01: Table 't2' already exists +call p_verify_reprepare_count(0); +SUCCESS + +drop temporary table t2; +execute stmt; +call p_verify_reprepare_count(1); +SUCCESS + +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +SUCCESS + +drop table t2; +drop table t1; +create table t1 (x varchar(20)); +execute stmt; +call p_verify_reprepare_count(1); +SUCCESS + +select * from t2; +x +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +SUCCESS + +drop table t2; +drop table t1; +deallocate prepare stmt; +# XXX: no validation of the first table in case of +# CREATE TEMPORARY TABLE. This is a shortcoming of the current code, +# but since validation is not strictly necessary, nothing is done +# about it. +# Will be fixed as part of work on Bug#21431 "Incomplete support of +# temporary tables" +create table t1 (a int); +insert into t1 (a) values (1); +prepare stmt from "create temporary table if not exists t2 as select * from t1"; +execute stmt; +drop table t2; +execute stmt; +execute stmt; +Warnings: +Note 1050 Table 't2' already exists +select * from t2; +a +1 +1 +execute stmt; +Warnings: +Note 1050 Table 't2' already exists +select * from t2; +a +1 +1 +1 +drop table t2; +create temporary table t2 (a varchar(10)); +execute stmt; +Warnings: +Note 1050 Table 't2' already exists +select * from t2; +a +1 +call p_verify_reprepare_count(0); +SUCCESS + +drop table t1; +create table t1 (x int); +execute stmt; +Warnings: +Note 1050 Table 't2' already exists +call p_verify_reprepare_count(1); +SUCCESS + +execute stmt; +Warnings: +Note 1050 Table 't2' already exists +call p_verify_reprepare_count(0); +SUCCESS + +drop table t1; +drop table t2; +deallocate prepare stmt; +# +# SQLCOM_UPDATE +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "update t2 set a=a+1 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_INSERT +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "insert into t2 set a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_INSERT_SELECT +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "insert into t2 select * from t1"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_REPLACE +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "replace t2 set a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_REPLACE_SELECT +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "replace t2 select * from t1"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_DELETE +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +prepare stmt from "delete from t2 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; +# +# SQLCOM_DELETE_MULTI +# +drop table if exists t1, t2, t3; +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +prepare stmt from "delete t2, t3 from t2, t3 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2, t3; +deallocate prepare stmt; +# +# SQLCOM_UPDATE_MULTI +# +drop table if exists t1, t2, t3; +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +prepare stmt from "update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1)"; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2, t3; +deallocate prepare stmt; +# Intermediate results: 8 SQLCOMs tested, 8 automatic reprepares +call p_verify_reprepare_count(8); +SUCCESS + +# +# SQLCOM_LOAD +# +drop table if exists t1; +create table t1 (a varchar(20)); +prepare stmt from "load data infile '../std_data_ln/words.dat' into table t1"; +ERROR HY000: This command is not supported in the prepared statement protocol yet +drop table t1; +# +# SQLCOM_SHOW_DATABASES +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show databases where (1) in (select * from t1)"; +execute stmt; +Database +drop table t1; +create table t1 (x int); +execute stmt; +Database +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_TABLES +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show tables where (1) in (select * from t1)"; +execute stmt; +Tables_in_test +drop table t1; +create table t1 (x int); +execute stmt; +Tables_in_test +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_FIELDS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show fields from t1 where (1) in (select * from t1)"; +execute stmt; +Field Type Null Key Default Extra +drop table t1; +create table t1 (x int); +execute stmt; +Field Type Null Key Default Extra +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_KEYS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show keys from t1 where (1) in (select * from t1)"; +execute stmt; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +drop table t1; +create table t1 (x int); +execute stmt; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_VARIABLES +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show variables where (1) in (select * from t1)"; +execute stmt; +Variable_name Value +drop table t1; +create table t1 (x int); +execute stmt; +Variable_name Value +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_STATUS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show status where (1) in (select * from t1)"; +execute stmt; +Variable_name Value +drop table t1; +create table t1 (x int); +execute stmt; +Variable_name Value +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_LOGS, +# SQLCOM_SHOW_ENGINE_MUTEX, SQLCOM_SHOW_PROCESSLIST +# +# Currently can not have a where clause, need to be covered +# with tests +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show engine all status where (1) in (select * from t1)"; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where (1) in (select * from t1)' at line 1 +prepare stmt from "show engine all logs where (1) in (select * from t1)"; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where (1) in (select * from t1)' at line 1 +prepare stmt from "show engine all mutex where (1) in (select * from t1)"; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where (1) in (select * from t1)' at line 1 +prepare stmt from "show processlist where (1) in (select * from t1)"; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where (1) in (select * from t1)' at line 1 +drop table t1; +# +# SQLCOM_SHOW_CHARSETS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show charset where (1) in (select * from t1)"; +execute stmt; +Charset Description Default collation Maxlen +drop table t1; +create table t1 (x int); +execute stmt; +Charset Description Default collation Maxlen +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_COLLATIONS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show collation where (1) in (select * from t1)"; +execute stmt; +Collation Charset Id Default Compiled Sortlen +drop table t1; +create table t1 (x int); +execute stmt; +Collation Charset Id Default Compiled Sortlen +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_TABLE_STATUS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show table status where (1) in (select * from t1)"; +execute stmt; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +drop table t1; +create table t1 (x int); +execute stmt; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_TRIGGERS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show triggers where (1) in (select * from t1)"; +execute stmt; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +drop table t1; +create table t1 (x int); +execute stmt; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_OPEN_TABLES +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show open tables where (1) in (select * from t1)"; +execute stmt; +Database Table In_use Name_locked +drop table t1; +create table t1 (x int); +execute stmt; +Database Table In_use Name_locked +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_STATUS_PROC +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show procedure status where (1) in (select * from t1)"; +execute stmt; +Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation +drop table t1; +create table t1 (x int); +execute stmt; +Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_STATUS_FUNC +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show function status where (1) in (select * from t1)"; +execute stmt; +Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation +drop table t1; +create table t1 (x int); +execute stmt; +Db Name Type Definer Modified Created Security_type Comment character_set_client collation_connection Database Collation +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SHOW_EVENTS +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "show events where (1) in (select * from t1)"; +execute stmt; +Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation +drop table t1; +create table t1 (x int); +execute stmt; +Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_SET_OPTION +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "set @a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_DO +# +drop table if exists t1; +create table t1 (a int); +prepare stmt from "do ((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; +# +# SQLCOM_CALL +# +drop table if exists t1; +drop procedure if exists p1; +create procedure p1(a int) begin end; +create table t1 (a int); +prepare stmt from "call p1((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +drop procedure p1; +deallocate prepare stmt; +# +# SQLCOM_CREATE_VIEW +# +drop table if exists t1; +drop view if exists v1; +create table t1 (a int); +prepare stmt from "create view v1 as select * from t1"; +execute stmt; +drop view v1; +drop table t1; +create table t1 (x int); +execute stmt; +drop view v1; +drop table t1; +deallocate prepare stmt; +# Intermediate result: number of reprepares matches the number +# of tests +call p_verify_reprepare_count(18); +SUCCESS + +# +# SQLCOM_ALTER_VIEW +# +drop view if exists v1; +create view v1 as select 1; +prepare stmt from "alter view v1 as select 2"; +ERROR HY000: This command is not supported in the prepared statement protocol yet +drop view v1; # Cleanup # drop temporary table if exists t1, t2, t3; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index d3df035444c..94791cd81f8 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -614,6 +614,8 @@ select * from t1; --echo # Currently a different result from conventional statements. --echo # A view is inlined once at prepare, later on view DDL --echo # does not affect prepared statement and it is not re-prepared. +--echo # This is reported in Bug#36002 Prepared statements: if a view +--echo # used in a statement is replaced, bad data execute stmt; call p_verify_reprepare_count(0); flush table t2; @@ -1347,6 +1349,588 @@ drop procedure p_12093; deallocate prepare stmt_sf; deallocate prepare stmt_sp; + +--echo ===================================================================== +--echo Ensure that metadata validation is performed for every type of +--echo SQL statement where it is needed. +--echo ===================================================================== +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +--echo # +--echo # SQLCOM_SELECT +--echo # +prepare stmt from "select 1 as res from dual where (1) in (select * from t1)"; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; +call p_verify_reprepare_count(1); +--echo # +--echo # SQLCOM_CREATE_TABLE +--echo # +--disable_warnings +drop table if exists t1; +drop table if exists t2; +--enable_warnings + +create table t1 (a int); + +prepare stmt from 'create table t2 as select * from t1'; +execute stmt; +drop table t2; +execute stmt; +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +--error ER_TABLE_EXISTS_ERROR +execute stmt; +call p_verify_reprepare_count(1); +--error ER_TABLE_EXISTS_ERROR +execute stmt; +call p_verify_reprepare_count(0); +drop table t2; +create temporary table t2 (a int); +--error ER_TABLE_EXISTS_ERROR +execute stmt; +call p_verify_reprepare_count(1); +--error ER_TABLE_EXISTS_ERROR +execute stmt; +call p_verify_reprepare_count(0); +drop temporary table t2; +execute stmt; +call p_verify_reprepare_count(1); +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +drop table t2; +drop table t1; +create table t1 (x varchar(20)); +execute stmt; +call p_verify_reprepare_count(1); +select * from t2; +drop table t2; +execute stmt; +call p_verify_reprepare_count(0); +drop table t2; +drop table t1; +deallocate prepare stmt; +--echo # XXX: no validation of the first table in case of +--echo # CREATE TEMPORARY TABLE. This is a shortcoming of the current code, +--echo # but since validation is not strictly necessary, nothing is done +--echo # about it. +--echo # Will be fixed as part of work on Bug#21431 "Incomplete support of +--echo # temporary tables" +create table t1 (a int); +insert into t1 (a) values (1); +prepare stmt from "create temporary table if not exists t2 as select * from t1"; +execute stmt; +drop table t2; +execute stmt; +execute stmt; +select * from t2; +execute stmt; +select * from t2; +drop table t2; +create temporary table t2 (a varchar(10)); +execute stmt; +select * from t2; +call p_verify_reprepare_count(0); +drop table t1; +create table t1 (x int); +execute stmt; +call p_verify_reprepare_count(1); +execute stmt; +call p_verify_reprepare_count(0); +drop table t1; +drop table t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_UPDATE +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "update t2 set a=a+1 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_INSERT +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "insert into t2 set a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; + +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_INSERT_SELECT +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "insert into t2 select * from t1"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_REPLACE +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "replace t2 set a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_REPLACE_SELECT +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "replace t2 select * from t1"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_DELETE +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +prepare stmt from "delete from t2 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_DELETE_MULTI +--echo # + +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +prepare stmt from "delete t2, t3 from t2, t3 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2, t3; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_UPDATE_MULTI +--echo # + +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +prepare stmt from "update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1)"; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1, t2, t3; +deallocate prepare stmt; +--echo # Intermediate results: 8 SQLCOMs tested, 8 automatic reprepares +call p_verify_reprepare_count(8); + +--echo # +--echo # SQLCOM_LOAD +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a varchar(20)); +--error ER_UNSUPPORTED_PS +prepare stmt from "load data infile '../std_data_ln/words.dat' into table t1"; +drop table t1; + +--echo # +--echo # SQLCOM_SHOW_DATABASES +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show databases where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_TABLES +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show tables where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_FIELDS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show fields from t1 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_KEYS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show keys from t1 where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_VARIABLES +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show variables where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_STATUS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show status where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_LOGS, +--echo # SQLCOM_SHOW_ENGINE_MUTEX, SQLCOM_SHOW_PROCESSLIST +--echo # + +--echo # Currently can not have a where clause, need to be covered +--echo # with tests + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +--error ER_PARSE_ERROR +prepare stmt from "show engine all status where (1) in (select * from t1)"; +--error ER_PARSE_ERROR +prepare stmt from "show engine all logs where (1) in (select * from t1)"; +--error ER_PARSE_ERROR +prepare stmt from "show engine all mutex where (1) in (select * from t1)"; +--error ER_PARSE_ERROR +prepare stmt from "show processlist where (1) in (select * from t1)"; +drop table t1; + +--echo # +--echo # SQLCOM_SHOW_CHARSETS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show charset where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_COLLATIONS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show collation where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_TABLE_STATUS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show table status where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_TRIGGERS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show triggers where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_OPEN_TABLES +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show open tables where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_STATUS_PROC +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show procedure status where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_STATUS_FUNC +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show function status where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SHOW_EVENTS +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "show events where (1) in (select * from t1)"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_SET_OPTION +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "set @a=((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_DO +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +prepare stmt from "do ((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_CALL +--echo # + +--disable_warnings +drop table if exists t1; +drop procedure if exists p1; +--enable_warnings +create procedure p1(a int) begin end; +create table t1 (a int); +prepare stmt from "call p1((1) in (select * from t1))"; +execute stmt; +drop table t1; +create table t1 (x int); +execute stmt; +drop table t1; +drop procedure p1; +deallocate prepare stmt; + +--echo # +--echo # SQLCOM_CREATE_VIEW +--echo # + +--disable_warnings +drop table if exists t1; +drop view if exists v1; +--enable_warnings +create table t1 (a int); +prepare stmt from "create view v1 as select * from t1"; +execute stmt; +drop view v1; +drop table t1; +create table t1 (x int); +execute stmt; +drop view v1; +drop table t1; +deallocate prepare stmt; +--echo # Intermediate result: number of reprepares matches the number +--echo # of tests +call p_verify_reprepare_count(18); + +--echo # +--echo # SQLCOM_ALTER_VIEW +--echo # + +--disable_warnings +drop view if exists v1; +--enable_warnings +create view v1 as select 1; +--error ER_UNSUPPORTED_PS +prepare stmt from "alter view v1 as select 2"; +drop view v1; + --echo # Cleanup --echo # --disable_warnings diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 491a4c8ca1b..dd26a2d8103 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -695,7 +695,7 @@ const char *set_thd_proc_info(THD *thd, const char *info, prepare matches the type of the object we obtained from the table definition cache. - @sa check_and_update_metadata_version() + @sa check_and_update_table_version() @sa Execute_observer @sa Prepared_statement::reprepare() */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8bd10e00f15..ab95575a965 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1068,6 +1068,7 @@ sp_head::execute(THD *thd) LEX *old_lex; Item_change_list old_change_list; String old_packet; + Metadata_version_observer *save_metadata_observer= thd->m_metadata_observer; Object_creation_ctx *saved_creation_ctx; @@ -1135,6 +1136,25 @@ sp_head::execute(THD *thd) thd->variables.sql_mode= m_sql_mode; save_abort_on_warning= thd->abort_on_warning; thd->abort_on_warning= 0; + /** + When inside a substatement (a stored function or trigger + statement), clear the metadata observer in THD, if any. + Remember the value of the observer here, to be able + to restore it when leaving the substatement. + + We reset the observer to suppress errors when a substatement + uses temporary tables. If a temporary table does not exist + at start of the main statement, it's not prelocked + and thus is not validated with other prelocked tables. + + Later on, when the temporary table is opened, metadata + versions mismatch, expectedly. + + The proper solution for the problem is to re-validate tables + of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000), + but it's not implemented yet. + */ + thd->m_metadata_observer= 0; /* It is also more efficient to save/restore current thd->lex once when @@ -1297,6 +1317,7 @@ sp_head::execute(THD *thd) thd->derived_tables= old_derived_tables; thd->variables.sql_mode= save_sql_mode; thd->abort_on_warning= save_abort_on_warning; + thd->m_metadata_observer= save_metadata_observer; thd->stmt_arena= old_arena; state= EXECUTED; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6a4ef0781a2..85f8e607213 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2876,7 +2876,6 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, first_successful_insert_id_in_prev_stmt; backup->first_successful_insert_id_in_cur_stmt= first_successful_insert_id_in_cur_stmt; - backup->m_metadata_observer= m_metadata_observer; if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !current_stmt_binlog_row_based) @@ -2896,7 +2895,6 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, cuted_fields= 0; transaction.savepoints= 0; first_successful_insert_id_in_cur_stmt= 0; - m_metadata_observer= 0; } @@ -2945,7 +2943,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) */ examined_row_count+= backup->examined_row_count; cuted_fields+= backup->cuted_fields; - m_metadata_observer= backup->m_metadata_observer; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 837b74b3b60..cdb2300cabc 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -45,7 +45,7 @@ At most 1 instance of this class is active at a time, in which case THD::m_metadata_observer is not NULL. - @sa check_and_update_metadata_version() for details of the + @sa check_and_update_table_version() for details of the version tracking algorithm @sa Execute_observer for details of how we detect that @@ -847,7 +847,7 @@ public: to avoid spurious ER_NEED_REPREPARE errors -- system and INFORMATION_SCHEMA tables are not subject to metadata version tracking. - @sa check_and_update_metadata_version() + @sa check_and_update_table_version() */ Metadata_version_observer *m_metadata_observer; @@ -983,25 +983,6 @@ public: bool enable_slow_log; bool last_insert_id_used; SAVEPOINT *savepoints; - /** - When inside a substatement (a stored function or trigger - statement), clear the metadata observer in THD, if any. - Remember the value of the observer here, to be able - to restore it when leaving the substatement. - - We reset the observer to suppress errors when a substatement - uses temporary tables. If a temporary table does not exist - at start of the main statement, it's not prelocked - and thus is not validated with other prelocked tables. - - Later on, when the temporary table is opened, metadata - versions mismatch, expectedly. - - The proper solution for the problem is to re-validate tables - of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000), - but it's not implemented yet. - */ - Metadata_version_observer *m_metadata_observer; }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index faf48b38fd0..b495b2a268b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -200,19 +200,19 @@ void init_update_queries(void) { bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags)); - sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_BACKUP_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_RESTORE_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA; @@ -235,19 +235,21 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE; - - sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE; + + sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_PLUGINS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; @@ -271,7 +273,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; @@ -279,9 +281,11 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | - CF_SHOW_TABLE_COMMAND); + CF_SHOW_TABLE_COMMAND | + CF_REEXECUTION_FRAGILE); sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | - CF_SHOW_TABLE_COMMAND); + CF_SHOW_TABLE_COMMAND | + CF_REEXECUTION_FRAGILE); /* The following is used to preserver CF_ROW_COUNT during the @@ -289,7 +293,7 @@ void init_update_queries(void) last called (or executed) statement is preserved. See mysql_execute_command() for how CF_ROW_COUNT is used. */ - sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE; sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; /* diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 720deac116d..b815b993591 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1481,6 +1481,43 @@ error: /** + Validate and prepare for execution CALL statement expressions. + + @param stmt prepared statement + @param tables list of tables used in this query + @param value_list list of expressions + + @retval FALSE success + @retval TRUE error, error message is set in THD +*/ + +static bool mysql_test_call_fields(Prepared_statement *stmt, + TABLE_LIST *tables, + List<Item> *value_list) +{ + DBUG_ENTER("mysql_test_call_fields"); + + List_iterator<Item> it(*value_list); + THD *thd= stmt->thd; + Item *item; + + if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) || + open_normal_and_derived_tables(thd, tables, 0)) + goto err; + + while ((item= it++)) + { + if (!item->fixed && item->fix_fields(thd, it.ref()) || + item->check_cols(1)) + goto err; + } + DBUG_RETURN(FALSE); +err: + DBUG_RETURN(TRUE); +} + + +/** Check internal SELECT of the prepared command. @param stmt prepared statement @@ -1601,6 +1638,17 @@ static bool mysql_test_create_table(Prepared_statement *stmt) res= select_like_stmt_test(stmt, 0, 0); } + else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + { + /* + Check that the source table exist, and also record + its metadata version. Even though not strictly necessary, + we validate metadata of all CREATE TABLE statements, + which keeps metadata validation code simple. + */ + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) + DBUG_RETURN(TRUE); + } /* put tables back for PS rexecuting */ lex->link_first_table_back(create_table, link_to_local); @@ -1838,7 +1886,21 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_DELETE: res= mysql_test_delete(stmt, tables); break; - + /* The following allow WHERE clause, so they must be tested like SELECT */ + case SQLCOM_SHOW_DATABASES: + case SQLCOM_SHOW_TABLES: + case SQLCOM_SHOW_TRIGGERS: + case SQLCOM_SHOW_EVENTS: + case SQLCOM_SHOW_OPEN_TABLES: + case SQLCOM_SHOW_FIELDS: + case SQLCOM_SHOW_KEYS: + case SQLCOM_SHOW_COLLATIONS: + case SQLCOM_SHOW_CHARSETS: + case SQLCOM_SHOW_VARIABLES: + case SQLCOM_SHOW_STATUS: + case SQLCOM_SHOW_TABLE_STATUS: + case SQLCOM_SHOW_STATUS_PROC: + case SQLCOM_SHOW_STATUS_FUNC: case SQLCOM_SELECT: res= mysql_test_select(stmt, tables); if (res == 2) @@ -1863,6 +1925,9 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_do_fields(stmt, tables, lex->insert_list); break; + case SQLCOM_CALL: + res= mysql_test_call_fields(stmt, tables, &lex->value_list); + break; case SQLCOM_SET_OPTION: res= mysql_test_set_fields(stmt, tables, &lex->var_list); break; @@ -1912,7 +1977,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_DROP_INDEX: case SQLCOM_ROLLBACK: case SQLCOM_TRUNCATE: - case SQLCOM_CALL: case SQLCOM_DROP_VIEW: case SQLCOM_REPAIR: case SQLCOM_ANALYZE: @@ -3064,6 +3128,26 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) } +/** + Assign parameter values either from variables, in case of SQL PS + or from the execute packet. + + @param expanded_query a container with the original SQL statement. + '?' placeholders will be replaced with + their values in case of success. + The result is used for logging and replication + @param packet pointer to execute packet. + NULL in case of SQL PS + @param packet_end end of the packet. NULL in case of SQL PS + + @todo Use a paremeter source class family instead of 'if's, and + support stored procedure variables. + + @retval TRUE an error occurred when assigning a parameter (likely + a conversion error or out of memory, or malformed packet) + @retval FALSE success +*/ + bool Prepared_statement::set_parameters(String *expanded_query, uchar *packet, uchar *packet_end) diff --git a/sql/table.h b/sql/table.h index 2b3c9b8d5b1..603557fee78 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1336,7 +1336,7 @@ struct TABLE_LIST (if any) with values obtained from the current table definition cache element. - @sa check_and_update_metadata_version() + @sa check_and_update_table_version() */ inline bool is_metadata_version_equal(TABLE_SHARE *s) const @@ -1349,7 +1349,7 @@ struct TABLE_LIST Record the value of metadata version of the corresponding table definition cache element in this parse tree node. - @sa check_and_update_metadata_version() + @sa check_and_update_table_version() */ inline void set_metadata_version(TABLE_SHARE *s) |