Commit bd2a7328 authored by unknown's avatar unknown

WL#4165 "Prepared statements: validation".

Add metadata validation to ~20 more SQL commands. Make sure that
these commands actually work in ps-protocol, since until now they
were enabled, but not carefully tested.
Fixes the ml003 bug found by Matthias during internal testing of the
patch.


mysql-test/r/ps_ddl.result:
  Update test results (WL#4165)
mysql-test/t/ps_ddl.test:
  Cover with tests metadata validation of 26 SQL statements.
sql/mysql_priv.h:
  Fix the name in the comment.
sql/sp_head.cc:
  Changed the way the observer is removed in case of stored procedures
  to support validation prepare stmt from "call p1(<expr>)": whereas
  tables used in the expression must be validated, substatements
  of p1 must not.
  The previous scheme used to silence the observer only in stored
  functions and triggers.
sql/sql_class.cc:
  Now the observer is silenced in sp_head::execute(). Remove it from
  Sub_statement_state.
sql/sql_class.h:
  Now the observer is silenced in sp_head::execute(). Remove it from
  Sub_statement_state.
sql/sql_parse.cc:
  Add CF_REEXECUTION_FRAGILE to 20 more SQLCOMs that need it.
sql/sql_prepare.cc:
  Add metadata validation to ~20 new SQLCOMs that need it.
  Fix memory leaks with expressions used in SHOW DATABASES and CALL
  (and prepared statements).
  We need to fix all expressions at prepare, since if these expressions
  use subqueries, there are one-time transformations of the parse
  tree that must be done at prepare. 
  List of fixed commands includes: SHOW TABLES, SHOW DATABASES,
  SHOW TRIGGERS, SHOW EVENTS, SHOW OPEN TABLES,SHOW KEYS, SHOW FIELDS, 
  SHOW COLLATIONS, SHOW CHARSETS, SHOW VARIABLES, SHOW TATUS, SHOW TABLE
  STATUS, SHOW PROCEDURE STATUS, SHOW FUNCTION STATUS, CALL.
  Add comment to set_parameters().
sql/table.h:
  Update comments.
parent 1ff9a243
......@@ -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;
......
......@@ -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
......
......@@ -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()
*/
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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 @@ class Open_tables_state
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 @@ class Sub_statement_state
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;
};
......
......@@ -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;
/*
......
......@@ -1480,6 +1480,43 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
}
/**
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.
......@@ -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)
......
......@@ -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)
......
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