summaryrefslogtreecommitdiffstats
path: root/mysql-test/suite/compat/oracle/t
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
commit3f619478f796eddbba6e39502fe941b285dd97b1 (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /mysql-test/suite/compat/oracle/t
parentInitial commit. (diff)
downloadmariadb-upstream.tar.xz
mariadb-upstream.zip
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mysql-test/suite/compat/oracle/t')
-rw-r--r--mysql-test/suite/compat/oracle/t/anonymous_derived.test56
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog-master.opt1
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog.test117
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_stm_ps.test57
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_stm_sp.test219
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_stm_sp_package.test158
-rw-r--r--mysql-test/suite/compat/oracle/t/column_compression.test85
-rw-r--r--mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test170
-rw-r--r--mysql-test/suite/compat/oracle/t/empty_string_literal.test8
-rw-r--r--mysql-test/suite/compat/oracle/t/events.test37
-rw-r--r--mysql-test/suite/compat/oracle/t/exception.test457
-rw-r--r--mysql-test/suite/compat/oracle/t/func_add_months.test38
-rw-r--r--mysql-test/suite/compat/oracle/t/func_case.test9
-rw-r--r--mysql-test/suite/compat/oracle/t/func_concat.test184
-rw-r--r--mysql-test/suite/compat/oracle/t/func_decode.test98
-rw-r--r--mysql-test/suite/compat/oracle/t/func_length.test18
-rw-r--r--mysql-test/suite/compat/oracle/t/func_misc.test346
-rw-r--r--mysql-test/suite/compat/oracle/t/func_pad.test31
-rw-r--r--mysql-test/suite/compat/oracle/t/func_replace.test22
-rw-r--r--mysql-test/suite/compat/oracle/t/func_substr.test47
-rw-r--r--mysql-test/suite/compat/oracle/t/func_time.test25
-rw-r--r--mysql-test/suite/compat/oracle/t/func_to_char.test236
-rw-r--r--mysql-test/suite/compat/oracle/t/func_trim.test77
-rw-r--r--mysql-test/suite/compat/oracle/t/gis-debug.test31
-rw-r--r--mysql-test/suite/compat/oracle/t/gis.test76
-rw-r--r--mysql-test/suite/compat/oracle/t/information_schema_parameters.test127
-rw-r--r--mysql-test/suite/compat/oracle/t/keywords.test29
-rw-r--r--mysql-test/suite/compat/oracle/t/minus.test44
-rw-r--r--mysql-test/suite/compat/oracle/t/misc.test38
-rw-r--r--mysql-test/suite/compat/oracle/t/mysqldump_restore.test30
-rw-r--r--mysql-test/suite/compat/oracle/t/parser.test727
-rw-r--r--mysql-test/suite/compat/oracle/t/plugin.test3
-rw-r--r--mysql-test/suite/compat/oracle/t/ps.test290
-rw-r--r--mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test38
-rw-r--r--mysql-test/suite/compat/oracle/t/rpl_sp_package.test134
-rw-r--r--mysql-test/suite/compat/oracle/t/rpl_sp_package_variables.test36
-rw-r--r--mysql-test/suite/compat/oracle/t/sequence.test48
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-anchor-row-type-table.test124
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-anonymous.test244
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-cache-invalidate.inc11
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-code.test1088
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-cursor-decl.test295
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test1597
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-cursor.test1044
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-expr.test165
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-goto-debug.test178
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-goto.test968
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-inout.test2501
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-memory-leak.test35
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-code.test182
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-db.test6
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-package.test10
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-trigger.test6
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-view.test6
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml.inc107
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-i_s.test69
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-innodb.test66
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-mdl.test110
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-mysqldump.test94
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-security.test332
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package.test3092
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-param.inc9
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-param.test363
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-row-vs-var.inc6
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-row.test2418
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-security.test345
-rw-r--r--mysql-test/suite/compat/oracle/t/sp.test2430
-rw-r--r--mysql-test/suite/compat/oracle/t/statement-expr.test86
-rw-r--r--mysql-test/suite/compat/oracle/t/table_value_constr.test1287
-rw-r--r--mysql-test/suite/compat/oracle/t/trigger.test106
-rw-r--r--mysql-test/suite/compat/oracle/t/truncate.test16
-rw-r--r--mysql-test/suite/compat/oracle/t/type_blob.test17
-rw-r--r--mysql-test/suite/compat/oracle/t/type_clob.test10
-rw-r--r--mysql-test/suite/compat/oracle/t/type_date.test101
-rw-r--r--mysql-test/suite/compat/oracle/t/type_number.test9
-rw-r--r--mysql-test/suite/compat/oracle/t/type_raw.test10
-rw-r--r--mysql-test/suite/compat/oracle/t/type_varchar.test9
-rw-r--r--mysql-test/suite/compat/oracle/t/type_varchar2.test19
-rw-r--r--mysql-test/suite/compat/oracle/t/update_innodb.test31
-rw-r--r--mysql-test/suite/compat/oracle/t/variables.test38
-rw-r--r--mysql-test/suite/compat/oracle/t/vcol.test16
-rw-r--r--mysql-test/suite/compat/oracle/t/versioning.test23
-rw-r--r--mysql-test/suite/compat/oracle/t/win.test22
83 files changed, 23848 insertions, 0 deletions
diff --git a/mysql-test/suite/compat/oracle/t/anonymous_derived.test b/mysql-test/suite/compat/oracle/t/anonymous_derived.test
new file mode 100644
index 00000000..7a9ee216
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/anonymous_derived.test
@@ -0,0 +1,56 @@
+--echo #
+--echo # MDEV-19162: anonymous derived tables part
+--echo #
+
+set @save_sql_mode=@@sql_mode;
+set session sql_mode=ORACLE;
+
+--disable_ps_protocol
+--enable_metadata
+SELECT * FROM (SELECT 1 FROM DUAL), (SELECT 2 FROM DUAL);
+--disable_metadata
+--enable_ps_protocol
+create table t1 (a int);
+insert into t1 values (2),(3);
+create table t2 (a int);
+insert into t2 values (2),(3);
+--disable_ps_protocol
+--enable_metadata
+select t1.a from t1, (select * from t2 where t2.a<= 2);
+select t1.a, b from t1, (select a as b from t2 where t2.a<= 2);
+select t1.a, b from t1, (select max(a) as b from t2);
+--disable_metadata
+--enable_ps_protocol
+explain extended
+select t1.a, b from t1, (select max(a) as b from t2);
+--error ER_BAD_FIELD_ERROR
+select * from (select tt.* from (select * from t1) as tt) where tt.a > 0;
+select * from (select tt.* from (select * from t1) as tt) where a > 0;
+
+create view v1 as select t1.a, b from t1, (select max(a) as b from t2);
+
+select * from v1;
+
+
+DELIMITER /;
+create procedure p1
+as
+begin
+ select t1.a, b from t1, (select max(a) as b from t2);
+end/
+DELIMITER ;/
+
+call p1;
+
+SET sql_mode=default;
+
+select * from v1;
+show create view v1;
+
+call p1;
+show create procedure p1;
+
+drop view v1;
+drop procedure p1;
+drop table t1,t2;
+set session sql_mode=@save_sql_mode;
diff --git a/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog-master.opt b/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog-master.opt
new file mode 100644
index 00000000..8f0cc182
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog-master.opt
@@ -0,0 +1 @@
+--flashback
diff --git a/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog.test b/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog.test
new file mode 100644
index 00000000..bda32af5
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/binlog_ptr_mysqlbinlog.test
@@ -0,0 +1,117 @@
+# ==== Purpose ====
+#
+# Test verifies that point in time recovery of binary log works when
+# sql_mode='ORACLE'.
+#
+# BEGIN statement is printed in three places
+# 1) "Gtid_log_event::print"
+# 2) "Xid_log_event::print" if flashback is enabled
+# 3) "Query_log_event::print" if flashback is enabled and engine is
+# non-transacional.
+#
+# Test verifies all these cases.
+#
+# ==== References ====
+#
+# MDEV-23108: Point in time recovery of binary log fails when sql_mode=ORACLE
+#
+--source include/have_log_bin.inc
+--source include/have_innodb.inc
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+SET @@SQL_MODE = 'ORACLE';
+
+--echo ##########################################################################
+--echo # Test verifies Gtid_log_event/Xid_log_event specific print #
+--echo ##########################################################################
+CREATE TABLE tm (f INT) ENGINE=MYISAM;
+INSERT INTO tm VALUES (10);
+
+CREATE TABLE t(f INT) ENGINE=INNODB;
+INSERT INTO t VALUES (10);
+
+DELIMITER /;
+CREATE OR REPLACE PROCEDURE simpleproc (param1 OUT INT) AS
+ BEGIN
+ SELECT COUNT(*) INTO param1 FROM t;
+ END;
+/
+CREATE FUNCTION f1 RETURN INT
+AS
+BEGIN
+ RETURN 10;
+END;
+/
+DELIMITER ;/
+
+FLUSH LOGS;
+--echo ##########################################################################
+--echo # Delete data from master so that it can be restored from binlog #
+--echo ##########################################################################
+DROP FUNCTION f1;
+DROP PROCEDURE simpleproc;
+DROP TABLE tm;
+DROP TABLE t;
+
+--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/test.sql
+--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/test.sql
+
+--echo ##########################################################################
+--echo # Post recovery using mysqlbinlog #
+--echo ##########################################################################
+SHOW TABLES;
+SELECT * FROM tm;
+SELECT * FROM t;
+--horizontal_results
+SELECT f1();
+CALL simpleproc(@a);
+SELECT @a;
+
+--echo "***** Clean Up *****"
+DROP TABLE t,tm;
+DROP PROCEDURE simpleproc;
+DROP FUNCTION f1;
+--remove_file $MYSQLTEST_VARDIR/tmp/test.sql
+RESET MASTER;
+
+--echo ##########################################################################
+--echo # Test verifies Gtid_log_event/Xid_log_event/Qery_log_event #
+--echo # specific print along with flashback option #
+--echo ##########################################################################
+CREATE TABLE tm(f INT) ENGINE=MYISAM;
+INSERT INTO tm VALUES (10);
+INSERT INTO tm VALUES (20);
+CREATE TABLE t(f INT) ENGINE=INNODB;
+INSERT INTO t VALUES (10);
+INSERT INTO t VALUES (20);
+--echo ##########################################################################
+--echo # Initial data #
+--echo ##########################################################################
+SELECT * FROM tm;
+SELECT * FROM t;
+FLUSH LOGS;
+DELETE FROM tm WHERE f=20;
+DELETE FROM t WHERE f=20;
+FLUSH LOGS;
+
+--echo ##########################################################################
+--echo # Data after deletion #
+--echo ##########################################################################
+SELECT * FROM tm;
+SELECT * FROM t;
+--exec $MYSQL_BINLOG --flashback $MYSQLD_DATADIR/master-bin.000002 > $MYSQLTEST_VARDIR/tmp/test.sql
+
+--let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/test.sql
+--let SEARCH_PATTERN=START TRANSACTION
+--source include/search_pattern_in_file.inc
+--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/test.sql
+
+--echo ##########################################################################
+--echo # Data after recovery using flashback #
+--echo ##########################################################################
+SELECT * FROM tm;
+SELECT * FROM t;
+
+--echo "***** Clean Up *****"
+DROP TABLE t,tm;
+--remove_file $MYSQLTEST_VARDIR/tmp/test.sql
diff --git a/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test b/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test
new file mode 100644
index 00000000..f305f611
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/binlog_stm_ps.test
@@ -0,0 +1,57 @@
+--source include/not_embedded.inc
+--source include/have_binlog_format_statement.inc
+
+--disable_query_log
+reset master; # get rid of previous tests binlog
+--enable_query_log
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10801 sql_mode: dynamic SQL placeholders
+--echo #
+
+CREATE TABLE t1 (a INT, b INT);
+SET @a=10, @b=20;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (?,?)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:a,:b)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:aaa,:bbb)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:"a",:"b")';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:"aaa",:"bbb")';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:1,:2)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:222,:111)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:0,:65535)';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'INSERT INTO t1 VALUES (:65535,:0)';
+EXECUTE stmt USING @a, @b;
+SELECT * FROM t1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-16095 Oracle-style placeholder inside GROUP BY..WITH ROLLUP breaks replication
+--echo #
+
+FLUSH LOGS;
+CREATE TABLE t1 (d DATE);
+INSERT INTO t1 VALUES ('1985-05-13'),('1989-12-24');
+CREATE TABLE t2 (d DATE, c BIGINT);
+DELIMITER $$;
+BEGIN
+ EXECUTE IMMEDIATE 'INSERT INTO t2 SELECT d, COUNT(*) FROM t1 GROUP BY d, :param' USING 1;
+ EXECUTE IMMEDIATE 'INSERT INTO t2 SELECT d, COUNT(*) FROM t1 GROUP BY d, :param WITH ROLLUP' USING 1;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1,t2;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
diff --git a/mysql-test/suite/compat/oracle/t/binlog_stm_sp.test b/mysql-test/suite/compat/oracle/t/binlog_stm_sp.test
new file mode 100644
index 00000000..e6f33cb1
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/binlog_stm_sp.test
@@ -0,0 +1,219 @@
+--source include/not_embedded.inc
+--source include/have_binlog_format_statement.inc
+
+--disable_query_log
+call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+reset master; # get rid of previous tests binlog
+--enable_query_log
+
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10914 ROW data type for stored routine variables
+--echo #
+
+CREATE TABLE t1 (a INT, b INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ rec.a:=100;
+ rec.b:=200;
+ INSERT INTO t1 VALUES (rec.a,rec.b);
+ INSERT INTO t1 VALUES (10, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
+ INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
+ rec.a:=NULL;
+ INSERT INTO t1 VALUES (11, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (11, rec=ROW(100,201));
+ INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
+ INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
+ INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
+ rec.b:=NULL;
+ INSERT INTO t1 VALUES (12, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
+ INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+--echo #
+--echo # Testing ROW fields in LIMIT
+--echo #
+
+FLUSH LOGS;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(10);
+CREATE TABLE t2 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a INT:= 1;
+ rec ROW(a INT);
+BEGIN
+ rec.a:= 1;
+ INSERT INTO t2 SELECT 1 FROM t1 LIMIT a;
+ INSERT INTO t2 SELECT 2 FROM t1 LIMIT rec.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1,t2;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+--echo #
+--echo # End of MDEV-10914 ROW data type for stored routine variables
+--echo #
+
+
+--echo #
+--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
+--echo #
+
+CREATE TABLE t1 (a INT, b INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec t1%ROWTYPE;
+BEGIN
+ rec.a:=100;
+ rec.b:=200;
+ SELECT rec=ROW(100,200) AS true1, ROW(100,200)=rec AS true2;
+ INSERT INTO t1 VALUES (rec.a,rec.b);
+ INSERT INTO t1 VALUES (10, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
+ INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
+ rec.a:=NULL;
+ INSERT INTO t1 VALUES (11, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (11, rec=ROW(100,201));
+ INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
+ INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
+ INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
+ rec.b:=NULL;
+ INSERT INTO t1 VALUES (12, rec=ROW(100,200));
+ INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
+ INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
+ INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+--echo #
+--echo # MDEV-12291 Allow ROW variables as SELECT INTO targets
+--echo #
+
+FLUSH LOGS;
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10, 'b10');
+CREATE TABLE t2 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 ROW(a INT, b VARCHAR(32));
+BEGIN
+ SELECT * INTO rec1 FROM t1;
+ INSERT INTO t2 VALUES (rec1.a, rec1.b);
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+FLUSH LOGS;
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10, 'b10');
+CREATE TABLE t2 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ SELECT * INTO rec1 FROM t1;
+ INSERT INTO t2 VALUES (rec1.a, rec1.b);
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+FLUSH LOGS;
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10, 'b10');
+CREATE TABLE t2 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ SELECT * INTO rec1 FROM t1;
+ INSERT INTO t2 VALUES (rec1.a, rec1.b);
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+
+--echo #
+--echo # MDEV-16020 SP variables inside GROUP BY..WITH ROLLUP break replication
+--echo #
+
+FLUSH LOGS;
+CREATE TABLE t1 (d DATE);
+INSERT INTO t1 VALUES ('1985-05-13'),('1989-12-24');
+CREATE TABLE t2 (d DATE, c BIGINT);
+DELIMITER $$;
+DECLARE
+ var INT;
+BEGIN
+ INSERT INTO t2 SELECT d, COUNT(*) FROM t1 GROUP BY d, var;
+ INSERT INTO t2 SELECT d, COUNT(*) FROM t1 GROUP BY d, var WITH ROLLUP;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1,t2;
+
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
diff --git a/mysql-test/suite/compat/oracle/t/binlog_stm_sp_package.test b/mysql-test/suite/compat/oracle/t/binlog_stm_sp_package.test
new file mode 100644
index 00000000..577ff58d
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/binlog_stm_sp_package.test
@@ -0,0 +1,158 @@
+--source include/not_embedded.inc
+--source include/have_binlog_format_statement.inc
+
+--disable_query_log
+reset master; # get rid of previous tests binlog
+--enable_query_log
+
+SET sql_mode=ORACLE;
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE IF NOT EXISTS p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+DROP PACKAGE BODY p1;
+DROP PACKAGE p1;
+DROP PACKAGE IF EXISTS p1;
+
+--echo #
+--echo # Creating a package with a COMMENT clause
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 COMMENT 'package-p1-comment' AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 COMMENT 'package-body-p1-comment' AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE p1;
+
+--echo #
+--echo # Creating a package with a different DEFINER
+--echo #
+
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # Creating a package with a different DEFINER, with SQL SECURITY INVOKER
+--echo #
+
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # Creating a new package in a remote database
+--echo #
+
+CREATE DATABASE test2;
+
+DELIMITER $$;
+CREATE PACKAGE test2.test2 COMMENT 'package-test2-comment' AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2.test2 COMMENT 'package-body-test2-comment' AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ PROCEDURE p1 AS BEGIN SELECT f1(); END;
+END;
+$$
+DELIMITER ;$$
+
+DROP PACKAGE BODY test2.test2;
+DROP PACKAGE test2.test2;
+DROP DATABASE test2;
+
+
+--echo #
+--echo # MDEV-13139 Package-wide variables in CREATE PACKAGE
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT:=0;
+ PROCEDURE p1 AS
+ BEGIN
+ INSERT INTO t1 VALUES (a);
+ a:=a+1;
+ END;
+BEGIN
+ a:=10;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+--source sp-cache-invalidate.inc
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
diff --git a/mysql-test/suite/compat/oracle/t/column_compression.test b/mysql-test/suite/compat/oracle/t/column_compression.test
new file mode 100644
index 00000000..01d4977b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/column_compression.test
@@ -0,0 +1,85 @@
+--source include/have_innodb.inc
+--source include/have_csv.inc
+--source include/have_normal_bzip.inc
+
+SET sql_mode=ORACLE;
+
+SET column_compression_zlib_wrap=true;
+CREATE TABLE t1 (a BLOB COMPRESSED);
+INSERT INTO t1 VALUES (REPEAT('a',10000));
+SELECT DATA_LENGTH<100 AS c FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_NAME='t1' AND TABLE_SCHEMA='test';
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-17363 - Compressed columns cannot be restored from dump
+--echo #
+
+--error ER_WRONG_FIELD_SPEC
+CREATE TABLE t1(a INT NOT NULL COMPRESSED);
+SHOW WARNINGS;
+
+CREATE TABLE t1(
+ a JSON COMPRESSED,
+ b VARCHAR(1000) COMPRESSED BINARY,
+ c NVARCHAR(1000) COMPRESSED BINARY,
+ d TINYTEXT COMPRESSED BINARY
+);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # VARCHAR and TEXT variants
+--echo #
+
+--let type=VARCHAR(10)
+--source include/column_compression_syntax_varchar.inc
+
+--let type=VARCHAR2(10)
+--source include/column_compression_syntax_varchar.inc
+
+--let type=TINYTEXT
+--source include/column_compression_syntax_varchar.inc
+
+--let type=TEXT
+--source include/column_compression_syntax_varchar.inc
+
+--let type=MEDIUMTEXT
+--source include/column_compression_syntax_varchar.inc
+
+--let type=LONGTEXT
+--source include/column_compression_syntax_varchar.inc
+
+
+--echo #
+--echo # VARBINARY and BLOB variables
+--echo #
+
+--let type=VARCHAR(10)
+--source include/column_compression_syntax_varbinary.inc
+
+--let type=TINYBLOB
+--source include/column_compression_syntax_varbinary.inc
+
+--let type=BLOB
+--source include/column_compression_syntax_varbinary.inc
+
+--let type=MEDIUMBLOB
+--source include/column_compression_syntax_varbinary.inc
+
+--let type=LONGBLOB
+--source include/column_compression_syntax_varbinary.inc
+
+
+--echo #
+--echo # NVARCHAR
+--echo #
+
+CREATE TABLE t1 (a NVARCHAR(10) COMPRESSED);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+--error ER_PARSE_ERROR
+CREATE TABLE t1 (a NVARCHAR(10) COMPRESSED BINARY COMPRESSED);
+--error ER_PARSE_ERROR
+CREATE TABLE t1 (a NVARCHAR(10) COMPRESSED DEFAULT '' COMPRESSED);
diff --git a/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test b/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test
new file mode 100644
index 00000000..0affc4ef
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test
@@ -0,0 +1,170 @@
+SET sql_mode=ORACLE;
+
+delimiter |;
+--error ER_INVALID_AGGREGATE_FUNCTION
+create aggregate function f1(x INT) return INT AS
+begin
+ insert into t1(sal) values (x);
+ return x;
+end|
+
+--error ER_NOT_AGGREGATE_FUNCTION
+create function f1(x INT) return INT AS
+begin
+ set x=5;
+ fetch group next row;
+return x+1;
+end |
+
+DELIMITER ;|
+
+
+CREATE TABLE marks(stud_id INT, grade_count INT);
+INSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8);
+SELECT * FROM marks;
+
+--echo # Using PL/SQL syntax: EXCEPTION WHEN NO_DATA_FOUND
+
+DELIMITER //;
+CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS
+ count_students INT DEFAULT 0;
+BEGIN
+ LOOP
+ FETCH GROUP NEXT ROW;
+ IF x THEN
+ count_students:= count_students + 1;
+ END IF;
+ END LOOP;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RETURN count_students;
+END aggregate_count //
+DELIMITER ;//
+SELECT aggregate_count(stud_id) FROM marks;
+DROP FUNCTION IF EXISTS aggregate_count;
+
+
+--echo # Using SQL/PSM systax: CONTINUE HANDLER
+
+DELIMITER //;
+CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS
+ count_students INT DEFAULT 0;
+ CONTINUE HANDLER FOR NOT FOUND RETURN count_students;
+BEGIN
+ LOOP
+ FETCH GROUP NEXT ROW;
+ IF x THEN
+ SET count_students= count_students + 1;
+ END IF;
+ END LOOP;
+END //
+DELIMITER ;//
+SELECT aggregate_count(stud_id) FROM marks;
+DROP FUNCTION IF EXISTS aggregate_count;
+
+
+DROP TABLE marks;
+
+
+--echo #
+--echo # MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW
+--echo #
+
+
+DELIMITER $$;
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE PROCEDURE p1 AS
+BEGIN
+ FETCH GROUP NEXT ROW;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_NOT_AGGREGATE_FUNCTION
+BEGIN NOT ATOMIC
+ FETCH GROUP NEXT ROW;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE DEFINER=root@localhost FUNCTION f1 RETURN INT AS
+BEGIN
+ FETCH GROUP NEXT ROW;
+ RETURN 0;
+END;
+$$
+DELIMITER ;$$
+
+
+CREATE TABLE t1 (a INT);
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE TRIGGER tr1
+ AFTER INSERT ON t1 FOR EACH ROW
+ FETCH GROUP NEXT ROW;
+DROP TABLE t1;
+
+
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE EVENT ev1
+ ON SCHEDULE EVERY 1 HOUR
+ STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH
+ ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK
+DO FETCH GROUP NEXT ROW;
+
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ FETCH GROUP NEXT ROW; -- In a package procedure
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 0;
+ END;
+END;
+$$
+
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ FETCH GROUP NEXT ROW; -- In a package function
+ RETURN 0;
+ END;
+END;
+$$
+
+--error ER_NOT_AGGREGATE_FUNCTION
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 0;
+ END;
+BEGIN
+ FETCH GROUP NEXT ROW; -- In a package executable section
+END;
+$$
+
+DELIMITER ;$$
+DROP PACKAGE pkg1;
diff --git a/mysql-test/suite/compat/oracle/t/empty_string_literal.test b/mysql-test/suite/compat/oracle/t/empty_string_literal.test
new file mode 100644
index 00000000..3c612f72
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/empty_string_literal.test
@@ -0,0 +1,8 @@
+USE test;
+--echo #
+--echo # MDEV-14013 : sql_mode=EMPTY_STRING_IS_NULL
+--echo #
+
+set @mode='ORACLE,EMPTY_STRING_IS_NULL';
+
+--source include/empty_string_literal.inc
diff --git a/mysql-test/suite/compat/oracle/t/events.test b/mysql-test/suite/compat/oracle/t/events.test
new file mode 100644
index 00000000..fb56af51
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/events.test
@@ -0,0 +1,37 @@
+-- source include/not_embedded.inc
+
+set sql_mode='ORACLE';
+
+--echo #
+--echo # MDEV-16891 EVENTs created with SQL_MODE=ORACLE fail to execute
+--echo #
+
+SET GLOBAL event_scheduler=off;
+
+SET sql_mode='ORACLE';
+CREATE TABLE t1 (a TIMESTAMP);
+CREATE EVENT e1
+ ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MICROSECOND
+ DO INSERT INTO t1 VALUES(NOW());
+SET GLOBAL event_scheduler=on;
+
+let $wait_timeout = 10;
+let $wait_condition =
+ SELECT COUNT(*) = 0
+ FROM INFORMATION_SCHEMA.EVENTS
+ WHERE event_schema = 'test' AND event_name = 'e1';
+--source include/wait_condition.inc
+
+SELECT COUNT(*) FROM t1;
+DROP TABLE t1;
+
+SET GLOBAL event_scheduler=off;
+
+--echo #
+--echo # MDEV-28588 SIGSEGV in __memmove_avx_unaligned_erms, strmake_root
+--echo #
+CREATE EVENT ev ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO BEGIN END;
+--vertical_results
+SELECT EVENT_DEFINITION FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='test' AND EVENT_NAME='ev';
+--horizontal_results
+DROP EVENT ev;
diff --git a/mysql-test/suite/compat/oracle/t/exception.test b/mysql-test/suite/compat/oracle/t/exception.test
new file mode 100644
index 00000000..6448a6ef
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/exception.test
@@ -0,0 +1,457 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # sql_mode=ORACLE: Predefined exceptions: TOO_MANY_ROWS, NO_DATA_FOUND, DUP_VAL_ON_INDEX
+--echo #
+
+--echo #
+--echo # Testing NO_DATA_FOUND and TOO_MANY_ROWS
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1 LIMIT lim;
+EXCEPTION
+ WHEN TOO_MANY_ROWS THEN res:='--- too_many_rows cought ---';
+ WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL p1(0, @res);
+SELECT @res;
+CALL p1(2, @res);
+SELECT @res;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing DUP_VAL_ON_INDEX
+--echo #
+
+CREATE TABLE t1 (a INT PRIMARY KEY);
+DELIMITER $$;
+CREATE PROCEDURE p1(res OUT VARCHAR)
+AS
+BEGIN
+ INSERT INTO t1 VALUES (10);
+ INSERT INTO t1 VALUES (10);
+EXCEPTION
+ WHEN DUP_VAL_ON_INDEX THEN res:='--- dup_val_on_index cought ---';
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL p1(@res);
+SELECT @res;
+SELECT * FROM t1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
+--echo #
+
+--echo #
+--echo # RAISE outside of an SP context
+--echo #
+
+--error ER_SP_COND_MISMATCH
+RAISE NO_DATA_FOUND;
+--error ER_SP_COND_MISMATCH
+RAISE INVALID_CURSOR;
+--error ER_SP_COND_MISMATCH
+RAISE DUP_VAL_ON_INDEX;
+--error ER_SP_COND_MISMATCH
+RAISE TOO_MANY_ROWS;
+
+--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
+RAISE;
+
+
+--echo #
+--echo # RAISE for an undefinite exception
+--echo #
+
+DELIMITER $$;
+--error ER_SP_COND_MISMATCH
+CREATE PROCEDURE p1
+AS
+BEGIN
+ RAISE xxx;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # RAISE for predefined exceptions
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ RAISE no_data_found;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ RAISE invalid_cursor;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1();
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ RAISE dup_val_on_index;
+END;
+$$
+DELIMITER ;$$
+--error ER_DUP_ENTRY
+CALL p1();
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ raise too_many_rows;
+END;
+$$
+DELIMITER ;$$
+--error ER_TOO_MANY_ROWS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # RAISE with no exception name (resignal)
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+BEGIN
+ RAISE;
+END;
+$$
+DELIMITER ;$$
+--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
+CALL p1();
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1(lim INT)
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1 LIMIT lim;
+EXCEPTION
+ WHEN TOO_MANY_ROWS THEN RAISE;
+ WHEN NO_DATA_FOUND THEN RAISE;
+END;
+$$
+DELIMITER ;$$
+CALL p1(0);
+--error ER_TOO_MANY_ROWS
+CALL p1(2);
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1(lim INT)
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1 LIMIT lim;
+EXCEPTION
+ WHEN OTHERS THEN RAISE;
+END;
+$$
+DELIMITER ;$$
+CALL p1(0);
+--error ER_TOO_MANY_ROWS
+CALL p1(2);
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a INT;
+ CURSOR c IS SELECT a FROM t1;
+BEGIN
+ FETCH c INTO a;
+EXCEPTION
+ WHEN INVALID_CURSOR THEN RAISE;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a INT;
+ CURSOR c IS SELECT a FROM t1;
+BEGIN
+ FETCH c INTO a;
+EXCEPTION
+ WHEN OTHERS THEN RAISE;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing that warning-alike errors are caught by OTHERS
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN VARCHAR
+AS
+ a INT:=10;
+BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'OK';
+EXCEPTION
+ WHEN OTHERS THEN RETURN 'Exception';
+END;
+$$
+DELIMITER ;$$
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
+--echo #
+
+
+--echo #
+--echo # MDEV-10587 sql_mode=ORACLE: User defined exceptions
+--echo #
+
+--echo #
+--echo # Checking that duplicate WHEN clause is not allowed
+--echo #
+
+DELIMITER $$;
+--error ER_SP_DUP_HANDLER
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+ e EXCEPTION;
+BEGIN
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN 'Got exception e';
+ WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Checking that raised user exceptions are further caught by name
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+--error ER_SIGNAL_EXCEPTION
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that raised user exceptions are further caught by OTHERS
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN 'Got some exception';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that 'WHEN e .. WHEN f' does not produce ER_SP_DUP_HANDLER
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE e; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE f; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that resignaled user exceptions are further caught by name
+--echo #
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN a || 'Got EXCEPTION2/e;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+--error ER_SIGNAL_EXCEPTION
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that resignaled user exceptions are further caught by OTHERS
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # End of MDEV-10587 sql_mode=ORACLE: User defined exceptions
+--echo #
+
+--echo #
+--echo # MDEV-12088 sql_mode=ORACLE: Do not require BEGIN..END in multi-statement exception handlers in THEN clause
+--echo #
+CREATE TABLE t1 (a INT PRIMARY KEY);
+INSERT INTO t1 VALUES (10),(20),(30);
+DELIMITER $$;
+CREATE PROCEDURE p1(a INT) AS
+BEGIN
+ INSERT INTO t1 (a) VALUES (a);
+EXCEPTION
+ WHEN DUP_VAL_ON_INDEX THEN
+ a:= a+1;
+ INSERT INTO t1 VALUES (a);
+ WHEN OTHERS THEN
+ NULL;
+ NULL;
+END;
+$$
+DELIMITER ;$$
+CALL p1(30);
+SELECT * FROM t1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/func_add_months.test b/mysql-test/suite/compat/oracle/t/func_add_months.test
new file mode 100644
index 00000000..ca9391ef
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_add_months.test
@@ -0,0 +1,38 @@
+--echo Test for ADD_MONTHS
+
+CREATE TABLE t1(c1 int, c2 datetime, c3 date, c4 time, c5 timestamp);
+
+INSERT INTO t1 VALUES (1, '2011-11-12 12:10:11', '2011-11-12', '12:10:11', '2011-11-12 12:10:11');
+INSERT INTO t1 VALUES (2, '2021-11-12 00:23:12', '2021-11-12', '00:23:12', '2021-11-12 00:23:12');
+INSERT INTO t1 VALUES (3, '2011-01-22 16:45:45', '2011-01-22', '16:45:45', '2011-01-22 16:45:45');
+INSERT INTO t1 VALUES (4, '2031-05-12 04:11:34', '2031-05-12', '04:11:34', '2031-05-12 04:11:34');
+INSERT INTO t1 VALUES (5, '2031-09-02 08:15:22', '2031-09-02', '08:15:22', '2031-09-02 08:15:22');
+INSERT INTO t1 VALUES (6, '0000-09-02 00:00:00', '0000-09-02', '00:00:00', '1980-09-02 00:00:00');
+INSERT INTO t1 VALUES (7, '9999-09-02', '9999-09-02', '00:00:00', '1980-09-02');
+
+# some normal case
+SELECT c1, ADD_MONTHS(c2, 2), ADD_MONTHS(c3, 2), ADD_MONTHS(c5, 2) FROM t1;
+SELECT c1, ADD_MONTHS(c2, 15), ADD_MONTHS(c3, 200), ADD_MONTHS(c5, 2000) FROM t1;
+SELECT c1, ADD_MONTHS(c2, 0), ADD_MONTHS(c3, -200), ADD_MONTHS(c5, -2) FROM t1;
+SELECT c1, ADD_MONTHS(c2, -15), ADD_MONTHS(c3, -111), ADD_MONTHS(c5, 2) FROM t1;
+
+# for time type, it will be overflow
+SELECT ADD_MONTHS(c4, 11) FROM t1 WHERE c1 = 1;
+
+UPDATE t1 SET c2=ADD_MONTHS(c2, 2);
+SELECT c2 FROM t1;
+
+EXPLAIN EXTENDED SELECT c1, ADD_MONTHS(c2, -15) FROM t1 WHERE c1 = 1;
+
+# string type can be convert to datetime type
+SELECT ADD_MONTHS("2000-10-10", 12);
+SELECT ADD_MONTHS("2000:10:10", 12);
+
+# number type can not be convert datetime type
+SELECT ADD_MONTHS(2000, 12);
+
+# last day of the month
+SELECT ADD_MONTHS('2011-01-31', 1), ADD_MONTHS('2012-01-31', 1), ADD_MONTHS('2012-01-31', 2), ADD_MONTHS('2012-01-31', 3);
+SELECT ADD_MONTHS('2011-01-30', 1), ADD_MONTHS('2012-01-30', 1), ADD_MONTHS('2012-01-30', 2), ADD_MONTHS('2012-01-30', 3);
+
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/func_case.test b/mysql-test/suite/compat/oracle/t/func_case.test
new file mode 100644
index 00000000..d5e0d650
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_case.test
@@ -0,0 +1,9 @@
+#
+# Testing CASE and its abbreviations
+#
+
+SET sql_mode=ORACLE;
+
+SELECT NVL(NULL, 'a'), NVL('a', 'b');
+
+SELECT NVL2(NULL, 'a', 'b'), NVL2('a', 'b', 'c');
diff --git a/mysql-test/suite/compat/oracle/t/func_concat.test b/mysql-test/suite/compat/oracle/t/func_concat.test
new file mode 100644
index 00000000..5a613242
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_concat.test
@@ -0,0 +1,184 @@
+#
+# Testing CONCAT with null values
+#
+
+SET sql_mode=ORACLE;
+
+EXPLAIN EXTENDED SELECT 'a'||'b'||'c';
+EXPLAIN EXTENDED SELECT CONCAT('a'||'b'||'c');
+
+SELECT '' || '';
+SELECT '' || 'b';
+SELECT '' || NULL;
+SELECT 'a' || '';
+SELECT 'a' || 'b';
+SELECT 'a' || NULL;
+SELECT NULL || '';
+SELECT NULL || 'b';
+SELECT NULL || NULL;
+
+SELECT '' || '' || '';
+SELECT '' || '' || 'c';
+SELECT '' || '' || NULL;
+SELECT '' || 'b' || '';
+SELECT '' || 'b' || 'c';
+SELECT '' || 'b' || NULL;
+SELECT '' || NULL || '';
+SELECT '' || NULL || 'c';
+SELECT '' || NULL || NULL;
+
+SELECT 'a' || '' || '';
+SELECT 'a' || '' || 'c';
+SELECT 'a' || '' || NULL;
+SELECT 'a' || 'b' || '';
+SELECT 'a' || 'b' || 'c';
+SELECT 'a' || 'b' || NULL;
+SELECT 'a' || NULL || '';
+SELECT 'a' || NULL || 'c';
+SELECT 'a' || NULL || NULL;
+
+SELECT NULL || '' || '';
+SELECT NULL || '' || 'c';
+SELECT NULL || '' || NULL;
+SELECT NULL || 'b' || '';
+SELECT NULL || 'b' || 'c';
+SELECT NULL || 'b' || NULL;
+SELECT NULL || NULL || '';
+SELECT NULL || NULL || 'c';
+SELECT NULL || NULL || NULL;
+
+CREATE TABLE t1 (a VARCHAR(10), b VARCHAR(10), c VARCHAR(10));
+
+INSERT INTO t1 VALUES ('', '', '');
+INSERT INTO t1 VALUES ('', '', 'c');
+INSERT INTO t1 VALUES ('', '', NULL);
+INSERT INTO t1 VALUES ('', 'b', '');
+INSERT INTO t1 VALUES ('', 'b', 'c');
+INSERT INTO t1 VALUES ('', 'b', NULL);
+INSERT INTO t1 VALUES ('', NULL, '');
+INSERT INTO t1 VALUES ('', NULL, 'c');
+INSERT INTO t1 VALUES ('', NULL, NULL);
+
+INSERT INTO t1 VALUES ('a', '', '');
+INSERT INTO t1 VALUES ('a', '', 'c');
+INSERT INTO t1 VALUES ('a', '', NULL);
+INSERT INTO t1 VALUES ('a', 'b', '');
+INSERT INTO t1 VALUES ('a', 'b', 'c');
+INSERT INTO t1 VALUES ('a', 'b', NULL);
+INSERT INTO t1 VALUES ('a', NULL, '');
+INSERT INTO t1 VALUES ('a', NULL, 'c');
+INSERT INTO t1 VALUES ('a', NULL, NULL);
+
+INSERT INTO t1 VALUES (NULL, '', '');
+INSERT INTO t1 VALUES (NULL, '', 'c');
+INSERT INTO t1 VALUES (NULL, '', NULL);
+INSERT INTO t1 VALUES (NULL, 'b', '');
+INSERT INTO t1 VALUES (NULL, 'b', 'c');
+INSERT INTO t1 VALUES (NULL, 'b', NULL);
+INSERT INTO t1 VALUES (NULL, NULL, '');
+INSERT INTO t1 VALUES (NULL, NULL, 'c');
+INSERT INTO t1 VALUES (NULL, NULL, NULL);
+
+SELECT LENGTH(a||b||c), a||b||c FROM t1 ORDER BY a,b,c;
+SELECT LENGTH(CONCAT(a||b||c)), CONCAT(a||b||c) FROM t1 ORDER BY a,b,c;
+
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12478 CONCAT function inside view casts values incorrectly with Oracle sql_mode
+--echo #
+
+SET sql_mode=ORACLE;
+CREATE VIEW v1 AS SELECT 'foo'||NULL||'bar' AS test;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+SET sql_mode=DEFAULT;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
+
+SET sql_mode=DEFAULT;
+CREATE VIEW v1 AS SELECT CONCAT('foo',NULL,'bar') AS test;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+SET sql_mode=ORACLE;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
+
+SET sql_mode=DEFAULT;
+CREATE VIEW v1 AS SELECT '0'||'1' AS test;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+SET sql_mode=ORACLE;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
+
+
+--echo #
+--echo # MDEV-16186 Concatenation operator || returns wrong results in sql_mode=ORACLE
+--echo #
+
+# Concatenation operator || has the same precedence with +
+# (stronger than << and weaker than * ^)
+
+SELECT -1<<1||1 AS a FROM DUAL;
+SELECT -1||0<<1 AS a FROM DUAL;
+
+EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL;
+EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL;
+
+SELECT -1+1||1 AS a FROM DUAL;
+SELECT -1||0+1 AS a FROM DUAL;
+
+EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL;
+EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL;
+
+SELECT 1*1||-1 AS a FROM DUAL;
+SELECT 1||1*-1 AS a FROM DUAL;
+
+EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL;
+EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL;
+
+SELECT -1^1||1 AS a FROM DUAL;
+SELECT -1||0^1 AS a FROM DUAL;
+
+EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL;
+EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL;
+
+
+--echo #
+--echo # MDEV-17359 Concatenation operator || in like expression failed in sql_mode=ORACLE
+--echo #
+
+SELECT 'abc' LIKE 'a'||'%';
+EXPLAIN EXTENDED SELECT 'abc' LIKE 'a'||'%';
+
+SELECT 'x' FROM DUAL WHERE 11 LIKE 1||1;
+SELECT 'x' FROM DUAL WHERE 1||1 LIKE 11;
+SELECT 'x' FROM DUAL WHERE 1||1 LIKE 1||1;
+
+CREATE TABLE t1 (c1 VARCHAR(10),c2 VARCHAR(10), ord INTEGER);
+INSERT INTO t1 VALUES ('a', 'ab' ,1);
+INSERT INTO t1 VALUES ('ab', 'ab', 2);
+INSERT INTO t1 VALUES ('abc', 'ab', 3);
+
+SELECT c1 FROM t1 WHERE c1 LIKE '%'||'b' ORDER BY ord;
+EXPLAIN EXTENDED SELECT c1 FROM t1 WHERE c1 LIKE '%'||'b' ORDER BY ord;
+
+SELECT c1 FROM t1 WHERE c1 LIKE c2||'%'||'c' ORDER BY ord;
+EXPLAIN EXTENDED SELECT c1 FROM t1 WHERE c1 LIKE c2||'%'||'c' ORDER BY ord;
+
+SELECT 'x' FROM t1 WHERE c1||c2 LIKE 'aa%';
+EXPLAIN EXTENDED SELECT 'x' FROM t1 WHERE c1||c2 LIKE 'aa%';
+
+SELECT 'x' FROM t1 WHERE c1||c2 LIKE c2||c1;
+EXPLAIN EXTENDED SELECT 'x' FROM t1 WHERE c1||c2 LIKE c2||c1;
+
+CREATE VIEW v1 AS SELECT c1, c2, c1 LIKE c2||'_' FROM t1 ORDER BY ord;
+SELECT * FROM v1;
+EXPLAIN EXTENDED SELECT * FROM v1;
+
+DROP VIEW v1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/func_decode.test b/mysql-test/suite/compat/oracle/t/func_decode.test
new file mode 100644
index 00000000..b8be7178
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_decode.test
@@ -0,0 +1,98 @@
+SET sql_mode=ORACLE;
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE(10);
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE(10,10);
+
+SELECT DECODE(10,10,'x10');
+SELECT DECODE(11,10,'x10');
+
+SELECT DECODE(10,10,'x10','def');
+SELECT DECODE(11,10,'x10','def');
+
+SELECT DECODE(10,10,'x10',11,'x11','def');
+SELECT DECODE(11,10,'x10',11,'x11','def');
+SELECT DECODE(12,10,'x10',11,'x11','def');
+
+EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def');
+
+CREATE TABLE decode (decode int);
+DROP TABLE decode;
+
+
+--echo #
+--echo # MDEV-13863 sql_mode=ORACLE: DECODE does not treat two NULLs as equivalent
+--echo #
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE(10);
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE(10,10);
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE_ORACLE(10);
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT DECODE_ORACLE(10,10);
+
+
+EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11');
+EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def');
+EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11');
+EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11','def');
+
+CREATE TABLE t1 (a INT);
+CREATE VIEW v1 AS
+ SELECT
+ DECODE(a,1,'x1',NULL,'xNULL') AS d1,
+ DECODE(a,1,'x1',NULL,'xNULL','xELSE') AS d2,
+ DECODE_ORACLE(a,1,'x1',NULL,'xNULL') AS d3,
+ DECODE_ORACLE(a,1,'x1',NULL,'xNULL','xELSE') AS d4
+ FROM t1;
+SHOW CREATE VIEW v1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+SELECT DECODE(TIME'10:20:31','10:20:31','then1','10:20:32','then2','def');
+SELECT DECODE(TIME'10:20:32','10:20:31','then1','10:20:32','then2','def');
+SELECT DECODE(TIME'10:20:33','10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def');
+SELECT DECODE(NULL,TIME'10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def');
+
+SELECT DECODE(TIMESTAMP'2001-01-01 10:20:31','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def');
+SELECT DECODE(TIMESTAMP'2001-01-01 10:20:32','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def');
+SELECT DECODE(TIMESTAMP'2001-01-01 10:20:33','2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def');
+SELECT DECODE(NULL,TIMESTAMP'2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def');
+
+SELECT DECODE('w1','w1','then1','w2','then2','def');
+SELECT DECODE('w2','w1','then1','w2','then2','def');
+SELECT DECODE('w3','w1','then1',NULL,'then2NULL','w3','then3','def');
+SELECT DECODE(NULL,'w1','then1',NULL,'then2NULL','w3','then3','def');
+
+SELECT DECODE(1,1,'then1',2,'then2','def');
+SELECT DECODE(2,1,'then1',2,'then2','def');
+SELECT DECODE(3,1,'then1',NULL,'then2NULL',3,'then3','def');
+SELECT DECODE(NULL,1,'then1',NULL,'then2NULL',3,'then3','def');
+SELECT DECODE(CAST(NULL AS SIGNED),1,'then1',NULL,'then2NULL',3,'then3','def');
+
+SELECT DECODE(1.0,1.0,'then1',2.0,'then2','def');
+SELECT DECODE(2.0,1.0,'then1',2.0,'then2','def');
+SELECT DECODE(3.0,1.0,'then1',NULL,'then2NULL',3.0,'then3','def');
+SELECT DECODE(NULL,1.0,'then1',NULL,'then2NULL',3.0,'then3','def');
+SELECT DECODE(CAST(NULL AS DECIMAL),1.0,'then1',NULL,'then2NULL',3.0,'then3','def');
+
+SELECT DECODE(1e0,1e0,'then1',2e0,'then2','def');
+SELECT DECODE(2e0,1e0,'then1',2e0,'then2','def');
+SELECT DECODE(3e0,1e0,'then1',NULL,'then2NULL',3e0,'then3','def');
+SELECT DECODE(NULL,1e0,'then1',NULL,'then2NULL',3e0,'then3','def');
+SELECT DECODE(CAST(NULL AS DOUBLE),1e0,'then1',NULL,'then2NULL',3e0,'then3','def');
+
+SELECT DECODE(NULL,NULL,1,2) FROM DUAL;
+SELECT DECODE(NULL,10,10,NULL,1,2) FROM DUAL;
+
+SELECT DECODE_ORACLE(NULL,NULL,1,2) FROM DUAL;
+SELECT DECODE_ORACLE(NULL,10,10,NULL,1,2) FROM DUAL;
+
+CREATE OR REPLACE TABLE t1 (a VARCHAR(10) DEFAULT NULL);
+INSERT INTO t1 VALUES (NULL),(1);
+SELECT a, DECODE(a,NULL,1,2) FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/func_length.test b/mysql-test/suite/compat/oracle/t/func_length.test
new file mode 100644
index 00000000..7b76d33a
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_length.test
@@ -0,0 +1,18 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-12783 sql_mode=ORACLE: Functions LENGTH() and LENGTHB()
+--echo #
+#
+# Testing LENGTH / LENGTHB
+#
+# LENGTH : return the length of char
+# LENGTHB : return the length of byte
+
+
+SELECT LENGTH(null), LENGTH('a'), LENGTH(123);
+SELECT LENGTHB(null), LENGTHB('a'), LENGTHB(123);
+
+SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));
+SELECT LENGTHB(_utf8 0xC39F), LENGTHB(CHAR(14844588 USING utf8));
+EXPLAIN EXTENDED SELECT LENGTH('a'), LENGTHB('a');
diff --git a/mysql-test/suite/compat/oracle/t/func_misc.test b/mysql-test/suite/compat/oracle/t/func_misc.test
new file mode 100644
index 00000000..c5b42134
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_misc.test
@@ -0,0 +1,346 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10578 sql_mode=ORACLE: SP control functions SQLCODE, SQLERRM
+--echo #
+
+--echo #
+--echo # Using SQLCODE and SQLERRM outside of an SP
+--echo #
+
+--error ER_BAD_FIELD_ERROR
+SELECT SQLCODE;
+
+--error ER_BAD_FIELD_ERROR
+SELECT SQLERRM;
+
+CREATE TABLE t1 (SQLCODE INT, SQLERRM VARCHAR(10));
+INSERT INTO t1 VALUES (10, 'test');
+SELECT SQLCODE, SQLERRM FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # Normal SQLCODE and SQLERRM usage
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(stmt VARCHAR)
+AS
+BEGIN
+ EXECUTE IMMEDIATE stmt;
+ SELECT 'Error1: ' || SQLCODE || ' ' || SQLERRM;
+EXCEPTION
+ WHEN OTHERS THEN
+ SELECT 'Error2: ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1('SELECT 1');
+CALL p1('xxx');
+CALL p1('SELECT 1');
+DROP PROCEDURE p1;
+
+--echo #
+--echo # SQLCODE and SQLERRM hidden by local variables
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ sqlcode INT:= 10;
+ sqlerrm VARCHAR(64) := 'test';
+BEGIN
+ SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ sqlcode INT;
+ sqlerrm VARCHAR(64);
+BEGIN
+ SQLCODE:= 10;
+ sqlerrm:= 'test';
+ SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # SQLCODE and SQLERRM hidden by parameters
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(sqlcode INT, sqlerrm VARCHAR)
+AS
+BEGIN
+ SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1(10, 'test');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # SQLCODE and SQLERRM in CREATE..SELECT
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ CREATE TABLE t1 AS SELECT SQLCODE, SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # SQLCODE and SQLERRM in EXPLAIN EXTENDED SELECT
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ EXPLAIN EXTENDED SELECT SQLCode, SQLErrm;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+
+
+--echo #
+--echo # Warning-alike errors in stored functions
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN VARCHAR
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN VARCHAR
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
+EXCEPTION
+ WHEN OTHERS THEN
+ RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Warning-alike errors in stored procedures
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1(res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1;
+ res:= 'No exception ' || SQLCODE || ' ' || SQLERRM;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ res:= 'Exception ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1(@a);
+SELECT @a;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1(res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ SELECT a INTO a FROM t1;
+ res:= 'No exception ' || SQLCODE || ' ' || SQLERRM;
+EXCEPTION
+ WHEN OTHERS THEN
+ res:= 'Exception ' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+CALL p1(@a);
+SELECT @a;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # SQLCODE and SQLERRM are cleared on RETURN
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN VARCHAR
+AS
+ a INT:=10;
+BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'Value=' || a;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN RETURN 'Exception|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+CREATE FUNCTION f2 RETURN VARCHAR
+AS
+ a VARCHAR(128);
+BEGIN
+ RETURN f1() || '|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f1() FROM DUAL;
+SELECT f2() FROM DUAL;
+DROP TABLE t1;
+DROP FUNCTION f2;
+DROP FUNCTION f1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN VARCHAR
+AS
+ a INT:=10;
+BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'Value=' || a;
+EXCEPTION
+ WHEN OTHERS THEN RETURN 'Exception|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+CREATE FUNCTION f2 RETURN VARCHAR
+AS
+ a VARCHAR(128);
+BEGIN
+ RETURN f1() || '|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f1() FROM DUAL;
+SELECT f2() FROM DUAL;
+DROP TABLE t1;
+DROP FUNCTION f2;
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # SQLCODE and SQLERRM are cleared on a return from a PROCEDURE
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1(res OUT VARCHAR)
+AS
+ a INT:=10;
+BEGIN
+ SELECT a INTO a FROM t1;
+ res:='Value=' || a;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN res:='Exception|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+CREATE FUNCTION f2 RETURN VARCHAR
+AS
+ res VARCHAR(128);
+BEGIN
+ CALL p1(res);
+ RETURN res || '|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f2() FROM DUAL;
+DROP FUNCTION f2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1(res OUT VARCHAR)
+AS
+ a INT:=10;
+BEGIN
+ SELECT a INTO a FROM t1;
+ res:='Value=' || a;
+EXCEPTION
+ WHEN OTHERS THEN res:='Exception|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+CREATE FUNCTION f2 RETURN VARCHAR
+AS
+ res VARCHAR(128);
+BEGIN
+ CALL p1(res);
+ RETURN res || '|' || SQLCODE || ' ' || SQLERRM;
+END;
+$$
+DELIMITER ;$$
+SELECT f2() FROM DUAL;
+DROP FUNCTION f2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of MDEV-10578 sql_mode=ORACLE: SP control functions SQLCODE, SQLERRM
+--echo #
+
+--echo #
+--echo # MDEV-12854 Synchronize CREATE..SELECT data type and result set metadata data type for INT functions
+--echo #
+
+--enable_metadata
+--disable_ps_protocol
+DELIMITER $$;
+BEGIN
+ SELECT SQLCODE;
+END
+$$
+DELIMITER ;$$
+--enable_ps_protocol
+--disable_metadata
diff --git a/mysql-test/suite/compat/oracle/t/func_pad.test b/mysql-test/suite/compat/oracle/t/func_pad.test
new file mode 100644
index 00000000..bc33a9fa
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_pad.test
@@ -0,0 +1,31 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-15739 - sql_mode=ORACLE: Make LPAD and RPAD return NULL instead of empty string
+--echo #
+
+SELECT RPAD('a',0), RPAD('abc',1), RPAD('abc',2) ;
+SELECT RPAD('a',0,'.'), RPAD('abc',1,'.'), RPAD('abc',2,'.') ;
+SELECT LPAD('a',0), LPAD('abc',1), LPAD('abc',2) ;
+SELECT LPAD('a',0,'.'), LPAD('abc',1,'.'), LPAD('abc',2,'.') ;
+
+CREATE TABLE t1 (c1 VARCHAR(10),c2 INTEGER, c3 VARCHAR(10), ord INTEGER);
+INSERT INTO t1 VALUES ('a',1,null,1);
+INSERT INTO t1 VALUES ('a',null,'.',2);
+INSERT INTO t1 VALUES (null,1,'.',3);
+INSERT INTO t1 VALUES ('a',-1,'.',4);
+INSERT INTO t1 VALUES ('a',0,'.',5);
+INSERT INTO t1 VALUES ('a',1,'.',6);
+INSERT INTO t1 VALUES ('a',2,'.',7);
+
+SELECT LPAD(c1,c2,c3), LPAD(c1,c2) FROM t1 ORDER BY ord;
+SELECT RPAD(c1,c2,c3), RPAD(c1,c2) FROM t1 ORDER BY ord;
+
+EXPLAIN EXTENDED SELECT RPAD('a',0,'.'), LPAD('a',0,'.'), LPAD(c1,c2,c3), LPAD(c1,c2), RPAD(c1,c2,c3), RPAD(c1,c2) FROM t1 ORDER BY ord;
+
+CREATE VIEW v1 AS SELECT RPAD('a',0,'.') AS "C1", LPAD('a',0,'.') AS "C2", LPAD(c1,c2,c3) AS "C3", LPAD(c1,c2) AS "C4", RPAD(c1,c2,c3) AS "C5", RPAD(c1,c2) AS "C6" FROM t1 ORDER BY ord;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+SELECT c1||'-'||c2||'-'||c3||'-'||c4||'-'||c5||'-'||c6 FROM v1;
+DROP VIEW v1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/func_replace.test b/mysql-test/suite/compat/oracle/t/func_replace.test
new file mode 100644
index 00000000..0028f7d2
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_replace.test
@@ -0,0 +1,22 @@
+#
+# Testing replace with null args
+#
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-13003 - Oracle compatibility : Replace function
+--echo #
+
+SELECT REPLACE(null,'a','b') ;
+SELECT REPLACE('ab',null,'b') ;
+SELECT REPLACE('ab','a',null) ;
+SELECT REPLACE('ab',null,null) ;
+SELECT REPLACE('aaa','a',null) ;
+
+EXPLAIN EXTENDED SELECT REPLACE('ab','a',null) ;
+
+CREATE VIEW v1 AS SELECT REPLACE('ab','a',null) ;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
diff --git a/mysql-test/suite/compat/oracle/t/func_substr.test b/mysql-test/suite/compat/oracle/t/func_substr.test
new file mode 100644
index 00000000..b661dfd3
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_substr.test
@@ -0,0 +1,47 @@
+--echo #
+--echo # MDEV-14012 - sql_mode=Oracle: substr(): treat position 0 as position 1
+--echo # MDEV-10574 - sql_mode=Oracle: return null instead of empty string
+--echo #
+
+SET sql_mode=ORACLE;
+SELECT SUBSTR('abc',2,1),SUBSTR('abc',1,1), SUBSTR('abc',0,1) FROM dual;
+SELECT SUBSTR('abc',2),SUBSTR('abc',1), SUBSTR('abc',0) FROM dual;
+SELECT SUBSTR(null,2,1),SUBSTR(null,1), SUBSTR(null,0) FROM dual;
+SELECT SUBSTR('abc',-2),SUBSTR('abc',-1), SUBSTR('abc',-0) FROM dual;
+SELECT SUBSTR('abc',-2,1),SUBSTR('abc',-1,1), SUBSTR('abc',-0,1) FROM dual;
+SELECT SUBSTR('abc',null) FROM dual;
+SELECT SUBSTR('abc',2,null),SUBSTR('abc',1,null), SUBSTR('abc',0,null) FROM dual;
+SELECT SUBSTR('abc',2,0),SUBSTR('abc',1,0), SUBSTR('abc',0,0) FROM dual;
+SELECT SUBSTR('abc',2,-1),SUBSTR('abc',1,-1), SUBSTR('abc',0,-1) FROM dual;
+SELECT SUBSTR(SPACE(0),1) FROM DUAL;
+
+CREATE TABLE t1 (c1 VARCHAR(10),start INTEGER, length INTEGER);
+INSERT INTO t1 VALUES ('abc', 1, 1);
+INSERT INTO t1 VALUES ('abc', 0, 1);
+INSERT INTO t1 VALUES (null, 1, 1);
+INSERT INTO t1 VALUES (null, 0, 1);
+INSERT INTO t1 VALUES ('abc', 1, 0);
+INSERT INTO t1 VALUES ('abc', 0, 0);
+INSERT INTO t1 VALUES (null, 1, 0);
+INSERT INTO t1 VALUES (null, 0, 0);
+INSERT INTO t1 VALUES ('abc', 1, -1);
+INSERT INTO t1 VALUES ('abc', 0, -1);
+INSERT INTO t1 VALUES (null, 1, -1);
+INSERT INTO t1 VALUES (null, 0, -1);
+INSERT INTO t1 VALUES (SPACE(0), 0, 1);
+
+SELECT SUBSTR(c1,start,length) FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL);
+CREATE TABLE t2 AS SELECT SUBSTR(C1,1,1) AS C1 from t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+EXPLAIN EXTENDED SELECT SUBSTR('abc',2,1) ;
+
+CREATE VIEW v1 AS SELECT SUBSTR('abc',2,1) ;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
diff --git a/mysql-test/suite/compat/oracle/t/func_time.test b/mysql-test/suite/compat/oracle/t/func_time.test
new file mode 100644
index 00000000..c1174f7f
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_time.test
@@ -0,0 +1,25 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-16152 Expressions with INTERVAL return bad results in some cases
+--echo #
+
+SELECT TIMESTAMP'2001-01-01 10:20:30' - INTERVAL '10' YEAR AS c1,
+ -INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c2;
+
+SELECT TIMESTAMP'2001-01-01 10:20:30' + INTERVAL '10' YEAR AS c1,
+ INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c2,
+ +INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c3;
+
+EXPLAIN EXTENDED SELECT
+ TIMESTAMP'2001-01-01 10:20:30' - INTERVAL '10' YEAR AS c1,
+ -INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c2;
+
+EXPLAIN EXTENDED SELECT
+ TIMESTAMP'2001-01-01 10:20:30' + INTERVAL '10' YEAR AS c1,
+ INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c2,
+ +INTERVAL '10' YEAR + TIMESTAMP'2001-01-01 10:20:30' AS c3;
diff --git a/mysql-test/suite/compat/oracle/t/func_to_char.test b/mysql-test/suite/compat/oracle/t/func_to_char.test
new file mode 100644
index 00000000..7a403215
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_to_char.test
@@ -0,0 +1,236 @@
+##############################################################
+# testcase for TO_CHAR() function for oracle
+# Part of MDEV-20017 Implement TO_CHAR() Oracle compatible function
+##############################################################
+
+# Save sql_mode
+set @save_sql_mode=@@sql_mode;
+
+--echo #
+--echo # test for datetime
+--echo #
+
+CREATE TABLE t_to_char1(c0 int, c1 date, c2 time, c3 datetime);
+
+INSERT INTO t_to_char1 VALUES (1, '1000-1-1', '00:00:00', '1000-1-1 00:00:00');
+INSERT INTO t_to_char1 VALUES (2, '9999-12-31', '23:59:59', '9999-12-31 23:59:59');
+INSERT INTO t_to_char1 VALUES (3, '2021-01-03', '08:30:00', '2021-01-03 08:30:00');
+INSERT INTO t_to_char1 VALUES (4, '2021-07-03', '18:30:00', '2021-07-03 18:30:00');
+
+CREATE TABLE t_to_char2(c1 timestamp);
+INSERT INTO t_to_char2 VALUES ('1980-01-11 04:50:39');
+INSERT INTO t_to_char2 VALUES ('2000-11-11 12:50:00');
+INSERT INTO t_to_char2 VALUES ('2030-11-11 18:20:10');
+
+# test for timestamp
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char2;
+SELECT TO_CHAR(c1, 'HH24-MI-SS') FROM t_to_char2;
+
+# test full output format
+--echo #
+--echo # test YYYY/YY/MM/DD/HH/HH24/MI/SS
+--echo #
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:mi:ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test YYY/Y/MON/DD/DY/HH/HH12/MI/SS
+--echo #
+SELECT TO_CHAR(c1, 'YYY-MON-DD') AS C1, TO_CHAR(c2, 'HH12:MI:SS') AS C2, TO_CHAR(c3, 'Y-MONTH-DY HH:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyy-Mon-Dd') AS C1, TO_CHAR(c2, 'Hh12:mi:Ss') AS C2, TO_CHAR(c3, 'y-Month-Dy Hh:Mi:Ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test RRRR/RR/DAY
+--echo #
+SELECT TO_CHAR(c1, 'RRRR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'RRRR-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'RR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'Rrrr-Mm-Dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'Rrrr-mm-dd Hh24:mi:ss') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'rr-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:Mi:ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test AD/A.D./BC/B.C./AM/A.M./PM/P.M.
+--echo #
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'BCYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'BCYYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'B.C.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'B.C.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'bcyyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'BcYYyy-MM-DD Hh24:mi:sS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'b.c.yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mI:Ss') AS C2, TO_CHAR(c3, 'b.C.Yyyy-Mm-dd hH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'PMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD P.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'pmHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD p.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'AMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD A.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'amHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD a.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test format without order
+--echo #
+SELECT TO_CHAR(c1, 'MM-YYYY-DD') AS C1, TO_CHAR(c2, 'HH:SS:MI') AS C2, TO_CHAR(c3, 'DD-YY-MM MI:SS:HH24') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyy-Dd-Mon') AS C1, TO_CHAR(c2, 'mi:Hh12:Ss') AS C2, TO_CHAR(c3, 'Ss:Hh:Mi Dy-y-Month') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'Dd-Mm-Rrrr') AS C1, TO_CHAR(c2, 'ss:hh:mi') AS C2, TO_CHAR(c3, 'ss:Rrrr-hh24-dd mon:mi') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYYA.D.-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test for special characters
+--echo #
+SELECT TO_CHAR(c1, 'YYYYMMDD') AS C1, TO_CHAR(c2, 'HHMISS') AS C2, TO_CHAR(c3, 'YYMMDDHH24MISS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY!!MM@DD') AS C1, TO_CHAR(c2, 'HH#MI$SS') AS C2, TO_CHAR(c3, 'YY%MM^DD*HH24(MI)SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY_MM+DD') AS C1, TO_CHAR(c2, 'HH=MI{SS') AS C2, TO_CHAR(c3, 'YY}MMDDHH24MISS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY,MM.DD') AS C1, TO_CHAR(c2, 'HH/MI;SS') AS C2, TO_CHAR(c3, 'YY>MM<DD]HH24[MI\SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY||||MM|DD') AS C1, TO_CHAR(c2, 'HH&|MI|&|SS') AS C2, TO_CHAR(c3, 'YY&&&\\MM|&&|DD HH24|| MI&||"abx"|SS') AS C3 FROM t_to_char1;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY&MM-DD') FROM t_to_char1 where c0=1;
+SELECT TO_CHAR(c1, 'YYYY"abx"MM"bsz"DD') AS C1 FROM t_to_char1;
+
+--echo #
+--echo # test for other locale
+--echo #
+SET character_set_client='utf8';
+SET character_set_connection='utf8';
+SET character_set_results='utf8';
+SET lc_time_names='zh_TW';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='de_DE';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='en_US';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='zh_CN';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+
+--echo #
+--echo # test for invalid format
+--echo #
+
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYYaxMON-DAY') FROM t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY\nMON-DAY') FROM t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY\rMON-DAY') FROM t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY-分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'YYYYxDDD') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'YYYY&DDD') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'xxYYYY-DD') from t_to_char1 where c0 = 1;
+
+SET character_set_client='latin1';
+SET character_set_connection='latin1';
+SET character_set_results='latin1';
+--echo #
+--echo # test for unusual format
+--echo #
+select to_char(c3, 'YYYYYYYYYYYYYYY') from t_to_char1;
+select to_char(c3, 'YYYYYYYYYYYYYYYDDDDDD') from t_to_char1;
+
+--echo #
+--echo # oracle max length is 144
+--echo #
+
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY') from t_to_char1 where c0 = 1;
+CREATE TABLE t_f(c1 varchar(150));
+insert into t_f values('YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD');
+select to_char('2000-11-11', c1) from t_f;
+DROP TABLE t_f;
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD-MM') from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # now only support two parameter.
+--echo #
+select to_char(c3) from t_to_char1 where c0 =1;
+select to_char(c3, "YYYY-MM-DD HH:MI:SS") from t_to_char1 where c0 =1;
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "zh_CN") from t_to_char1 where c0 = 1;
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "NLS_DATE_LANGUAGE = zh_CN") from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # oracle support format but mariadb does not support
+--echo #
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'DDD') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'D') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'DS') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'IY') from t_to_char1 where c0 = 1;
+--error ER_STD_INVALID_ARGUMENT
+select to_char(c3, 'IYYY') from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # test for first argument data type
+--echo #
+--error ER_STD_INVALID_ARGUMENT
+select to_char(1, 'yyyy');
+--error ER_STD_INVALID_ARGUMENT
+select to_char(1.1, 'yyyy');
+CREATE TABLE t_a(c1 int, c2 float, c3 decimal, c4 char(20), c5 varchar(20), c6 nchar(20), c7 nvarchar(20));
+insert into t_a VALUES (1, 3.2, 2002.02, '2000-11-11', '2000-11-11', '2000-11-11', '2000-11-11');
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c1, 'YYYY') from t_a;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c2, 'YYYY') from t_a;
+--error ER_STD_INVALID_ARGUMENT
+SELECT TO_CHAR(c3, 'YYYY') from t_a;
+SELECT TO_CHAR(c4, 'YYYY') from t_a;
+SELECT TO_CHAR(c5, 'YYYY') from t_a;
+SELECT TO_CHAR(c6, 'YYYY') from t_a;
+SELECT TO_CHAR(c7, 'YYYY') from t_a;
+DROP TABLE t_a;
+
+CREATE TABLE t_b(c0 int, c1 char(20), c2 varchar(20), c3 nchar(20), c4 nvarchar(20));
+INSERT INTO t_b VALUES (1111, 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD');
+SELECT TO_CHAR('2000-11-11', c0) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c1) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c2) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c3) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c4) FROM t_b;
+DROP TABLE t_b;
+
+EXPLAIN EXTENDED SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char1;
+
+--echo #
+--echo # test for time type with date format string
+--echo #
+SELECT TO_CHAR(c2, 'YYYY-MM-DD HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'YYYY-MON-DY HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'MON-YYYY-DY HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'YYYY-MONTH-DAY HH:MI:SS') from t_to_char1;
+
+DROP TABLE t_to_char1;
+DROP TABLE t_to_char2;
+
+
+--echo #
+--echo # Test strict mode
+--echo #
+
+create table t1 (a datetime, b int, f varchar(30)) engine=myisam;
+insert into t1 values ("2021-01-24 19:22:10", 2014, "YYYY-MM-DD");
+insert into t1 values ("2021-01-24 19:22:10", 2014, "YYYY-MQ-DD");
+create table t2 (a varchar(30)) engine=myisam;
+insert into t2 select to_char(a,f) from t1;
+set @@sql_mode="STRICT_ALL_TABLES";
+--error ER_STD_INVALID_ARGUMENT
+insert into t2 select to_char(a,f) from t1;
+select * from t2;
+drop table t1,t2;
+set @local.sql_mode=@sql_mode;
+
+--echo #
+--echo # MDEV-29152: Assertion failed ... upon TO_CHAR with wrong argument
+--echo #
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
+SELECT TO_CHAR((VALUES('2022-12-12','2020-10-10')));
+
+--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
+SELECT TO_CHAR((STR_TO_DATE('2023-01-01', '%d-%m-%Y'), 'YYYY-MM-DD') );
+
diff --git a/mysql-test/suite/compat/oracle/t/func_trim.test b/mysql-test/suite/compat/oracle/t/func_trim.test
new file mode 100644
index 00000000..153238fb
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_trim.test
@@ -0,0 +1,77 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-15664 sql_mode=ORACLE: Make TRIM return NULL instead of empty string
+--echo #
+
+SELECT TRIM('abc'), TRIM('abc ')||'.', '.'||TRIM(' abc ')||'.', TRIM(' '), TRIM(NULL), TRIM(SPACE(0)),TRIM(SPACE(10)) FROM dual;
+
+SELECT TRIM(TRAILING 'abc' FROM 'abc');
+SELECT TRIM(TRAILING 'abc' FROM 'abc ');
+SELECT TRIM(TRAILING 'abc' FROM ' abc');
+
+SELECT TRIM(LEADING 'abc' FROM 'abc');
+SELECT TRIM(LEADING 'abc' FROM 'abc ');
+SELECT TRIM(LEADING 'abc' FROM ' abc');
+
+SELECT TRIM(BOTH 'abc' FROM 'abc');
+SELECT TRIM(BOTH 'abc' FROM 'abc ');
+SELECT TRIM(BOTH 'abc' FROM ' abc');
+
+SELECT RTRIM('abc'), RTRIM('abc ')||'.', RTRIM(' abc ')||'.', RTRIM(' '), RTRIM(NULL), RTRIM(SPACE(0)),RTRIM(SPACE(10)) FROM dual;
+SELECT LTRIM('abc'), LTRIM('abc '), LTRIM(' abc '), LTRIM(' '), LTRIM(NULL), LTRIM(SPACE(0)),LTRIM(SPACE(10)) FROM dual;
+
+CREATE TABLE t1 (c1 VARCHAR(10),ord INTEGER);
+INSERT INTO t1 VALUES ('abc',1);
+INSERT INTO t1 VALUES (SPACE(0),2);
+INSERT INTO t1 VALUES ('',3);
+INSERT INTO t1 VALUES (' ',4);
+INSERT INTO t1 VALUES (' ',5);
+INSERT INTO t1 VALUES (' a ',6);
+INSERT INTO t1 VALUES ('aa',7);
+INSERT INTO t1 VALUES ('aabb',8);
+INSERT INTO t1 VALUES ('bbaa',9);
+INSERT INTO t1 VALUES ('aabbaa',10);
+
+SELECT ord,'['||c1||']','.'||COALESCE(TRIM(LEADING 'a' FROM c1),'NULL')||'.' FROM t1 ORDER BY ord;
+SELECT ord,'['||c1||']','.'||COALESCE(TRIM(TRAILING 'a' FROM c1),'NULL')||'.' FROM t1 ORDER BY ord;
+SELECT ord,'['||c1||']','.'||COALESCE(TRIM(BOTH 'a' FROM c1),'NULL')||'.' FROM t1 ORDER BY ord;
+SELECT ord,'['||c1||']',COALESCE(LTRIM(c1),'NULL') FROM t1 ORDER BY ord;
+SELECT ord,'['||c1||']',COALESCE(RTRIM(c1),'NULL')||'.' FROM t1 ORDER BY ord;
+
+EXPLAIN EXTENDED SELECT TRIM('abc'),
+ TRIM(BOTH 'a' FROM 'abc'),
+ TRIM(LEADING 'a' FROM 'abc'),
+ TRIM(TRAILING 'a' FROM 'abc') ;
+
+EXPLAIN EXTENDED SELECT RTRIM('abc'),
+ LTRIM('abc');
+
+
+CREATE VIEW v1 AS SELECT ord,TRIM('abc'),RTRIM('abc'),LTRIM('abc'),
+ '['||c1||']',
+ TRIM(LEADING 'a' FROM c1),
+ TRIM(TRAILING 'a' FROM c1),
+ TRIM(BOTH 'a' FROM c1),
+ LTRIM(c1),
+ RTRIM(c1)
+ FROM t1 ORDER BY ord ;
+SHOW CREATE VIEW v1;
+SELECT * FROM v1;
+DROP VIEW v1;
+
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 VARCHAR(10) NOT NULL);
+CREATE TABLE t2 AS SELECT TRIM(LEADING 'a' FROM c1) AS C1,
+ TRIM(TRAILING 'a' FROM c1) AS C2,
+ TRIM(BOTH 'a' FROM c1) AS C3,
+ LTRIM(c1) AS C4,
+ RTRIM(c1) AS C5
+ FROM t1;
+SHOW CREATE TABLE t2;
+DROP TABLE t2;
+DROP TABLE t1;
+
+CREATE TABLE trim_oracle (trim_oracle int);
+DROP TABLE trim_oracle;
diff --git a/mysql-test/suite/compat/oracle/t/gis-debug.test b/mysql-test/suite/compat/oracle/t/gis-debug.test
new file mode 100644
index 00000000..6053e546
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/gis-debug.test
@@ -0,0 +1,31 @@
+--source include/have_geometry.inc
+--source include/have_debug.inc
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+--echo #
+--echo # MDEV-19994 Add class Function_collection
+--echo #
+
+SET SESSION debug_dbug="+d,make_item_func_call_native_simulate_not_found";
+--error ER_PARSE_ERROR
+SELECT CONTAINS(POINT(1,1),POINT(1,1));
+--error ER_PARSE_ERROR
+SELECT WITHIN(POINT(1,1),POINT(1,1));
+SET SESSION debug_dbug="-d,make_item_func_call_native_simulate_not_found";
+
+--echo #
+--echo # MDEV-20009 Add CAST(expr AS pluggable_type)
+--echo #
+
+SET SESSION debug_dbug="+d,emulate_geometry_create_typecast_item";
+SELECT AsText(CAST('POINT(0 0)' AS GEOMETRY));
+SET SESSION debug_dbug="-d,emulate_geometry_create_typecast_item";
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/gis.test b/mysql-test/suite/compat/oracle/t/gis.test
new file mode 100644
index 00000000..cb1b8690
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/gis.test
@@ -0,0 +1,76 @@
+-- source include/have_geometry.inc
+
+SET sql_mode=ORACLE;
+
+
+SELECT CONTAINS(POINT(1,1), POINT(1,1));
+SELECT CONTAINS(POINT(1,1), POINT(0,0));
+
+SELECT WITHIN(POINT(1,1), POINT(1,1));
+SELECT WITHIN(POINT(1,1), POINT(0,0));
+
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+--echo #
+--echo # MDEV-19994 Add class Function_collection
+--echo #
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT CONTAINS();
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT CONTAINS(POINT(1,1));
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT CONTAINS(POINT(1,1), POINT(1,1), POINT(1,1));
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT WITHIN();
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT WITHIN(POINT(1,1));
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT WITHIN(POINT(1,1), POINT(1,1), POINT(1,1));
+
+--echo #
+--echo # MDEV-20009 Add CAST(expr AS pluggable_type)
+--echo #
+
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS GEOMETRY);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS GEOMETRYCOLLECTION);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS POINT);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS LINESTRING);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS POLYGON);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS MULTIPOINT);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS MULTILINESTRING);
+--error ER_UNKNOWN_OPERATOR
+SELECT CAST(1 AS MULTIPOLYGON);
+
+
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, GEOMETRY);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, GEOMETRYCOLLECTION);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, POINT);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, LINESTRING);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, POLYGON);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, MULTIPOINT);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, MULTILINESTRING);
+--error ER_UNKNOWN_OPERATOR
+SELECT CONVERT(1, MULTIPOLYGON);
+
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/information_schema_parameters.test b/mysql-test/suite/compat/oracle/t/information_schema_parameters.test
new file mode 100644
index 00000000..c13a5910
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/information_schema_parameters.test
@@ -0,0 +1,127 @@
+
+--echo #
+--echo # MDEV-15416 Crash when reading I_S.PARAMETERS
+--echo #
+
+--echo # Create in sql_mode=ORACLE, display in sql_mode=ORACLE and sql_mode=DEFAULT
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PROCEDURE p1(a0 t1.a%TYPE,
+ a1 test.t1.a%TYPE,
+ b0 t1%ROWTYPE,
+ b1 test.t1%ROWTYPE,
+ d ROW(a INT,b DOUBLE))
+AS
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+--vertical_results
+SET sql_mode=ORACLE;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='p1';
+SET sql_mode=DEFAULT;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='p1';
+--horizontal_results
+DROP PROCEDURE p1;
+
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE FUNCTION f1(a0 t1.a%TYPE,
+ a1 test.t1.a%TYPE,
+ b0 t1%ROWTYPE,
+ b1 test.t1%ROWTYPE,
+ d ROW(a INT,b DOUBLE))
+ RETURN INT
+AS
+BEGIN
+ RETURN 0;
+END;
+$$
+DELIMITER ;$$
+--vertical_results
+SET sql_mode=ORACLE;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='f1';
+SET sql_mode=DEFAULT;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='f1';
+--horizontal_results
+DROP FUNCTION f1;
+
+
+--echo # Create in sql_mode=DEFAULT, display in sql_mode=DEFAULT and sql_mode=ORACLE
+
+SET sql_mode=DEFAULT;
+DELIMITER $$;
+CREATE PROCEDURE p1(a0 TYPE OF t1.a,
+ a1 TYPE OF test.t1.a,
+ b0 ROW TYPE OF t1,
+ b1 ROW TYPE OF test.t1,
+ d ROW(a INT,b DOUBLE))
+BEGIN
+END;
+$$
+DELIMITER ;$$
+--vertical_results
+SET sql_mode=DEFAULT;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='p1';
+SET sql_mode=ORACLE;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='p1';
+--horizontal_results
+DROP PROCEDURE p1;
+
+
+SET sql_mode=DEFAULT;
+DELIMITER $$;
+CREATE FUNCTION f1(a0 TYPE OF t1.a,
+ a1 TYPE OF test.t1.a,
+ b0 ROW TYPE OF t1,
+ b1 ROW TYPE OF test.t1,
+ d ROW(a INT,b DOUBLE))
+ RETURNS INT
+BEGIN
+ RETURN 0;
+END;
+$$
+DELIMITER ;$$
+--vertical_results
+SET sql_mode=DEFAULT;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='f1';
+SET sql_mode=ORACLE;
+SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='f1';
+--horizontal_results
+DROP FUNCTION f1;
+
+--echo #
+--echo # MDEV 18092 Query with the table I_S.PARAMETERS stop working
+--echo # after a package is created
+--echo #
+
+SET sql_mode=ORACLE;
+
+CREATE DATABASE db1_mdev18092;
+USE db1_mdev18092;
+
+DELIMITER $$;
+
+CREATE PROCEDURE p1(a INT)
+AS BEGIN
+ NULL;
+END;
+$$
+
+CREATE OR REPLACE PACKAGE employee_tools AS
+ FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2);
+ PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2));
+ PROCEDURE raiseSalaryStd(eid INT);
+ PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2));
+END;
+$$
+DELIMITER ;$$
+
+--vertical_results
+SELECT *, '---------------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA='db1_mdev18092';
+--horizontal_results
+
+DROP DATABASE db1_mdev18092;
diff --git a/mysql-test/suite/compat/oracle/t/keywords.test b/mysql-test/suite/compat/oracle/t/keywords.test
new file mode 100644
index 00000000..0768633b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/keywords.test
@@ -0,0 +1,29 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-17363 Compressed columns cannot be restored from dump
+--echo # In sql_mode=ORACLE, COMPRESSED is still valid both as an SP label
+--echo # and an SP variable name.
+--echo #
+
+DELIMITER $$;
+BEGIN
+ IF TRUE THEN
+ GOTO compressed;
+ END IF;
+ SELECT 'This should not be reached' AS warn;
+<<compressed>>
+ BEGIN
+ SELECT 1 AS a;
+ END;
+END
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+DECLARE compressed INT DEFAULT 1;
+BEGIN
+ SELECT compressed;
+END
+$$
+DELIMITER ;$$
diff --git a/mysql-test/suite/compat/oracle/t/minus.test b/mysql-test/suite/compat/oracle/t/minus.test
new file mode 100644
index 00000000..cbaf4227
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/minus.test
@@ -0,0 +1,44 @@
+CREATE TABLE tx1 (c1 int, c2 varchar(30));
+CREATE TABLE tx2 (c1 int, c2 varchar(30));
+CREATE TABLE tx3 (c1 int, c2 varchar(30));
+INSERT INTO tx1 VALUES (1, 'jim');
+INSERT INTO tx1 VALUES (2, 'menny');
+INSERT INTO tx1 VALUES (3, 'linda');
+
+INSERT INTO tx2 VALUES (1, 'jim');
+INSERT INTO tx2 VALUES (2, 'kris');
+INSERT INTO tx2 VALUES (3, 'shory');
+
+INSERT INTO tx3 VALUES (1, 'jim');
+INSERT INTO tx3 VALUES (2, 'kris');
+INSERT INTO tx3 VALUES (3, 'linda');
+
+--echo #
+--echo # test when sql_mode is not oracle
+--echo #
+
+SELECT c2 FROM tx1 EXCEPT SELECT c2 from tx2;
+--error 1064
+SELECT c2 FROM tx1 MINUS SELECT c2 from tx2;
+
+# MINUS should not be a reserved word
+create table MINUS (a int);
+drop table MINUS;
+
+--echo #
+--echo # test when sql_mode is oracle
+--echo #
+
+SET sql_mode=ORACLE;
+SELECT c2 FROM tx1 MINUS SELECT c2 from tx2;
+SELECT c2 FROM tx1 MINUS SELECT c2 from tx2 MINUS SELECT c2 from tx3;
+SELECT c2 FROM tx1 MINUS SELECT c2 from tx2 EXCEPT SELECT c2 from tx3;
+SELECT c2 FROM tx1 MINUS SELECT c2 from tx2 UNION SELECT c2 from tx3;
+
+# MINUS should be a reserved word
+--error ER_PARSE_ERROR
+create table MINUS (a int);
+
+DROP TABLE tx1;
+DROP TABLE tx2;
+DROP TABLE tx3; \ No newline at end of file
diff --git a/mysql-test/suite/compat/oracle/t/misc.test b/mysql-test/suite/compat/oracle/t/misc.test
new file mode 100644
index 00000000..b3fdfdf3
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/misc.test
@@ -0,0 +1,38 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Start of 10.2 tests
+--echo #
+
+--echo #
+--echo # MDEV-27690 Crash on `CHARACTER SET csname COLLATE DEFAULT` in column definition
+--echo #
+
+CREATE TABLE t1 (a CHAR(10) CHARACTER SET latin1 COLLATE DEFAULT);
+DROP TABLE t1;
+SELECT CAST('a' AS CHAR(10) CHARACTER SET latin1 COLLATE DEFAULT);
+SELECT COLUMN_GET(COLUMN_CREATE(0, 'string'),0 AS CHAR CHARACTER SET latin1 COLLATE DEFAULT) AS c1;
+
+--echo #
+--echo # End of 10.2 tests
+--echo #
+
+
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-12086 sql_mode=ORACLE: allow SELECT UNIQUE as a synonym for SELECT DISTINCT
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20),(20),(30),(30),(30);
+SELECT UNIQUE a FROM t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/mysqldump_restore.test b/mysql-test/suite/compat/oracle/t/mysqldump_restore.test
new file mode 100644
index 00000000..dd54c2b5
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/mysqldump_restore.test
@@ -0,0 +1,30 @@
+# See comments in mysql-test/main/mysqldump_restore.test
+--source include/not_embedded.inc
+
+SET sql_mode=ORACLE;
+
+let $mysqldumpfile = $MYSQLTEST_VARDIR/tmp/mysqldumpfile.sql;
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-17363 Compressed columns cannot be restored from dump
+--echo #
+
+CREATE TABLE t1 (a VARCHAR(1000) COMPRESSED CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL);
+INSERT INTO `t1` VALUES (REPEAT('a', 256));
+--exec $MYSQL_DUMP --skip-extended-insert test --skip-comments t1 > $mysqldumpfile
+let $table_name = test.t1;
+--source include/mysqldump.inc
+
+CREATE TABLE t1 (a LONGTEXT COMPRESSED CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL);
+INSERT INTO `t1` VALUES (REPEAT('a', 256));
+--exec $MYSQL_DUMP --skip-extended-insert test --skip-comments t1 > $mysqldumpfile
+let $table_name = test.t1;
+--source include/mysqldump.inc
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/parser.test b/mysql-test/suite/compat/oracle/t/parser.test
new file mode 100644
index 00000000..40b4e297
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/parser.test
@@ -0,0 +1,727 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger
+--echo #
+
+CREATE TABLE t1 (a INT);
+--error ER_UNKNOWN_STRUCTURED_VARIABLE
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET @@NEW.a=0;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-15615 Unexpected syntax error instead of "Unknown system variable" inside an SP
+--echo #
+
+DELIMITER $$;
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+DECLARE
+ a INT;
+BEGIN
+ SET GLOBAL a=10;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # MDEV-16202 Latest changes made erroneously some keywords reserved in sql_mode=ORACLE
+--echo #
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1(name VARCHAR(64), pattern TEXT) AS
+ query TEXT DEFAULT REPLACE(pattern, 'name', name);
+BEGIN
+ SELECT query AS '';
+ EXECUTE IMMEDIATE query;
+EXCEPTION
+ WHEN OTHERS THEN
+ BEGIN
+ SHOW ERRORS;
+ END;
+END;
+$$
+
+CREATE PROCEDURE p2(name VARCHAR(64)) AS
+BEGIN
+ CALL p1(name, 'DECLARE name INT; BEGIN name:=10; SELECT name; END');
+ EXECUTE IMMEDIATE REPLACE('CREATE TABLE t1 (name INT)', 'name', name);
+ CALL p1(name, 'SELECT name FROM t1');
+ CALL p1(name, 'SELECT name ''alias'' FROM t1');
+ CALL p1(name, 'SELECT name()');
+ CALL p1(name, 'SELECT name.name()');
+ CALL p1(name, 'SELECT name DATE FROM t1');
+ CALL p1(name, 'SELECT name HISTORY FROM t1');
+ CALL p1(name, 'SELECT name NEXT FROM t1');
+ CALL p1(name, 'SELECT name PERIOD FROM t1');
+ CALL p1(name, 'SELECT name PREVIOUS FROM t1');
+ CALL p1(name, 'SELECT name SYSTEM FROM t1');
+ CALL p1(name, 'SELECT name SYSTEM_TIME FROM t1');
+ CALL p1(name, 'SELECT name TIME FROM t1');
+ CALL p1(name, 'SELECT name TIMESTAMP FROM t1');
+ CALL p1(name, 'SELECT name TRANSACTION FROM t1');
+ CALL p1(name, 'SELECT name VALUE FROM t1');
+ CALL p1(name, 'SELECT name VERSIONING FROM t1');
+ CALL p1(name, 'SELECT name WITHOUT FROM t1');
+ DROP TABLE t1;
+END;
+$$
+DELIMITER ;$$
+
+--disable_column_names
+CALL p2('date');
+CALL p2('history');
+CALL p2('next');
+CALL p2('period');
+CALL p2('previous');
+CALL p2('system');
+CALL p2('system_time');
+CALL p2('time');
+CALL p2('timestamp');
+CALL p2('transaction');
+CALL p2('value');
+CALL p2('versioning');
+CALL p2('without');
+--enable_column_names
+
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-16244 sql_mode=ORACLE: Some keywords do not work in variable declarations
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER /;
+
+DECLARE
+ do INT;
+BEGIN
+ SELECT do INTO do FROM DUAL;
+END;
+/
+
+DECLARE
+ handler INT;
+BEGIN
+ SELECT handler INTO handler FROM DUAL;
+END;
+/
+
+DECLARE
+ repair INT;
+BEGIN
+ SELECT repair INTO repair FROM DUAL;
+END;
+/
+
+DECLARE
+ shutdown INT;
+BEGIN
+ SELECT shutdown INTO shutdown FROM DUAL;
+END;
+/
+
+DECLARE
+ truncate INT;
+BEGIN
+ SELECT truncate INTO truncate FROM DUAL;
+END;
+/
+
+DECLARE
+ close INT;
+BEGIN
+ SELECT close INTO close FROM DUAL;
+END;
+/
+
+DECLARE
+ commit INT;
+BEGIN
+ SELECT commit INTO commit FROM DUAL;
+END;
+/
+
+DECLARE
+ open INT;
+BEGIN
+ SELECT open INTO open FROM DUAL;
+END;
+/
+
+DECLARE
+ rollback INT;
+BEGIN
+ SELECT rollback INTO rollback FROM DUAL;
+END;
+/
+
+DECLARE
+ savepoint INT;
+BEGIN
+ SELECT savepoint INTO savepoint FROM DUAL;
+END;
+/
+
+DECLARE
+ contains INT;
+BEGIN
+ SELECT contains INTO contains FROM DUAL;
+END;
+/
+
+DECLARE
+ language INT;
+BEGIN
+ SELECT language INTO language FROM DUAL;
+END;
+/
+
+DECLARE
+ no INT;
+BEGIN
+ SELECT no INTO no FROM DUAL;
+END;
+/
+
+DECLARE
+ charset INT;
+BEGIN
+ SELECT charset INTO charset FROM DUAL;
+END;
+/
+DECLARE
+ follows INT;
+BEGIN
+ SELECT follows INTO follows FROM DUAL;
+END;
+/
+
+DECLARE
+ precedes INT;
+BEGIN
+ SELECT precedes INTO precedes FROM DUAL;
+END;
+/
+
+DELIMITER ;/
+
+
+--echo #
+--echo # MDEV-16464 Oracle Comp.: Sql-Error on "SELECT name, comment FROM mysql.proc"
+--echo #
+
+SET sql_mode=ORACLE;
+SELECT name, comment FROM mysql.proc WHERE db='test';
+
+CREATE TABLE comment (comment INT);
+SELECT comment FROM comment;
+SELECT comment comment FROM comment comment;
+SELECT comment AS comment FROM comment AS comment;
+DROP TABLE comment;
+
+DELIMITER /;
+DECLARE
+ comment INT;
+BEGIN
+ SELECT comment INTO comment FROM DUAL;
+END;
+/
+DELIMITER ;/
+
+DELIMITER /;
+CREATE PROCEDURE comment COMMENT 'test' AS
+BEGIN
+ SELECT 1;
+END;
+/
+BEGIN
+ comment;
+END;
+/
+DELIMITER ;/
+CALL comment();
+CALL comment;
+DROP PROCEDURE comment;
+
+enable_prepare_warnings;
+DELIMITER /;
+CREATE FUNCTION comment RETURN INT COMMENT 'test' AS
+BEGIN
+ RETURN 1;
+END;
+/
+DELIMITER ;/
+SELECT test.comment() FROM DUAL;
+disable_prepare_warnings;
+DROP FUNCTION comment;
+
+
+--echo #
+--echo # MDEV-17660 sql_mode=ORACLE: Some keywords do not work as label names: history, system, versioning, without
+--echo #
+
+DELIMITER /;
+BEGIN
+<<date_format>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+BEGIN
+<<decode>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+BEGIN
+<<history>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+BEGIN
+<<system>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+BEGIN
+<<versioning>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+BEGIN
+<<without>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+--echo #
+--echo # MDEV-17666 sql_mode=ORACLE: Keyword ELSEIF should not be reserved
+--echo #
+
+DELIMITER /;
+DECLARE
+ ELSEIF INT;
+BEGIN
+ ELSEIF:=1;
+END;
+/
+DELIMITER ;/
+
+DELIMITER /;
+BEGIN
+<<ELSEIF>>
+ NULL;
+END;
+/
+DELIMITER ;/
+
+
+--echo #
+--echo # MDEV-17693 Shift/reduce conflicts for NAMES,ROLE,PASSWORD in the option_value_no_option_type grammar
+--echo #
+
+CREATE TABLE names (names INT);
+SELECT names FROM names AS names;
+DROP TABLE names;
+
+CREATE TABLE password (password INT);
+SELECT password FROM password AS password;
+DROP TABLE password;
+
+CREATE TABLE role (role INT);
+SELECT role FROM role AS role;
+DROP TABLE role;
+
+DELIMITER $$;
+DECLARE
+ names VARCHAR(32) DEFAULT '[names]';
+ password VARCHAR(32) DEFAULT '[password]';
+ role VARCHAR(32) DEFAULT '[role]';
+BEGIN
+<<names>>
+ SELECT names;
+<<password>>
+ SELECT password;
+<<role>>
+ SELECT role;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_BAD_VAR_SHADOW
+DECLARE
+ names VARCHAR(32);
+BEGIN
+ SET names='[names]';
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_BAD_VAR_SHADOW
+DECLARE
+ password VARCHAR(32);
+BEGIN
+ SET password='[password]';
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+DECLARE
+ role VARCHAR(32);
+BEGIN
+ SET role='[role]';
+END;
+$$
+DELIMITER ;$$
+
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+SELECT @@GLOBAL.names;
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+SELECT @@GLOBAL.password;
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+SELECT @@GLOBAL.role;
+
+
+--echo #
+--echo # MDEV-22822 sql_mode="oracle" cannot declare without variable errors
+--echo #
+--echo # It's OK to have no declarations between DECLARE and BEGIN.
+--echo #
+
+DELIMITER //;
+BEGIN
+ DECLARE
+ BEGIN
+ NULL;
+ END;
+EXCEPTION
+WHEN OTHERS THEN
+ NULL;
+END;
+//
+DELIMITER ;//
+
+
+DELIMITER //;
+DECLARE
+BEGIN
+ NULL;
+EXCEPTION
+WHEN OTHERS THEN
+ NULL;
+END;
+//
+DELIMITER ;//
+
+
+DELIMITER //;
+BEGIN
+<<lab>>
+ DECLARE
+ BEGIN
+ NULL;
+ END;
+EXCEPTION
+WHEN OTHERS THEN
+ NULL;
+END;
+//
+DELIMITER ;//
+
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
+
+
+--echo #
+--echo # MDEV-21998: Server crashes in st_select_lex::add_table_to_list
+--echo # upon mix of KILL and sequences
+--echo #
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+KILL ( SELECT 1 ) + LASTVAL(s);
+--error ER_SUBQUERIES_NOT_SUPPORTED
+KILL LASTVAL(s);
+
+--echo #
+--echo # MDEV-23094: Multiple calls to a Stored Procedure from another
+--echo # Stored Procedure crashes server
+--echo #
+
+create table t1 (id1 int primary key, data1 int);
+create table t2 (id2 int primary key, data2 int);
+
+delimiter //;
+create procedure p1(id int,dt int) as
+begin
+ if (exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt))
+ then
+ select 1;
+ end if;
+end //
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+delimiter //;
+create procedure p1(id int, dt int) as
+begin
+case (exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt))
+when 1 then
+ select 1;
+else
+ select 0;
+end case;
+end //
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+delimiter //;
+create procedure p1(id int, dt int) as
+begin
+declare wcont int default 1;
+begin
+ while (exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt)) and wcont
+ loop
+ select 1;
+ set wcont=0;
+ end loop;
+end;
+end //
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+delimiter //;
+create procedure p1(id int, dt int) as
+begin
+declare count int default 1;
+begin
+ repeat
+ select 1;
+ set count=count+1;
+ until (exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt)) and
+ count < 3
+ end repeat;
+end;
+end //
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+delimiter //;
+create procedure p1(id int, dt int) as
+begin
+for i in 1..(exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt))
+loop
+ select 1;
+end loop;
+end //
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+set sql_mode=ORACLE;
+delimiter //;
+create or replace procedure p1(id int, dt int) as
+begin
+ while (1)
+ loop
+ exit when (exists(select * from t1 where id1 = id and data1 = dt) or
+ not exists (select * from t2 where id2 = id and data2 = dt));
+ end loop;
+end;
+//
+delimiter ;//
+
+call p1(1,2);
+call p1(1,2);
+
+drop procedure p1;
+
+drop table t1,t2;
+
+--echo # End of 10.4 tests
+
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+--echo #
+--echo # MDEV-20734 Allow reserved keywords as user defined type names
+--echo #
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a DUAL);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS DUAL);
+
+
+--echo #
+--echo # MDEV-20735 Allow non-reserved keywords as user defined type names
+--echo #
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a ASCII);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS ASCII);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a LANGUAGE);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS LANGUAGE);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a CLOSE);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS CLOSE);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a NAMES);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS NAMES);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a END);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS END);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a GLOBAL);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS GLOBAL);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a ACTION);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS ACTION);
+
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a BEGIN);
+--error ER_UNKNOWN_DATA_TYPE
+SELECT CAST(1 AS BEGIN);
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
+
+
+--echo #
+--echo # Start of 10.6 tests
+--echo #
+
+--echo #
+--echo # MDEV-19682 sql_mode="oracle" does not support sysdate
+--echo #
+
+# SYSDATE is not deterministic. Let's use LIKE and equality.
+# The main point here is only to check that SYSDATE
+# gets parsed without parentheses. The actial value is not important.
+SELECT sysdate LIKE '____-__-__ __:__:__';
+SELECT sysdate = sysdate();
+SELECT sysdate = sysdate(0);
+
+--error ER_PARSE_ERROR
+CREATE DATABASE sysdate;
+
+--error ER_PARSE_ERROR
+CREATE TABLE sysdate (a INT);
+
+--error ER_PARSE_ERROR
+CREATE TABLE t1 (sysdate INT);
+
+--error ER_PARSE_ERROR
+CREATE TABLE t1 (a sysdate);
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE FUNCTION sysdate RETURN INT AS
+BEGIN
+ RETURN 1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE FUNCTION sysdate() RETURN INT AS
+BEGIN
+ RETURN 1;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+DECLARE
+ sysdate INT := 10;
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+BEGIN
+<<sysdate>>
+ NULL;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # End of 10.6 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/plugin.test b/mysql-test/suite/compat/oracle/t/plugin.test
new file mode 100644
index 00000000..a84c4ae7
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/plugin.test
@@ -0,0 +1,3 @@
+SET sql_mode=ORACLE;
+
+--source include/install_plugin_if_exists.inc
diff --git a/mysql-test/suite/compat/oracle/t/ps.test b/mysql-test/suite/compat/oracle/t/ps.test
new file mode 100644
index 00000000..39770b48
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/ps.test
@@ -0,0 +1,290 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10801 sql_mode: dynamic SQL placeholders
+--echo #
+
+SET @a=10, @b=20;
+PREPARE stmt FROM 'SELECT ?,?';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :a,:b';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :aaa,:bbb';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :"a",:"b"';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :"aaa",:"bbb"';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :1,:2';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :222,:111';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :0,:65535';
+EXECUTE stmt USING @a, @b;
+PREPARE stmt FROM 'SELECT :65535,:0';
+EXECUTE stmt USING @a, @b;
+
+--echo #
+--echo # MDEV-10709 Expressions as parameters to Dynamic SQL
+--echo #
+
+--echo #
+--echo # Testing disallowed expressions in USING
+--echo #
+
+PREPARE stmt FROM 'SELECT :1 FROM DUAL';
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE stmt USING (SELECT 1);
+DEALLOCATE PREPARE stmt;
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+BEGIN
+ RETURN 'test';
+END;
+$$
+DELIMITER ;$$
+PREPARE stmt FROM 'SELECT ? FROM DUAL';
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE stmt USING f1();
+DEALLOCATE PREPARE stmt;
+DROP FUNCTION f1;
+
+--echo #
+--echo # Using a user variable as a EXECUTE..USING out parameter
+--echo #
+
+DELIMITER /;
+CREATE PROCEDURE p1(a OUT INT)
+AS
+BEGIN
+ a:= 10;
+END;
+/
+DELIMITER ;/
+SET @a=1;
+CALL p1(@a);
+SELECT @a;
+SET @a=2;
+PREPARE stmt FROM 'CALL p1(?)';
+EXECUTE stmt USING @a;
+SELECT @a;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Using an SP variable as a EXECUTE..USING out parameter
+--echo #
+
+DELIMITER /;
+CREATE PROCEDURE p1 (a OUT INT)
+AS
+BEGIN
+ a:=10;
+END;
+/
+CREATE PROCEDURE p2 (a OUT INT)
+AS
+BEGIN
+ PREPARE stmt FROM 'CALL p1(?)';
+ EXECUTE stmt USING a;
+END;
+/
+DELIMITER ;/
+SET @a= 1;
+CALL p2(@a);
+SELECT @a;
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Using a trigger field as a EXECUTE..USING out parameter
+--echo #
+DELIMITER /;
+CREATE PROCEDURE p1 (a OUT INT)
+AS
+BEGIN
+ a:= 10;
+END;
+/
+DELIMITER ;/
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW CALL p1(:NEW.a);
+INSERT INTO t1 VALUES (1);
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Testing re-prepare on a table metadata update between PREPARE and EXECUTE
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER /;
+CREATE PROCEDURE p1(a IN INT)
+AS
+BEGIN
+ INSERT INTO t1 VALUES (a);
+END;
+/
+DELIMITER ;/
+PREPARE stmt FROM 'CALL p1(?)';
+EXECUTE stmt USING 10;
+SELECT * FROM t1;
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:=NEW.a+1;
+EXECUTE stmt USING 20;
+SELECT * FROM t1;
+DEALLOCATE PREPARE stmt;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # End of MDEV-10709 Expressions as parameters to Dynamic SQL
+--echo #
+
+--echo #
+--echo # MDEV-10585 EXECUTE IMMEDIATE statement
+--echo #
+
+--echo #
+--echo # Testing disallowed expressions in USING
+--echo #
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE 'SELECT :1 FROM DUAL' USING (SELECT 1);
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+BEGIN
+ RETURN 'test';
+END;
+$$
+DELIMITER ;$$
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE 'SELECT ? FROM DUAL' USING f1();
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Testing simple expressions
+--echo #
+
+EXECUTE IMMEDIATE 'SELECT :1 FROM DUAL' USING 10;
+
+
+--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 _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
+--error ER_CANT_AGGREGATE_2COLLATIONS
+PREPARE stmt FROM _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE (SELECT 'SELECT 1');
+--error ER_SUBQUERIES_NOT_SUPPORTED
+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 COALESCE(NULL);
+--error ER_PARSE_ERROR
+PREPARE stmt FROM COALESCE(NULL);
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+BEGIN
+ RETURN 't1';
+END;
+$$
+DELIMITER ;$$
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE f1();
+--error ER_SUBQUERIES_NOT_SUPPORTED
+PREPARE stmt FROM f1();
+DROP FUNCTION f1;
+
+--echo #
+--echo # Testing user variables in prepare source
+--echo #
+
+SET @table_name='DUAL';
+EXECUTE IMMEDIATE 'SELECT 1 AS a FROM ' || @table_name;
+PREPARE stmt FROM '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)
+AS
+BEGIN
+ EXECUTE IMMEDIATE 'SELECT 1 AS c FROM '|| table_name;
+END;
+$$
+DELIMITER ;$$
+CALL p1('DUAL');
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ table_name VARCHAR(64):='DUAL';
+BEGIN
+ EXECUTE IMMEDIATE 'SELECT 1 AS c FROM ' || table_name;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # End of MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
+--echo #
+
+
+--echo #
+--echo # MDEV-12846 sql_mode=ORACLE: using Oracle-style placeholders in direct query execution makes the server crash
+--echo #
+
+# When running with --ps, the below queries return
+# CR_PARAMS_NOT_BOUND instead of ER_PARSE_ERROR
+
+--disable_ps_protocol
+--error ER_PARSE_ERROR
+SELECT ? FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT :a FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT :1 FROM DUAL;
+
+--error ER_PARSE_ERROR
+SELECT 1+? FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT 1+:a FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT 1+:1 FROM DUAL;
+--enable_ps_protocol
diff --git a/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test b/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test
new file mode 100644
index 00000000..b2aff233
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test
@@ -0,0 +1,38 @@
+--source include/have_binlog_format_row.inc
+--source include/master-slave.inc
+
+SET SQL_MODE=DEFAULT;
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES (NULL);
+INSERT INTO t1 VALUES ('2001-01-01');
+
+SET SQL_MODE= ORACLE;
+CREATE TABLE t2 SELECT * FROM t1;
+
+--let $binlog_file = LAST
+source include/show_binlog_events.inc;
+
+SET SQL_MODE= DEFAULT;
+SHOW CREATE TABLE t1;
+SHOW CREATE TABLE t2;
+
+SET SQL_MODE= ORACLE;
+SHOW CREATE TABLE t1;
+SHOW CREATE TABLE t2;
+
+--sync_slave_with_master
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+SET SQL_MODE= DEFAULT;
+SHOW CREATE TABLE t1;
+SHOW CREATE TABLE t2;
+
+SET SQL_MODE= ORACLE;
+SHOW CREATE TABLE t1;
+SHOW CREATE TABLE t2;
+
+# Cleanup
+--connection master
+DROP TABLE t1, t2;
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/compat/oracle/t/rpl_sp_package.test b/mysql-test/suite/compat/oracle/t/rpl_sp_package.test
new file mode 100644
index 00000000..40bb0b0d
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/rpl_sp_package.test
@@ -0,0 +1,134 @@
+--source include/master-slave.inc
+
+connection master;
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PACKAGE pack AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY pack AS
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT f1();
+ END;
+END pack;
+$$
+DELIMITER ;$$
+
+sync_slave_with_master;
+connection slave;
+--vertical_results
+--replace_column 13 # 14 #
+SELECT * FROM mysql.proc WHERE db='test' AND name='pack';
+--replace_column 13 # 14 #
+SELECT * FROM mysql.proc WHERE db='test' AND name LIKE 'pack.%';
+--horizontal_results
+
+SET @@sql_mode=ORACLE;
+SELECT pack.f1();
+CALL pack.p1();
+SET @@sql_mode=DEFAULT;
+
+connection master;
+DROP PACKAGE pack;
+
+sync_slave_with_master;
+connection slave;
+SELECT COUNT(*) FROM mysql.proc WHERE db='test' AND name='pack';
+
+--echo #
+--echo # Creating a package with a COMMENT
+--echo #
+
+connection master;
+
+DELIMITER $$;
+CREATE PACKAGE p1 COMMENT 'package-p1-comment' AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 COMMENT 'package-body-p1-comment' AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT definer, name, security_type, type, `comment` FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+sync_slave_with_master;
+SELECT definer, name, security_type, type, `comment` FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+
+connection master;
+DROP PACKAGE p1;
+sync_slave_with_master;
+
+
+--echo #
+--echo # Creating a package with a different DEFINER
+--echo #
+
+connection master;
+
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+sync_slave_with_master;
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+
+connection master;
+DROP PACKAGE p1;
+sync_slave_with_master;
+
+--echo #
+--echo # Creating a package with a different DEFINER + SQL SECURITY INVOKER
+--echo #
+
+connection master;
+
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+sync_slave_with_master;
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+
+connection master;
+DROP PACKAGE p1;
+sync_slave_with_master;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/compat/oracle/t/rpl_sp_package_variables.test b/mysql-test/suite/compat/oracle/t/rpl_sp_package_variables.test
new file mode 100644
index 00000000..fca61124
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/rpl_sp_package_variables.test
@@ -0,0 +1,36 @@
+--source include/master-slave.inc
+
+connection master;
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-13139 Package-wide variables in CREATE PACKAGE
+--echo #
+connection master;
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ va INT:=10;
+ PROCEDURE p1 AS
+ BEGIN
+ INSERT INTO t1 VALUES (va);
+ END;
+BEGIN
+ CREATE OR REPLACE TABLE t1 (a INT);
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+sync_slave_with_master;
+SELECT * FROM t1;
+connection master;
+DROP PACKAGE p1;
+DROP TABLE t1;
+sync_slave_with_master;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/compat/oracle/t/sequence.test b/mysql-test/suite/compat/oracle/t/sequence.test
new file mode 100644
index 00000000..9bd8cec5
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sequence.test
@@ -0,0 +1,48 @@
+--source include/have_binlog_format_row.inc
+
+SET sql_mode=ORACLE;
+
+--disable_ps2_protocol
+CREATE SEQUENCE s1;
+SHOW CREATE SEQUENCE s1;
+SELECT s1.currval;
+SELECT s1.nextval;
+SELECT s1.nextval;
+SELECT s1.nextval;
+EXPLAIN EXTENDED SELECT s1.nextval;
+SELECT nextval(s1);
+EXPLAIN EXTENDED SELECT s1.currval;
+SELECT lastval(s1);
+DROP SEQUENCE s1;
+--enable_ps2_protocol
+
+CREATE SEQUENCE s1;
+CREATE VIEW v1 AS SELECT s1.nextval AS a;
+SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1';
+--disable_ps2_protocol
+SELECT * FROM v1;
+--enable_ps2_protocol
+SHOW CREATE VIEW v1;
+DROP VIEW v1;
+DROP SEQUENCE s1;
+
+
+CREATE SEQUENCE s1;
+CREATE VIEW v1 AS SELECT s1.currval AS a;
+SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1';
+SELECT * FROM v1;
+SHOW CREATE VIEW v1;
+DROP VIEW v1;
+DROP SEQUENCE s1;
+
+--echo #
+--echo # MDEV-12533 sql_mode=ORACLE: Add support for database qualified sequence names in NEXTVAL and CURRVAL
+--echo #
+--disable_ps2_protocol
+CREATE SEQUENCE s1;
+SELECT test.s1.nextval;
+SELECT test.s1.currval;
+SELECT .s1.nextval;
+SELECT .s1.currval;
+DROP SEQUENCE s1;
+--enable_ps2_protocol
diff --git a/mysql-test/suite/compat/oracle/t/sp-anchor-row-type-table.test b/mysql-test/suite/compat/oracle/t/sp-anchor-row-type-table.test
new file mode 100644
index 00000000..6e13a616
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-anchor-row-type-table.test
@@ -0,0 +1,124 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-13581 ROW TYPE OF t1 and t1%ROWTYPE for routine parameters
+--echo #
+
+CREATE TABLE t1 (a INT, b TEXT, c ENUM('a','b','c'));
+DELIMITER $$;
+CREATE PROCEDURE p1 (a t1%ROWTYPE) AS
+BEGIN
+ CREATE TABLE t2 AS SELECT a.a AS a, a.b AS b, a.c AS c;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+CREATE PROCEDURE p2 AS
+ a t1%ROWTYPE;
+BEGIN
+ CALL p1(a);
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT, b TEXT);
+DELIMITER $$;
+CREATE PROCEDURE p1 (a OUT t1%ROWTYPE) AS
+BEGIN
+ SET a.a=10;
+ SET a.b='text';
+END;
+$$
+CREATE PROCEDURE p2 AS
+ a t1%ROWTYPE;
+BEGIN
+ CALL p1(a);
+ SELECT a.a, a.b;
+END;
+$$
+CREATE FUNCTION f1(a t1%ROWTYPE) RETURN TEXT AS
+BEGIN
+ RETURN CONCAT(a.a, ' ', a.b);
+END;
+$$
+CREATE FUNCTION f2 RETURN TEXT AS
+ a t1%ROWTYPE;
+BEGIN
+ CALL p1(a);
+ RETURN f1(a);
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+SELECT f2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP FUNCTION f2;
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+CREATE DATABASE db1;
+CREATE TABLE db1.t1 (a INT, b TEXT);
+DELIMITER $$;
+CREATE PROCEDURE p1 (a OUT db1.t1%ROWTYPE) AS
+BEGIN
+ SET a.a=10;
+ SET a.b='text';
+END;
+$$
+CREATE PROCEDURE p2 AS
+ a db1.t1%ROWTYPE;
+BEGIN
+ CALL p1(a);
+ SELECT a.a, a.b;
+END;
+$$
+CREATE FUNCTION f1(a db1.t1%ROWTYPE) RETURN TEXT AS
+BEGIN
+ RETURN CONCAT(a.a, ' ', a.b);
+END;
+$$
+CREATE FUNCTION f2() RETURN TEXT AS
+ a db1.t1%ROWTYPE;
+BEGIN
+ CALL p1(a);
+ RETURN f1(a);
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+SELECT f2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP FUNCTION f2;
+DROP FUNCTION f1;
+DROP DATABASE db1;
+
+
+--echo #
+--echo # MDEV-14139 Anchored data types for variables
+--echo #
+
+CREATE TABLE t1 (int11 INT, text0 TEXT);
+DELIMITER $$;
+DECLARE
+ row1 t1%ROWTYPE;
+ a_row1 row1%TYPE;
+ aa_row1 a_row1%TYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT a_row1.int11 AS int11, a_row1.text0 AS text0;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ CREATE TABLE t2 AS SELECT aa_row1.int11 AS int11, aa_row1.text0 AS text0;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-anonymous.test b/mysql-test/suite/compat/oracle/t/sp-anonymous.test
new file mode 100644
index 00000000..ac61e8ac
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-anonymous.test
@@ -0,0 +1,244 @@
+--source include/have_innodb.inc
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10655 Anonymous blocks
+--echo #
+
+--echo # Testing BEGIN NOT ATOMIC with no declarations
+DELIMITER /;
+BEGIN NOT ATOMIC
+ SELECT 1 AS a;
+END
+/
+DELIMITER ;/
+
+--echo # Testing BEGIN NOT ATOMIC with declarations
+--echo # DECLARE starts a new block and thus must be followed by BEGIN .. END
+DELIMITER /;
+BEGIN NOT ATOMIC
+ DECLARE
+ i INT DEFAULT 5;
+ x INT DEFAULT 10;
+ BEGIN
+ <<label>>
+ WHILE i > 3 LOOP
+ i:= i - 1;
+ SELECT i;
+ END LOOP label;
+ END;
+END
+/
+DELIMITER ;/
+
+
+--echo # Anonymous blocks with no declarations and no exceptions
+
+DELIMITER $$;
+BEGIN
+ SELECT 1 AS a;
+END
+$$
+DELIMITER ;$$
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(30);
+ ROLLBACK;
+END;
+$$
+DELIMITER ;$$
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(30);
+END;
+$$
+DELIMITER ;$$
+ROLLBACK;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(30);
+ COMMIT;
+END;
+$$
+DELIMITER ;$$
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(30);
+END;
+$$
+DELIMITER ;$$
+COMMIT;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+--error ER_DUP_ENTRY
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(20);
+END;
+$$
+DELIMITER ;$$
+COMMIT;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+--echo # Anonymous blocks with no declarations, with exceptions
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+BEGIN
+ INSERT INTO t1 VALUES(20);
+ INSERT INTO t1 VALUES(20);
+EXCEPTION
+ WHEN DUP_VAL_ON_INDEX THEN NULL;
+END;
+$$
+DELIMITER ;$$
+COMMIT;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+--echo # Anonymous blocks with declarations, with no exceptions
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a20 INT:=20;
+ a30 INT:=30;
+BEGIN
+ INSERT INTO t1 VALUES(a20);
+ INSERT INTO t1 VALUES(a30);
+ ROLLBACK;
+END;
+$$
+DELIMITER ;$$
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a20 INT:=20;
+ a30 INT:=30;
+BEGIN
+ INSERT INTO t1 VALUES(a20);
+ INSERT INTO t1 VALUES(a30);
+END;
+$$
+DELIMITER ;$$
+ROLLBACK;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a20 INT:=20;
+ a30 INT:=30;
+BEGIN
+ INSERT INTO t1 VALUES(a20);
+ INSERT INTO t1 VALUES(a30);
+ COMMIT;
+END;
+$$
+DELIMITER ;$$
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a20 INT:=20;
+ a30 INT:=30;
+BEGIN
+ INSERT INTO t1 VALUES(a20);
+ INSERT INTO t1 VALUES(a30);
+END;
+$$
+DELIMITER ;$$
+COMMIT;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
+
+
+--echo # Anonymous blocks with declarations, with exceptions
+
+SET AUTOCOMMIT=OFF;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a20 INT:=20;
+BEGIN
+ INSERT INTO t1 VALUES(a20);
+ INSERT INTO t1 VALUES(a20);
+EXCEPTION
+ WHEN DUP_VAL_ON_INDEX THEN NULL;
+END;
+$$
+DELIMITER ;$$
+COMMIT;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET AUTOCOMMIT=DEFAULT;
diff --git a/mysql-test/suite/compat/oracle/t/sp-cache-invalidate.inc b/mysql-test/suite/compat/oracle/t/sp-cache-invalidate.inc
new file mode 100644
index 00000000..945af6e7
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cache-invalidate.inc
@@ -0,0 +1,11 @@
+--echo # sp-cache-invalidate
+--disable_query_log
+DELIMITER $$;
+CREATE FUNCTION dummy RETURN INT AS
+BEGIN
+ RETURN 1;
+END;
+$$
+DELIMITER ;$$
+DROP FUNCTION dummy;
+--enable_query_log
diff --git a/mysql-test/suite/compat/oracle/t/sp-code.test b/mysql-test/suite/compat/oracle/t/sp-code.test
new file mode 100644
index 00000000..1ffd3b94
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-code.test
@@ -0,0 +1,1088 @@
+-- source include/have_debug.inc
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Testing exceptions in the top-level blocks
+--echo #
+
+--echo # No HANDLER declarations, no exceptions
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+AS
+BEGIN
+ RETURN 10;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1();
+DROP FUNCTION f1;
+
+--echo # No HANDLER declarations, no code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 ()
+IS
+BEGIN
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo # No HANDLER declarations, no code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+EXCEPTION
+ WHEN 1002 THEN v:=225;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # No HANDLER declarations, some code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=224;
+EXCEPTION
+ WHEN 1002 THEN v:=225;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Some HANDLER declarations, no code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=123;
+ END;
+BEGIN
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Some HANDLER declarations, no code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=123;
+ END;
+BEGIN
+EXCEPTION
+ WHEN 1002 THEN v:=225;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Some HANDLER declarations, some code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=123;
+ END;
+BEGIN
+ v:=223;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # Some HANDLER declarations, some code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT VARCHAR2(20))
+IS
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=123;
+ END;
+ CONTINUE HANDLER FOR 1001
+ BEGIN
+ SET v=223;
+ END;
+BEGIN
+ v:= 1;
+EXCEPTION
+ WHEN 1002 THEN SET v=225;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Testing EXCEPTIONS in internal blocks
+--echo #
+
+--echo # No HANDLER declarations, no code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ BEGIN
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # No HANDLER declarations, no code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ BEGIN
+ EXCEPTION
+ WHEN 20002 THEN v:=335;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # No HANDLER declarations, some code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ BEGIN
+ v:=223;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # No HANDLER declarations, some code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ BEGIN
+ v:=223;
+ EXCEPTION
+ WHEN 20002 THEN v:=335;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # Some HANDLER declarations, no code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ DECLARE
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=323;
+ END;
+ BEGIN
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # Some HANDLER declarations, no code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ DECLARE
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=323;
+ END;
+ BEGIN
+ EXCEPTION
+ WHEN 20002 THEN v:=335;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # Some HANDLER declarations, some code, no exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ DECLARE
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=323;
+ END;
+ BEGIN
+ v:= 324;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo # Some HANDLER declarations, some code, some exceptions
+DELIMITER /;
+CREATE PROCEDURE p1 (v IN OUT INT)
+IS
+BEGIN
+ v:=123;
+ DECLARE
+ EXIT HANDLER FOR 1000
+ BEGIN
+ v:=323;
+ END;
+ BEGIN
+ v:= 324;
+ EXCEPTION WHEN 2002 THEN v:= 325;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Testing EXIT statement
+--echo #
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ i:= i + 1;
+ IF i >= 5 THEN
+ EXIT;
+ END IF;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ i:= i + 1;
+ EXIT WHEN i >=5;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ BEGIN
+ i:= i + 1;
+ IF i >= 5 THEN
+ EXIT;
+ END IF;
+ EXCEPTION
+ WHEN OTHERS THEN i:= 1000;
+ END;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE PROCEDURE p1(a IN OUT INT)
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ LOOP
+ BEGIN
+ i:= i + 1;
+ IF i >=5 THEN
+ EXIT;
+ END IF;
+ EXCEPTION
+ WHEN OTHERS THEN a:=1000;
+ END;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ a:= i;
+EXCEPTION
+ WHEN OTHERS THEN a:=11;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+set @v= 10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Testing RETURN in procedures
+DELIMITER /;
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+BEGIN
+ IF a < 10 THEN
+ BEGIN
+ a:= a + 1;
+ RETURN;
+ END;
+ END IF;
+ a:= 200;
+EXCEPTION
+ WHEN OTHERS THEN
+ BEGIN
+ a:= 100;
+ RETURN;
+ END;
+END;
+/
+DELIMITER ;/
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+--echo # Testing FOR loop statement
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, b INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN 1 .. a
+ LOOP
+ total:= total + i;
+ IF i = b THEN
+ EXIT;
+ END IF;
+ END LOOP;
+ RETURN total;
+END
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3, 100) FROM DUAL;
+SELECT f1(3, 2) FROM DUAL;
+DROP FUNCTION f1;
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, b INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN REVERSE 1..a
+ LOOP
+ total:= total + i;
+ IF i = b THEN
+ EXIT;
+ END IF;
+ END LOOP;
+ RETURN total;
+END
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3, 100) FROM DUAL;
+SELECT f1(3, 2) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing labeled FOR LOOP statement
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, limita INT, b INT, limitb INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ <<la>>
+ FOR ia IN 1 .. a
+ LOOP
+ total:= total + 1000;
+ <<lb>>
+ FOR ib IN 1 .. b
+ LOOP
+ total:= total + 1;
+ EXIT lb WHEN ib = limitb;
+ EXIT la WHEN ia = limita;
+ END LOOP lb;
+ END LOOP la;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(2, 1, 2, 2) FROM DUAL;
+SELECT f1(2, 2, 2, 2) FROM DUAL;
+SELECT f1(2, 3, 2, 3) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing labeled ITERATE in a labeled FOR LOOP
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ <<li>>
+ FOR i IN 1 .. a
+ LOOP
+ total:= total + 1000;
+ IF i = 5 THEN
+ ITERATE li;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ <<li>>
+ FOR i IN 1 .. a
+ LOOP
+ FOR j IN 1 .. 2
+ LOOP
+ total:= total + 1000;
+ IF i = 5 THEN
+ ITERATE li;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ <<lj>>
+ FOR j IN 1 .. 2
+ LOOP
+ <<li>>
+ FOR i IN 1 .. a
+ LOOP
+ total:= total + 1000;
+ IF i = 5 THEN
+ ITERATE li;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing CONTINUE statement
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ FOR i IN 1 .. a
+ LOOP
+ CONTINUE WHEN i=5;
+ total:= total + 1;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SHOW FUNCTION CODE f1;
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+--echo #
+--echo # Start of MDEV-10597 Cursors with parameters
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(arg_value_a VARCHAR, arg_value_b VARCHAR,
+ arg_pattern_a VARCHAR, arg_pattern_b VARCHAR)
+AS
+ v_a VARCHAR(10);
+ v_b VARCHAR(20);
+ CURSOR c (p_value_a VARCHAR,
+ p_value_b VARCHAR,
+ p_pattern_a VARCHAR,
+ p_pattern_b VARCHAR,
+ p_limit_a INT,
+ p_limit_b INT,
+ p_unused TEXT) IS
+ (SELECT p_value_a, p_value_b FROM DUAL
+ WHERE p_value_a LIKE p_pattern_a LIMIT p_limit_a)
+ UNION
+ (SELECT p_value_b, p_value_a FROM DUAL
+ WHERE p_value_b LIKE p_pattern_b LIMIT p_limit_b);
+BEGIN
+ OPEN c(arg_value_a, (SELECT arg_value_b),
+ arg_pattern_a, arg_pattern_b, 100, 101, 'x');
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_a, v_b;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1('aaa','bbb','aaa','bbb');
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # End of MDEV-10597 Cursors with parameters
+--echo #
+
+
+--echo #
+--echo # MDEV-10914 ROW data type for stored routine variables
+--echo #
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT
+AS
+ a ROW(a INT, b INT);
+BEGIN
+ a.b:= 200;
+ RETURN a.b;
+END;
+$$
+DELIMITER ;$$
+SHOW FUNCTION CODE f1;
+SELECT f1();
+DROP FUNCTION f1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
+BEGIN
+ rec:= ROW(10,20.123456,30.123,'test');
+ SELECT rec.a, rec.b, rec.c, rec.d;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
+ ROW(10,20.123456,30.123,'test');
+BEGIN
+ SELECT rec.a, rec.b, rec.c, rec.d;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
+ rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
+BEGIN
+ rec1:= ROW(10,20.123456,30.123,'test');
+ rec2:= rec1;
+ SELECT rec2.a, rec2.b, rec2.c, rec2.d;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
+ ROW(10,20.123456,30.123,'test');
+ rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) := rec1;
+BEGIN
+ SELECT rec2.a, rec2.b, rec2.c, rec2.d;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # End of MDEV-10914 ROW data type for stored routine variables
+--echo #
+
+--echo #
+--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ rec1.c:= 10e2;
+ rec1.d:= 10.12;
+ rec1.c:= rec1.d;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1,rec2 cur1%ROWTYPE;
+ rec3 cur2%ROWTYPE;
+ BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ END;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-10581 sql_mode=ORACLE: Explicit cursor FOR LOOP
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur0 IS SELECT 10 AS a, 'b0' AS b;
+ CURSOR cur1 IS SELECT 10 AS a, 'b0' AS b;
+ CURSOR cur2 IS SELECT 10 AS a, 'b0' AS b;
+BEGIN
+ FOR rec1 IN cur1
+ LOOP
+ SELECT rec1.a, rec1.b;
+ rec1.a:= 11;
+ rec1.b:= 'b1';
+ SELECT rec1.a, rec1.b;
+ END LOOP;
+ FOR rec0 IN cur0
+ LOOP
+ rec0.a:= 10;
+ rec0.b:='b0';
+ END LOOP;
+ FOR rec2 IN cur2
+ LOOP
+ rec2.a:= 10;
+ rec2.b:='b0';
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur0 IS SELECT 10 AS a, 'b0' AS b;
+BEGIN
+ FOR rec0 IN cur0
+ LOOP
+ DECLARE
+ CURSOR cur1 IS SELECT 11 AS a, 'b1' AS b;
+ BEGIN
+ rec0.a:= 11;
+ rec0.b:= 'b0';
+ FOR rec1 IN cur1
+ LOOP
+ rec1.a:= 11;
+ rec1.b:= 'b1';
+ DECLARE
+ CURSOR cur2 IS SELECT 12 AS a, 'b2' AS b;
+ BEGIN
+ FOR rec2 IN cur2
+ LOOP
+ rec2.a:=12;
+ rec2.b:='b2';
+ END LOOP;
+ END;
+ END LOOP;
+ END;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR loop
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ FOR rec1 IN (SELECT 11 AS a, 'b1' AS b)
+ LOOP
+ SELECT rec1.a, rec1.b;
+ rec1.a:= 11;
+ rec1.b:= 'b1';
+ SELECT rec1.a, rec1.b;
+ END LOOP;
+ FOR rec0 IN (SELECT 10 AS a, 'b0' AS b)
+ LOOP
+ rec0.a:= 10;
+ rec0.b:='b0';
+ END LOOP;
+ FOR rec2 IN (SELECT 12 AS a, 'b2' AS b)
+ LOOP
+ rec2.a:= 10;
+ rec2.b:='b0';
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ FOR rec0 IN (SELECT 10 AS a, 'b0' AS b)
+ LOOP
+ rec0.a:= 11;
+ rec0.b:= 'b0';
+ FOR rec1 IN (SELECT 11 AS a, 'b1' AS b)
+ LOOP
+ rec1.a:= 11;
+ rec1.b:= 'b1';
+ FOR rec2 IN (SELECT 12 AS a, 'b2' AS b)
+ LOOP
+ rec2.a:=12;
+ rec2.b:='b2';
+ END LOOP;
+ END LOOP;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
+--echo #
+
+--echo #
+--echo # Cursor declaration and cursor%ROWTYPE declaration in the same block
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (1,'a');
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT a FROM t1;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ rec1.a:= 10;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Recursive cursor and cursor%ROWTYPE declarations in the same block
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT:=10;
+ CURSOR cur1 IS SELECT a;
+ rec1 cur1%ROWTYPE;
+ CURSOR cur2 IS SELECT rec1.a + 1 "a";
+ rec2 cur2%ROWTYPE;
+BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ SELECT rec1.a;
+ open cur2;
+ FETCH cur2 INTO rec2;
+ CLOSE cur2;
+ SELECT rec2.a;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12441 Variables declared after cursors with parameters lose values
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 INT:=101;
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur0(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 INT:=101;
+ CURSOR cur1(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x2 INT:=102;
+ CURSOR cur2(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x3 INT:=103;
+BEGIN
+ OPEN cur0(0,1);
+ CLOSE cur0;
+ SELECT x0, x1, x2, x3;
+ OPEN cur1(10,11);
+ CLOSE cur1;
+ SELECT x0, x1, x2, x3;
+ OPEN cur2(20,21);
+ CLOSE cur2;
+ SELECT x0, x1, x2, x3;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1();
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 t1.a%TYPE:=101;
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 ROW(a INT,b INT):=ROW(101,102);
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1.a, x1.b;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+CALL p1();
+DROP PROCEDURE p1;
+
+--echo #
+--echo # MDEV-19640 Wrong SHOW PROCEDURE output for SET GLOBAL sysvar1=expr, sysvar2=expr
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1() AS
+BEGIN
+ SET GLOBAL max_allowed_packet=16000000, max_error_count=60;
+ SELECT @@GLOBAL.max_allowed_packet, @@GLOBAL.max_error_count;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # MDEV-19639 sql_mode=ORACLE: Wrong SHOW PROCEDURE output for sysvar:=expr
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1() AS
+BEGIN
+ max_error_count:=10;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
new file mode 100644
index 00000000..21683dd4
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
@@ -0,0 +1,295 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
+--echo #
+
+--echo #
+--echo # Variable after cursor declaration
+--echo #
+
+CREATE TABLE t1 (a INT);
+insert into t1 values (1);
+insert into t1 values (2);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT a FROM t1;
+ var1 varchar(10);
+BEGIN
+ OPEN c;
+ fetch c into var1;
+ SELECT c%ROWCOUNT,var1;
+ close c;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Variable after condition declaration
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ dup_key CONDITION FOR SQLSTATE '23000';
+ var1 varchar(40);
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+BEGIN
+ var1:='';
+ insert into t1 values (1);
+ select var1;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Condition after cursor declaration
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ var1 varchar(40);
+ var2 integer;
+ CURSOR c IS SELECT col1 FROM t1;
+ dup_key CONDITION FOR SQLSTATE '23000';
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+BEGIN
+ var1:='';
+ insert into t1 values (1);
+ SELECT var1;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Cursor after handler declaration
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1
+AS
+ var1 varchar(40);
+ var2 integer;
+ dup_key CONDITION FOR SQLSTATE '23000';
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+ CURSOR c IS SELECT col1 FROM t1;
+BEGIN
+ var1:='';
+ insert into t1 values (1);
+ SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Condition after handler declaration
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1
+AS
+ var1 varchar(40);
+ var2 integer;
+ dup_key CONDITION FOR SQLSTATE '23000';
+ CURSOR c IS SELECT col1 FROM t1;
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+ divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+ var1:='';
+ insert into t1 values (1);
+ SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Variable after handler declaration
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1
+AS
+ var1 varchar(40);
+ var2 integer;
+ dup_key CONDITION FOR SQLSTATE '23000';
+ CURSOR c IS SELECT col1 FROM t1;
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+ divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+ var1:='';
+ insert into t1 values (1);
+ SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Variable after cursor (inner block)
+--echo #
+
+CREATE TABLE t1 (col1 INT);
+insert into t1 values (1);
+insert into t1 values (2);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT col1 FROM t1;
+ var1 varchar(40);
+BEGIN
+ OPEN c;
+ begin
+ declare
+ CURSOR c IS SELECT col1 FROM t1 where col1=2;
+ var2 integer;
+ dup_key CONDITION FOR SQLSTATE '23000';
+ CONTINUE HANDLER FOR dup_key
+ BEGIN
+ var1:='duplicate key in index';
+ END;
+ begin
+ OPEN c;
+ fetch c into var1;
+ SELECT 'inner cursor',var1;
+ insert into t1 values (2);
+ close c;
+ end;
+ end;
+ SELECT var1;
+ fetch c into var1;
+ SELECT c%ROWCOUNT,var1;
+ begin
+ insert into t1 values (2);
+ exception when 1062 then
+ begin
+ SELECT 'dup key caugth';
+ end;
+ end;
+ close c;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Cursor declaration and row type declaration in same block
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+insert into t1 values(1,'a');
+delimiter $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT a FROM t1;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ rec1.a:= 10;
+END;
+$$
+delimiter ;$$
+call p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+
+--echo #
+--echo # Recursive cursor and cursor%ROWTYPE declarations in the same block
+--echo #
+
+delimiter $$;
+CREATE PROCEDURE p1
+AS
+ a INT:=10;
+ b VARCHAR(10):='b0';
+ c DOUBLE:=0.1;
+ CURSOR cur1 IS SELECT a, b, c;
+ rec1 cur1%ROWTYPE;
+ CURSOR cur2 IS SELECT rec1.a + 1 "a", rec1.b||'0' AS b, rec1.c AS c;
+ rec2 cur2%ROWTYPE;
+BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ SELECT rec1.a;
+ OPEN cur2;
+ FETCH cur2 INTO rec2;
+ CLOSE cur2;
+ SELECT rec2.a;
+ CREATE TABLE t2 AS SELECT rec2.a AS a, rec2.b AS b, rec2.c AS c;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12916 Wrong column data type for an INT field of a cursor-anchored ROW variable
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT DEFAULT 10;
+ CURSOR cur1 IS SELECT a;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ CREATE TABLE t1 AS SELECT rec1.a;
+ SHOW CREATE TABLE t1;
+ DROP TABLE t1;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test b/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
new file mode 100644
index 00000000..78a38c5f
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
@@ -0,0 +1,1597 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+--echo #
+
+--echo #
+--echo # A complete working example
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+CREATE TABLE t2 LIKE t1;
+INSERT INTO t1 VALUES (10,'b10');
+INSERT INTO t1 VALUES (20,'b20');
+INSERT INTO t1 VALUES (30,'b30');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR c IS SELECT a,b FROM t1;
+BEGIN
+ DECLARE
+ rec c%ROWTYPE;
+ BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO rec;
+ EXIT WHEN c%NOTFOUND;
+ SELECT 'rec=(' || rec.a ||','|| rec.b||')' AS c FROM dual;
+ INSERT INTO t2 VALUES (rec.a, rec.b);
+ END LOOP;
+ CLOSE c;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+DROP PROCEDURE p1;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # cursor%ROWTYPE referring to a table in a non-existing database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM tes2.t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+--error ER_NO_SUCH_TABLE
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # cursor%ROWTYPE referring to a table in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CALL p1();
+DROP TABLE t1;
+--error ER_NO_SUCH_TABLE
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # cursor%ROWTYPE referring to a table in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM test.t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Cursor%ROWTYPE referring to a view in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM v1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CREATE VIEW v1 AS SELECT * FROM t1;
+CALL p1();
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # cursor%ROWTYPE referring to a view in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM test.v1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CREATE VIEW v1 AS SELECT * FROM t1;
+CALL p1();
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Checking that all cursor%ROWTYPE fields are NULL by default
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ SELECT rec1.a, rec1.b, rec1.c, rec1.d;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with a ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb');
+ BEGIN
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with an incompatible ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb','ccc');
+ BEGIN
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with a ROW variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
+ rec2 cur%ROWTYPE := rec1;
+ BEGIN
+ SELECT rec2.a, rec2.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A ROW variable using a cursor%ROWTYPE variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb');
+ rec2 ROW(a INT, b VARCHAR(10)):= rec1;
+ BEGIN
+ SELECT rec2.a, rec2.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning cursor%ROWTYPE variables with a different column count
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE);
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ rec2:=rec1;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ rec1:=rec2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning compatible cursor%ROWTYPE variables (equal number of fields)
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (x INT, y VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ rec2:=rec1;
+ SELECT rec2.x, rec2.y;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning between incompatible cursor%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 ROW(x INT,y INT,z INT);
+ BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec2.z:= 30;
+ rec1:= rec2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning between compatible cursor%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 ROW(x INT,y INT);
+ BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec1:= rec2;
+ SELECT rec1.a, rec1.b;
+ rec1.a:= 11;
+ rec1.b:= 21;
+ rec2:= rec1;
+ SELECT rec2.x, rec2.y;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning cursor%ROWTYPE from a ROW expression
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ rec1:= ROW(10,20);
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a cursor%ROWTYPE variable with a wrong field count
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ CLOSE cur1;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_WRONG_NO_OF_FETCH_ARGS
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a cursor%ROWTYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE TABLE t2 LIKE t1;
+INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
+INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32);
+INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ OPEN cur;
+ LOOP
+ FETCH cur INTO rec;
+ EXIT WHEN cur%NOTFOUND;
+ SELECT rec.a, rec.b, rec.c, rec.d;
+ INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d);
+ END LOOP;
+ CLOSE cur;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a cursor%ROWTYPE variable, cur%ROWTYPE declared inside the LOOP
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE TABLE t2 LIKE t1;
+INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
+INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32);
+INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ OPEN cur;
+ LOOP
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ FETCH cur INTO rec;
+ EXIT WHEN cur%NOTFOUND;
+ SELECT rec.a, rec.b, rec.c, rec.d;
+ INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d);
+ END;
+ END LOOP;
+ CLOSE cur;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a cursor%ROWTYPE variable with different column names
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (x INT, y VARCHAR(10));
+INSERT INTO t1 VALUES (10,'bbb');
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ SELECT rec2.x, rec2.y;
+ CLOSE cur1;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a cursor%ROWTYPE variable, with truncation
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (a INT, b INT);
+INSERT INTO t1 VALUES (10,'11x');
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ SELECT rec2.a, rec2.b;
+ CLOSE cur1;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # cursor%ROWTYPE variables are not allowed in LIMIT
+--echo #
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,2);
+DELIMITER $$;
+--error ER_WRONG_SPVAR_TYPE_IN_LIMIT
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE:=(1,2);
+ BEGIN
+ SELECT * FROM t1 LIMIT rec1.a;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+--echo #
+--echo # cursor%ROWTYPE variable fields as OUT parameters
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
+AS
+BEGIN
+ a:=10;
+ b:='bb';
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ CALL p1(rec1.a, rec1.b);
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Passing the entire cursor%ROWTYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ SELECT a.a, a.b;
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur%ROWTYPE:= ROW(10,'bb');
+ BEGIN
+ CALL p1(rec1);
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Passing the entire cursor%ROWTYPE variable as an OUT parameter
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ a:= ROW(10,'bb');
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur%ROWTYPE;
+ BEGIN
+ CALL p1(rec1);
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Assigning a cursor%ROWTYPE field to an OUT parameter
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1 (res IN OUT INTEGER)
+AS
+ a INT:=10;
+ CURSOR cur1 IS SELECT a FROM DUAL;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ res:=rec1.a;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1(@res);
+SELECT @res;
+SET @res=NULL;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Testing Item_splocal_row_field_by_name::print
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur1%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ EXPLAIN EXTENDED SELECT rec.a, rec.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Run time error in the cursor statement
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT
+ 10 AS a,
+ CONCAT(_latin1'a' COLLATE latin1_bin,
+ _latin1'a' COLLATE latin1_swedish_ci) AS b;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ SELECT a,b;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_CANT_AGGREGATE_2COLLATIONS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+
+--echo #
+--echo # Non-existing field
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur1%ROWTYPE;
+ BEGIN
+ SELECT rec.c;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+ALTER TABLE t1 ADD c INT;
+#
+# The below ALTER is needed as a workaround to call sp_cache_invalidate()
+# Please remove it after fixing MDEV-12166
+#
+ALTER PROCEDURE p1 COMMENT 'test';
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that field names are case insensitive
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ SELECT rec.A, rec.B;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that cursor%ROWTYPE uses temporary tables vs shadowed real tables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ SELECT rec.A, rec.B;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+DROP TEMPORARY TABLE t1;
+#
+# The below ALTER is needed as a workaround to call sp_cache_invalidate()
+# Please remove it after fixing MDEV-12166
+#
+ALTER PROCEDURE p1 COMMENT 'test';
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that the structure of cursor%ROWTYPE variables is determined at the CURSOR instantiation time
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DROP TABLE t1;
+ CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+ DECLARE
+ rec cur%ROWTYPE; -- This has a column "c"
+ BEGIN
+ rec.c:=10;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE; -- This does not have a column "c"
+ BEGIN
+ DROP TABLE t1;
+ CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+ rec.c:=10;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Duplicate field nams in a cursor referenced by %ROWTYPE
+--echo #
+
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT * FROM t1, t2;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ SELECT rec.a;
+ rec.a:=10;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_DUP_FIELDNAME
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Tricky field names a cursor referenced by %ROWTYPE
+--echo #
+
+SET NAMES utf8;
+CREATE TABLE t1 (a VARCHAR(10));
+INSERT INTO t1 VALUES ('a');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT a, CONCAT(a,'a'), CONCAT(a,'ö') FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ OPEN cur;
+ FETCH cur INTO rec;
+ CLOSE cur;
+ SELECT rec.a, rec."CONCAT(a,'a')", rec."CONCAT(a,'ö')";
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+SET NAMES latin1;
+
+
+--echo #
+--echo # Using definitions recursively (cursor%ROWTYPE variables in another cursor SELECT)
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'b1'),(20,'b2'),(30,'b3');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur1 IS SELECT a,b FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE:=ROW(0,'b0');
+ CURSOR cur2 IS SELECT rec1.a AS a, rec1.b AS b FROM t1;
+ BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ OPEN cur2;
+ LOOP
+ FETCH cur2 INTO rec2;
+ EXIT WHEN cur2%NOTFOUND;
+ SELECT rec2.a, rec2.b;
+ END LOOP;
+ CLOSE cur2;
+ END;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing queries with auto-generated Items.
+--echo # An instance of Item_func_conv_charset is created during the below SELECT query.
+--echo # We check here that during an implicit cursor OPEN
+--echo # done in sp_instr_cursor_copy_struct::exec_core()
+--echo # all temporary Items are created on a proper memory root and are safely destroyed.
+--echo #
+
+CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1, b VARCHAR(10) CHARACTER SET utf8);
+INSERT INTO t1 VALUES (0xFF, 'a');
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT CONCAT(a,b) AS c FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ SELECT HEX(rec1.c);
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-10581 sql_mode=ORACLE: Explicit cursor FOR LOOP
+--echo #
+
+--echo # IN followed by a non-identifier
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN 10
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # IN followed by a quoted identifier: table.column
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN c1.c2
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # IN followed by a quoted identifier: .table.column
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN .c1.c2
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # IN followed by a quoted identifier: schema.table.column
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN c1.c2.c3
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # IN followed by an unknown cursor name
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN c2
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # Make sure "rec" shadows other declarations outside the loop
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10, 'b0');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec INT:=10;
+ CURSOR c1 IS SELECT a,b FROM t1;
+BEGIN
+ FOR rec IN c1
+ LOOP
+ SELECT rec.a;
+ END LOOP;
+ SELECT rec;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo # Make sure "rec" is not visible after END LOOP
+
+DELIMITER $$;
+--error ER_UNKNOWN_STRUCTURED_VARIABLE
+CREATE PROCEDURE p1 AS
+ CURSOR c1 IS SELECT 'test' AS a FROM DUAL;
+BEGIN
+ FOR rec IN c1
+ LOOP
+ NULL;
+ END LOOP;
+ rec.a:= 10;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # Make sure that duplicate column names are not allowed
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT 'a' AS a, 'A' as a;
+BEGIN
+ FOR rec IN cur
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+--error ER_DUP_FIELDNAME
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo # A complete working example
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'b0');
+INSERT INTO t1 VALUES (11,'b1');
+INSERT INTO t1 VALUES (12,'b2');
+CREATE TABLE t2 LIKE t1;
+CREATE TABLE t3 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT a, b FROM t1;
+BEGIN
+ FOR rec IN cur
+ LOOP
+ SELECT rec.a, rec.b;
+ INSERT INTO t2 VALUES (rec.a, rec.b);
+ rec.a:= rec.a + 1000;
+ rec.b:= 'b' || rec.b;
+ INSERT INTO t3 VALUES (rec.a, rec.b);
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+SELECT * FROM t3;
+DROP PROCEDURE p1;
+DROP TABLE t3;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12314 Implicit cursor FOR LOOP for cursors with parameters
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b0');
+INSERT INTO t1 VALUES (11,'b1');
+INSERT INTO t1 VALUES (12,'b2');
+DELIMITER $$;
+CREATE PROCEDURE p1(pa INT, pb VARCHAR(32)) AS
+ CURSOR cur(va INT, vb VARCHAR(32)) IS
+ SELECT a, b FROM t1 WHERE a=va AND b=vb;
+BEGIN
+ FOR rec IN cur(pa,pb)
+ LOOP
+ SELECT rec.a, rec.b;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1(10,'B0');
+CALL p1(11,'B1');
+CALL p1(12,'B2');
+CALL p1(12,'non-existing');
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR loop
+--echo #
+
+--echo # Parse error in the cursor SELECT statement
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT a, b FROM)
+ LOOP
+ SELECT rec.a, rec.b;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo # Make sure "rec" is not visible after END LOOP
+
+DELIMITER $$;
+--error ER_UNKNOWN_STRUCTURED_VARIABLE
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT 'test' AS a)
+ LOOP
+ NULL;
+ END LOOP;
+ rec.a:= 10;
+END;
+$$
+DELIMITER ;$$
+
+--echo # Make sure "rec" is not visible inside the SELECT statement
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT rec)
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+--error ER_BAD_FIELD_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT rec.a)
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+--error ER_UNKNOWN_TABLE
+CALL p1;
+DROP PROCEDURE p1;
+
+--echo # Totally confusing name mixture
+
+CREATE TABLE rec (rec INT);
+INSERT INTO rec VALUES (10);
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT rec FROM rec)
+ LOOP
+ SELECT rec.rec;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE rec;
+
+
+--echo # Make sure that duplicate column names are not allowed
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT 'a' AS a, 'A' as a)
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+--error ER_DUP_FIELDNAME
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo # A complete working example
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'b0');
+INSERT INTO t1 VALUES (11,'b1');
+INSERT INTO t1 VALUES (12,'b2');
+CREATE TABLE t2 LIKE t1;
+CREATE TABLE t3 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec IN (SELECT a, b FROM t1)
+ LOOP
+ SELECT rec.a, rec.b;
+ INSERT INTO t2 VALUES (rec.a, rec.b);
+ rec.a:= rec.a + 1000;
+ rec.b:= 'b'|| rec.b;
+ INSERT INTO t3 VALUES (rec.a, rec.b);
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+SELECT * FROM t3;
+DROP PROCEDURE p1;
+DROP TABLE t3;
+DROP TABLE t2;
+DROP TABLE t1;
+
+
+--echo # A combination of explicit and implicit cursors
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'b1');
+INSERT INTO t1 VALUES (11,'b2');
+INSERT INTO t1 VALUES (12,'b3');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ FOR rec1 IN (SELECT a, b FROM t1)
+ LOOP
+ DECLARE
+ CURSOR cur2 IS SELECT a+1000 AS a, 'bb'||b AS b FROM t1 WHERE a=rec1.a AND b=rec1.b;
+ BEGIN
+ SELECT rec1.a, rec1.b;
+ FOR rec2 IN cur2
+ LOOP
+ SELECT rec2.a, rec2.b;
+ END LOOP;
+ END;
+ END LOOP;
+ FOR rec1 IN (SELECT a,b FROM t1)
+ LOOP
+ FOR rec2 IN (SELECT a+2000 AS a,'bbb'||b AS b FROM t1 WHERE a=rec1.a AND b=rec1.b)
+ LOOP
+ SELECT rec2.a, rec2.b;
+ END LOOP;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-15941 Explicit cursor FOR loop does not close the cursor
+--echo #
+
+DELIMITER $$;
+--error ER_SP_CURSOR_NOT_OPEN
+DECLARE
+ CURSOR cur IS SELECT 1 AS a FROM DUAL;
+ v INT;
+BEGIN
+ FOR rec IN cur
+ LOOP
+ NULL;
+ END LOOP;
+ FETCH cur INTO v;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_CURSOR_NOT_OPEN
+DECLARE
+ CURSOR cur IS SELECT 1 AS a FROM DUAL;
+ v INT;
+BEGIN
+<<label>>
+ FOR rec IN cur
+ LOOP
+ NULL;
+ END LOOP label;
+ FETCH cur INTO v;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_CURSOR_ALREADY_OPEN
+DECLARE
+ CURSOR cur IS SELECT 1 AS a FROM DUAL;
+BEGIN
+ OPEN cur;
+ FOR rec IN cur
+ LOOP
+ NULL;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+DECLARE
+ CURSOR cur IS SELECT 1 AS a FROM DUAL;
+BEGIN
+ FOR rec IN cur
+ LOOP
+ SELECT rec.a;
+ END LOOP;
+ SELECT cur%ISOPEN;
+ FOR rec IN cur
+ LOOP
+ SELECT rec.a;
+ END LOOP;
+ SELECT cur%ISOPEN;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+DECLARE
+ CURSOR cur IS SELECT 1 AS a FROM DUAL;
+BEGIN
+<<label1>>
+ FOR rec IN cur
+ LOOP
+ SELECT rec.a;
+ END LOOP label1;
+ SELECT cur%ISOPEN;
+<<label2>>
+ FOR rec IN cur
+ LOOP
+ SELECT rec.a;
+ END LOOP;
+ SELECT cur%ISOPEN;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # MDEV-14139 Anchored data types for variables
+--echo #
+
+DELIMITER $$;
+DECLARE
+ CURSOR c1 IS SELECT 10 AS a, 'bbb' AS b, TIME'10:20:30' AS c;
+ row1 c1%ROWTYPE;
+ a_row1 row1%TYPE;
+ aa_row1 a_row1%TYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT a_row1.a AS a, a_row1.b AS b, a_row1.c AS c;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ CREATE TABLE t2 AS SELECT aa_row1.a AS a, aa_row1.b AS b, aa_row1.c AS c;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # MDEV-14388 Server crashes in handle_select / val_uint in ORACLE mode
+--echo #
+
+CREATE TABLE t1 (id INT);
+INSERT INTO t1 VALUES (0),(1),(2),(3);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT is
+BEGIN
+ FOR v1 in (SELECT id FROM t1)
+ LOOP
+ NULL;
+ END LOOP;
+ RETURN 1;
+END;
+$$
+DELIMITER ;$$
+SELECT f1();
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (id INT);
+INSERT INTO t1 VALUES (1),(2),(3),(4);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT IS
+ CURSOR cur IS SELECT id FROM t1;
+ rec cur%ROWTYPE;
+BEGIN
+ RETURN 1;
+END;
+$$
+DELIMITER ;$$
+SELECT f1();
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-17278 CURSOR FOR LOOP - ERROR: unexpected end of stream, read 0 bytes (SERVER CRASH)
+--echo #
+
+CREATE TABLE t1 (id2 int, id int, en1 enum('aaa','a','b','c'));
+INSERT INTO t1 VALUES(1,1,'aaa'),(2,2,'a'),(3,3,'b'),(4,4,'c');
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+BEGIN
+ FOR rec IN (SELECT en1 FROM t1)
+ LOOP
+ SELECT rec.en1;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor.test b/mysql-test/suite/compat/oracle/t/sp-cursor.test
new file mode 100644
index 00000000..d7e2a5de
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor.test
@@ -0,0 +1,1044 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
+--echo #
+
+
+--echo #
+--echo # Cursor attributes outside of an SP context
+--echo #
+
+--error ER_SP_CURSOR_MISMATCH
+SELECT c%ISOPEN;
+--error ER_SP_CURSOR_MISMATCH
+SELECT c%FOUND;
+--error ER_SP_CURSOR_MISMATCH
+SELECT c%NOTFOUND;
+--error ER_SP_CURSOR_MISMATCH
+SELECT c%ROWCOUNT;
+
+
+--echo #
+--echo # Undefinite cursor attributes
+--echo #
+
+DELIMITER $$;
+--error ER_SP_CURSOR_MISMATCH
+CREATE PROCEDURE p1
+AS
+BEGIN
+ SELECT c%ISOPEN;
+END;
+$$
+--error ER_SP_CURSOR_MISMATCH
+CREATE PROCEDURE p1
+AS
+BEGIN
+ SELECT c%ROWCOUNT;
+END;
+$$
+--error ER_SP_CURSOR_MISMATCH
+CREATE PROCEDURE p1
+AS
+BEGIN
+ SELECT c%FOUND;
+END;
+$$
+--error ER_SP_CURSOR_MISMATCH
+CREATE PROCEDURE p1
+AS
+BEGIN
+ SELECT c%NOTFOUND;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%FOUND;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%NOTFOUND;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_CURSOR_NOT_OPEN
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT with INVALID_CURSOR exception
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%ROWCOUNT;
+EXCEPTION
+ WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%FOUND;
+EXCEPTION
+ WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ SELECT c%NOTFOUND;
+EXCEPTION
+ WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # print()
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT * FROM t1 ORDER BY a;
+BEGIN
+ EXPLAIN EXTENDED SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Declared data type of the attributes
+--echo #
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR c IS SELECT * FROM t1 ORDER BY a;
+BEGIN
+ OPEN c;
+ CREATE TABLE t2 AS SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Core functionality
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+INSERT INTO t1 VALUES (30);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT:=0;
+ CURSOR c IS SELECT * FROM t1 ORDER BY a;
+BEGIN
+ SELECT a, c%ISOPEN;
+ OPEN c;
+ /*
+ After OPEN and before FETCH:
+ - %ROWCOUNT returns 0
+ - %FOUND and %NOTFOUND return NULL
+ */
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ FETCH c INTO a;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ FETCH c INTO a;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ FETCH c INTO a;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ FETCH c INTO a;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ CLOSE c;
+ SELECT a, c%ISOPEN;
+ /*
+ After reopen and before FETCH:
+ - %ROWCOUNT returns 0
+ - %FOUND and %NOTFOUND return NULL
+ */
+ OPEN c;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ FETCH c INTO a;
+ SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %NOTFOUND as a loop exit condition
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+INSERT INTO t1 VALUES (30);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT:=0;
+ CURSOR c IS SELECT * FROM t1 ORDER BY a;
+BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO a;
+ EXIT WHEN c%NOTFOUND;
+ SELECT a;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %FOUND as a loop exit condition
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+INSERT INTO t1 VALUES (30);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT:=0;
+ CURSOR c IS SELECT * FROM t1 ORDER BY a;
+BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO a;
+ EXIT WHEN NOT c%FOUND;
+ SELECT a;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # End of MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
+--echo #
+
+--echo #
+--echo # MDEV-10597 Cursors with parameters
+--echo #
+
+--echo #
+--echo # OPEN with a wrong number of parameters
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+--error ER_WRONG_PARAMCOUNT_TO_CURSOR
+CREATE PROCEDURE p1(a_a INT,a_b VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
+BEGIN
+ OPEN c(a_a);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ DBMS_OUTPUT.PUT_LINE('Fetched a record a='||TO_CHAR(v_a)||' b='||v_b);
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+--echo #
+--echo # Cursor parameters are not visible outside of the cursor
+--echo #
+
+DELIMITER $$;
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+CREATE PROCEDURE p1(a_a INT)
+AS
+ v_a INT;
+ CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
+BEGIN
+ OPEN c(a_a);
+ p_a:=10;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_UNKNOWN_SYSTEM_VARIABLE
+CREATE PROCEDURE p1(a_a INT)
+AS
+ v_a INT;
+ CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
+BEGIN
+ p_a:=10;
+ OPEN c(a_a);
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Cursor parameter shadowing a local variable
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DELIMITER $$;
+CREATE PROCEDURE p1(a INT)
+AS
+ v_a INT:=NULL;
+ p_a INT:=NULL;
+ CURSOR c (p_a VARCHAR2) IS SELECT a FROM t1 WHERE p_a IS NOT NULL;
+BEGIN
+ OPEN c(a);
+ FETCH c INTO v_a;
+ IF c%NOTFOUND THEN
+ BEGIN
+ SELECT 'No records found' AS msg;
+ RETURN;
+ END;
+ END IF;
+ CLOSE c;
+ SELECT 'Fetched a record a='||v_a AS msg;
+ INSERT INTO t1 VALUES (v_a);
+END;
+$$
+DELIMITER ;$$
+CALL p1(1);
+SELECT * FROM t1;
+CALL p1(NULL);
+SELECT * FROM t1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Parameters in SELECT list
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT, p_b VARCHAR) IS SELECT p_a,p_b FROM DUAL;
+BEGIN
+ FOR i IN 0..1
+ LOOP
+ OPEN c(a_a + i,a_b);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
+ END LOOP;
+ CLOSE c;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1(1,'b1');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Parameters in SELECT list + UNION
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT, p_b VARCHAR) IS
+ SELECT p_a,p_b FROM DUAL
+ UNION ALL
+ SELECT p_a+1,p_b||'b' FROM DUAL;
+BEGIN
+ OPEN c(a_a,a_b);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1(1,'b1');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Parameters in SELECT list + type conversion + warnings
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a VARCHAR)
+AS
+ v_a INT;
+ CURSOR c (p_a INT) IS SELECT p_a FROM DUAL;
+BEGIN
+ OPEN c(a_a);
+ LOOP
+ FETCH c INTO v_a;
+ EXIT WHEN c%NOTFOUND;
+ SELECT 'Fetched a record a=' || v_a AS msg;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1('1b');
+CALL p1('b1');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # One parameter in SELECT list + subselect
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a VARCHAR)
+AS
+ v_a VARCHAR(10);
+ CURSOR c (p_a VARCHAR) IS
+ SELECT p_a FROM DUAL UNION SELECT REVERSE(p_a) FROM DUAL;
+BEGIN
+ OPEN c((SELECT a_a));
+ LOOP
+ FETCH c INTO v_a;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_a;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1('ab');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Two parameters in SELECT list + subselect
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ v_a VARCHAR(10);
+ v_b VARCHAR(20);
+ CURSOR c (p_a VARCHAR, p_b VARCHAR) IS
+ SELECT p_a, p_b FROM DUAL
+ UNION
+ SELECT p_b, p_a FROM DUAL;
+BEGIN
+ OPEN c((SELECT 'aaa'),(SELECT 'bbb'));
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_a, v_b;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Two parameters in SELECT list + two parameters in WHERE + subselects
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a VARCHAR, a_b VARCHAR)
+AS
+ v_a VARCHAR(10);
+ v_b VARCHAR(20);
+ CURSOR c (value_a VARCHAR, value_b VARCHAR,
+ pattern_a VARCHAR, pattern_b VARCHAR) IS
+ SELECT value_a, value_b FROM DUAL WHERE value_a LIKE pattern_a
+ UNION
+ SELECT value_b, value_a FROM DUAL WHERE value_b LIKE pattern_b;
+BEGIN
+ OPEN c((SELECT 'aaa'),(SELECT 'bbb'),(SELECT a_a),(SELECT a_b));
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_a, v_b;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1('%','%');
+CALL p1('aaa','xxx');
+CALL p1('xxx','bbb');
+CALL p1('xxx','xxx');
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Parameters in SELECT list + stored function
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1 (a VARCHAR) RETURN VARCHAR
+AS
+BEGIN
+ RETURN a || 'y';
+END;
+$$
+CREATE PROCEDURE p1(a_a VARCHAR)
+AS
+ v_a VARCHAR(10);
+ v_b VARCHAR(10);
+ CURSOR c (p_sel_a VARCHAR, p_cmp_a VARCHAR) IS
+ SELECT p_sel_a, p_cmp_a FROM DUAL;
+BEGIN
+ OPEN c(f1(a_a), f1(a_a));
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_a;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1('x');
+# A complex expression
+CALL p1(f1(COALESCE(NULL, f1('x'))));
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # One parameter in WHERE clause
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (1,'11');
+INSERT INTO t1 VALUES (1,'12');
+INSERT INTO t1 VALUES (2,'21');
+INSERT INTO t1 VALUES (2,'22');
+INSERT INTO t1 VALUES (3,'31');
+INSERT INTO t1 VALUES (3,'32');
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a INT)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT) IS SELECT a,b FROM t1 WHERE a=p_a;
+BEGIN
+ OPEN c(a_a);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ INSERT INTO t2 VALUES (v_a,v_b);
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1(1);
+SELECT * FROM t2;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Two parameters in WHERE clause
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (1,'11');
+INSERT INTO t1 VALUES (1,'12');
+INSERT INTO t1 VALUES (2,'21');
+INSERT INTO t1 VALUES (2,'22');
+INSERT INTO t1 VALUES (3,'31');
+INSERT INTO t1 VALUES (3,'32');
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT, p_b VARCHAR) IS SELECT a,b FROM t1 WHERE a=p_a AND b=p_b;
+BEGIN
+ OPEN c(a_a, a_b);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ INSERT INTO t2 VALUES (v_a,v_b);
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1(1,'11');
+SELECT * FROM t2;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Parameters in WHERE and HAVING clauses
+--echo #
+CREATE TABLE t1 (name VARCHAR(10), value INT);
+INSERT INTO t1 VALUES ('but',1);
+INSERT INTO t1 VALUES ('but',1);
+INSERT INTO t1 VALUES ('but',1);
+INSERT INTO t1 VALUES ('bin',1);
+INSERT INTO t1 VALUES ('bin',1);
+INSERT INTO t1 VALUES ('bot',1);
+DELIMITER $$;
+CREATE PROCEDURE p1 (arg_name_limit VARCHAR, arg_total_limit INT)
+AS
+ v_name VARCHAR(10);
+ v_total INT;
+-- +0 is needed to work around the bug MDEV-11081
+ CURSOR c(p_v INT) IS
+ SELECT name, SUM(value + p_v) + 0 AS total FROM t1
+ WHERE name LIKE arg_name_limit
+ GROUP BY name HAVING total>=arg_total_limit;
+BEGIN
+ FOR i IN 0..1
+ LOOP
+ OPEN c(i);
+ LOOP
+ FETCH c INTO v_name, v_total;
+ EXIT WHEN c%NOTFOUND;
+ SELECT v_name, v_total;
+ END LOOP;
+ CLOSE c;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1('%', 2);
+CALL p1('b_t', 0);
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # One parameter in LIMIT clause
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (1,'b1');
+INSERT INTO t1 VALUES (2,'b2');
+INSERT INTO t1 VALUES (3,'b3');
+INSERT INTO t1 VALUES (4,'b4');
+INSERT INTO t1 VALUES (5,'b5');
+INSERT INTO t1 VALUES (6,'b6');
+DELIMITER $$;
+CREATE PROCEDURE p1(a_a INT)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR c (p_a INT) IS SELECT a,b FROM t1 ORDER BY a LIMIT p_a;
+BEGIN
+ CREATE TABLE t2 (a INT, b VARCHAR(10));
+ OPEN c(a_a);
+ LOOP
+ FETCH c INTO v_a, v_b;
+ EXIT WHEN c%NOTFOUND;
+ INSERT INTO t2 VALUES (v_a,v_b);
+ END LOOP;
+ CLOSE c;
+ SELECT * FROM t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1(1);
+CALL p1(3);
+CALL p1(6);
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # End of MDEV-10597 Cursors with parameters
+--echo #
+
+--echo #
+--echo # MDEV-12209 sql_mode=ORACLE: Syntax error in a OPEN cursor with parameters makes the server crash
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (1,'A');
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1(a INT,b VARCHAR)
+AS
+ CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
+BEGIN
+ OPEN c(a+, b);
+ LOOP
+ FETCH c INTO a, b;
+ EXIT WHEN c%NOTFOUND;
+ SELECT a, b;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3));
+INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123');
+INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123');
+CREATE TABLE t2 LIKE t1;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ v_a t1.a%TYPE;
+ v_b t1.b%TYPE;
+ v_c t1.c%TYPE;
+ CURSOR c IS SELECT a,b,c FROM t1;
+BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO v_a, v_b, v_c;
+ EXIT WHEN c%NOTFOUND;
+ INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c);
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+INSERT INTO t1 VALUES (20,'b20');
+INSERT INTO t1 VALUES (30,'b30');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec ROW(a INT, b VARCHAR(32));
+ CURSOR c IS SELECT a,b FROM t1;
+BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO rec;
+ EXIT WHEN c%NOTFOUND;
+ SELECT ('rec=(' || rec.a ||','|| rec.b||')') AS c;
+ END LOOP;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12441 Variables declared after cursors with parameters lose values
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 INT:=101;
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 t1.a%TYPE:=101;
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
+ x1 ROW(a INT,b INT):=ROW(101,102);
+BEGIN
+ OPEN cur(10,11);
+ CLOSE cur;
+ SELECT x0, x1.a, x1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'Tbl-t1.b0');
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT a,b FROM t1;
+ x1 t1%ROWTYPE:=ROW(101,'Var-x1.b0');
+BEGIN
+ SELECT x0, x1.a, x1.b;
+ OPEN cur(10,11);
+ FETCH cur INTO x1;
+ CLOSE cur;
+ SELECT x0, x1.a, x1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'Tbl-t1.b0');
+DELIMITER $$;
+CREATE PROCEDURE p1() AS
+ x0 INT:=100;
+ CURSOR cur(cp1 INT, cp2 INT) IS SELECT a,b FROM t1;
+ x1 cur%ROWTYPE:=ROW(101,'Var-x1.b0');
+BEGIN
+ SELECT x0, x1.a, x1.b;
+ OPEN cur(10,11);
+ FETCH cur INTO x1;
+ CLOSE cur;
+ SELECT x0, x1.a, x1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-12854 Synchronize CREATE..SELECT data type and result set metadata data type for INT functions
+--echo #
+
+--enable_metadata
+--disable_ps_protocol
+DELIMITER $$;
+DECLARE
+ CURSOR c IS SELECT 1 AS c FROM DUAL;
+BEGIN
+ OPEN c;
+ SELECT
+ c%ISOPEN,
+ c%NOTFOUND,
+ c%FOUND,
+ c%ROWCOUNT;
+ CLOSE c;
+END;
+$$
+DELIMITER ;$$
+--enable_ps_protocol
+--disable_metadata
+
+
+--echo #
+--echo # MDEV-17387 MariaDB Server giving wrong error while executing select query from procedure
+--echo #
+
+CREATE TABLE t1
+(
+ JOBN varchar(18) NOT NULL,
+ pk int(11) NOT NULL,
+ PRIMARY KEY (pk),
+ KEY (JOBN)
+);
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ lS NUMBER(10) :=0;
+ CURSOR cBPD IS SELECT * FROM t1 WHERE JOBN='x';
+BEGIN
+ FOR lbpd IN cBPD LOOP
+ lS:=lS+1;
+ END LOOP;
+EXCEPTION
+ WHEN OTHERS THEN
+ BEGIN
+ SELECT SQLERRM;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Start of 10.8 tests
+--echo #
+
+--echo #
+--echo # MDEV-10654 IN, OUT, INOUT parameters in CREATE FUNCTION
+--echo #
+
+DELIMITER $$;
+DECLARE
+ va INT;
+ CURSOR cur (a IN INT) IS SELECT a FROM dual;
+BEGIN
+ OPEN cur(1);
+ FETCH cur INTO va;
+ CLOSE cur;
+ SELECT va;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_NOT_SUPPORTED_YET
+DECLARE
+ va INT;
+ CURSOR cur (a OUT INT) IS SELECT a FROM dual;
+BEGIN
+ OPEN cur(1);
+ FETCH cur INTO va;
+ CLOSE cur;
+ SELECT va;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_NOT_SUPPORTED_YET
+DECLARE
+ va INT;
+ CURSOR cur (a INOUT INT) IS SELECT a FROM dual;
+BEGIN
+ OPEN cur(1);
+ FETCH cur INTO va;
+ CLOSE cur;
+ SELECT va;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # End of 10.8 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/sp-expr.test b/mysql-test/suite/compat/oracle/t/sp-expr.test
new file mode 100644
index 00000000..06a5c59b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-expr.test
@@ -0,0 +1,165 @@
+# Testing expressions of different kinds in various parts of SP syntax
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+#
+# Subselects in SP control structures
+#
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DELIMITER $$;
+
+BEGIN
+ CASE ((1) IN (SELECT a FROM t1)) WHEN 1 THEN SELECT 1;
+ ELSE SELECT NULL;
+ END CASE;
+END;
+$$
+BEGIN
+ CASE (EXISTS (SELECT a FROM t1)) WHEN 1 THEN SELECT 1;
+ ELSE SELECT NULL;
+ END CASE;
+END;
+$$
+
+BEGIN
+ IF ((1) IN (SELECT a FROM t1)) THEN SELECT 1;
+ ELSE SELECT NULL;
+ END IF;
+END;
+$$
+BEGIN
+ IF (EXISTS (SELECT a FROM t1)) THEN SELECT 1;
+ ELSE SELECT NULL;
+ END IF;
+END;
+$$
+
+BEGIN
+ WHILE ((1234) IN (SELECT * FROM t1)) LOOP
+ SELECT 1;
+ END LOOP;
+END;
+$$
+BEGIN
+ WHILE (EXISTS (SELECT * FROM t1 WHERE a=1234)) LOOP
+ SELECT 1;
+ END LOOP;
+END;
+$$
+
+BEGIN
+ REPEAT
+ SELECT 1;
+ UNTIL (1 IN (SELECT * FROM t1))
+ END REPEAT;
+END;
+$$
+BEGIN
+ REPEAT
+ SELECT 1;
+ UNTIL EXISTS (SELECT * FROM t1 WHERE a=1)
+ END REPEAT;
+END;
+$$
+
+BEGIN
+ FOR i IN 0..(1 IN (SELECT * FROM t1))
+ LOOP
+ SELECT i;
+ END LOOP;
+END;
+$$
+BEGIN
+ FOR i IN 0..EXISTS (SELECT * FROM t1 WHERE a=1)
+ LOOP
+ SELECT i;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+#
+# Subselects as SP variable default values
+#
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+DECLARE
+ a INT DEFAULT ((10) IN (SELECT * FROM t1));
+BEGIN
+ SELECT a;
+END;
+$$
+DECLARE
+ a INT DEFAULT EXISTS (SELECT * FROM t1);
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+#
+# Subselects SP function return values
+#
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT AS
+BEGIN
+ RETURN ((1) IN (SELECT * FROM t1));
+END;
+$$
+CREATE FUNCTION f2() RETURN INT AS
+BEGIN
+ RETURN EXISTS (SELECT * FROM t1 WHERE a=1);
+END;
+$$
+DELIMITER ;$$
+SELECT f1();
+SELECT f2();
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP TABLE t1;
+
+
+#
+# Subselects in CURSOR parameters
+#
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+DELIMITER $$;
+DECLARE
+ va INT;
+ CURSOR cur(amin INT) IS SELECT a FROM t1 WHERE a>amin ORDER BY a;
+BEGIN
+ OPEN cur(1 IN (SELECT * FROM t1));
+ FETCH cur INTO va;
+ SELECT va;
+ CLOSE cur;
+END;
+$$
+DECLARE
+ va INT;
+ CURSOR cur(amin INT) IS SELECT a FROM t1 WHERE a>amin ORDER BY a;
+BEGIN
+ OPEN cur(EXISTS (SELECT * FROM t1));
+ FETCH cur INTO va;
+ SELECT va;
+ CLOSE cur;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-goto-debug.test b/mysql-test/suite/compat/oracle/t/sp-goto-debug.test
new file mode 100644
index 00000000..0ded370b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-goto-debug.test
@@ -0,0 +1,178 @@
+-- source include/have_debug.inc
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-20667 Server crash on pop_cursor
+--echo #
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<< iac_err >>
+ RETURN;
+END//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<< iac_err >>
+ RETURN ;
+END//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ GOTO iac_err;
+<< iac_err >>
+ RETURN ;
+END//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur2 IS SELECT 'cur2' FROM DUAL;
+ BEGIN
+ SELECT 'cur2';
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur3 IS SELECT 'cur3' FROM DUAL;
+ BEGIN
+ SELECT 'cur3';
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur4 IS SELECT 'cur4' FROM DUAL;
+ BEGIN
+ SELECT 'cur4';
+ GOTO ret;
+ END;
+ END IF;
+ GOTO ret;
+ END;
+ END IF;
+ GOTO ret;
+ END;
+ END IF;
+<<ret>>
+ RETURN;
+END;
+//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1(lab VARCHAR(32)) IS
+BEGIN
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur2 IS SELECT 'cur2' FROM DUAL;
+ BEGIN
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur3 IS SELECT 'cur3' FROM DUAL;
+ BEGIN
+ IF 1=1 THEN
+ DECLARE
+ CURSOR cur4 IS SELECT 'cur4' FROM DUAL;
+ BEGIN
+ IF lab = 'cur4' THEN
+ SELECT 'goto from cur4' AS comment;
+ GOTO ret;
+ END IF;
+ END;
+ END IF;
+ IF lab = 'cur3' THEN
+ SELECT 'goto from cur3' AS comment;
+ GOTO ret;
+ END IF;
+ END;
+ END IF;
+ IF lab = 'cur2' THEN
+ SELECT 'goto from cur2' AS comment;
+ GOTO ret;
+ END IF;
+ END;
+ END IF;
+<<ret>>
+ RETURN;
+END;
+//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+CALL p1('');
+CALL p1('cur2');
+CALL p1('cur3');
+CALL p1('cur4');
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<<iac_err >>
+ RETURN;
+END//
+DELIMITER ;//
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-goto.test b/mysql-test/suite/compat/oracle/t/sp-goto.test
new file mode 100644
index 00000000..9c15d10b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-goto.test
@@ -0,0 +1,968 @@
+set sql_mode=oracle;
+--echo #
+--echo # MDEV-10697 sql_mode=ORACLE: GOTO statement
+--echo #
+
+--echo # matrice of tests in procedure
+--echo # |--------------------------------------------------------
+--echo # | | Same | Outside | to sub | No |
+--echo # | | block | one block | block | matching |
+--echo # | | | | | label |
+--echo # |--------------------------------------------------------
+--echo # | Forward jump | F1 | F3 | F5 | F7 |
+--echo # |--------------------------------------------------------
+--echo # | Backward jump | F2 | F4 | F6 | F8 |
+--echo # |--------------------------------------------------------
+--echo # Jump from handler to outside handling code block : F9
+--echo # Jump from handler to handling code block : F10 (forbidden)
+--echo # Jump inside handler : F21
+--echo # Jump between handler : F22 (forbidden)
+--echo # Jump from cascaded block with handler : F11
+--echo # Duplicate label in same block : F12 (forbidden)
+--echo # Duplicate label in different block : F13
+--echo # Jump outside unlabeled block : F14
+--echo # Jump inside/outside labeled block : F15
+--echo # Jump from if / else : F16
+--echo # Jump with cursors : F17
+--echo # Jump outside case : F18
+--echo # Jump inside/outside case block : F19
+--echo # Jump outside labeled loop : F20
+--echo # Jump (continue) labeled loop : F23
+--echo # Two consecutive label : P24
+--echo # Two consecutive label (backward and forward jump) : P25
+--echo # Two consecutive label, continue to wrong label : P26
+--echo # Consecutive goto label and block label : P27
+
+--echo # Test in function
+--echo # backward jump : func1
+--echo # forward jump : func2
+
+--echo # Test in trigger
+--echo # forward jump : trg1
+
+--echo #
+--echo # Forward jump in same block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f1(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ goto lab1;
+<<lab1>>
+ goto lab2;
+ p2:='b';
+<<lab2>>
+ return ;
+END;
+$$
+
+DELIMITER ;$$
+call f1(@wp1);
+select 'f1',@wp1;
+DROP PROCEDURE f1;
+
+--echo #
+--echo # Backward jump in same block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f2(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='b') then
+ return ;
+ end if;
+ p2:='b';
+ goto lab1;
+END;
+$$
+DELIMITER ;$$
+call f2(@wp1);
+select 'f2',@wp1;
+DROP PROCEDURE f2;
+
+--echo #
+--echo # Forward jump outside one block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f3(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ if (p2='a') then
+ goto lab1;
+ end if;
+ p2:='c';
+<<lab1>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+call f3(@wp1);
+select 'f3',@wp1;
+DROP PROCEDURE f3;
+
+--echo #
+--echo # Backward jump outside one block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f4(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='a') then
+ p2:=p2||'b';
+ goto lab1;
+ end if;
+ if (p2='ab') then
+ p2:=p2||'c';
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+call f4(@wp1);
+select 'f4',@wp1;
+DROP PROCEDURE f4;
+
+DELIMITER $$;
+--echo #
+--echo # Forward jump inside sub block
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f5(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+goto lab5 ;
+ if (p2='a') then
+<<lab5>>
+ p2:=p2||'b';
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Backward jump inside sub block
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f6(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ if (p2='a') then
+<<lab6>>
+ p2:=p2||'b';
+ return ;
+ end if;
+goto lab6 ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Backward jump - missing label
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f7(p2 IN OUT VARCHAR)
+AS
+BEGIN
+<<lab>>
+ goto lab7 ;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Forward jump - missing label
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f8(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ goto lab8 ;
+<<lab>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # Jump from handler to procedure code
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f9(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab9>>
+ if lim=-1 then
+ res:=res||' -- goto end limit -1 --';
+ goto lab9_end;
+ end if;
+
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought ---';
+ lim:=0;
+ goto lab9;
+ end;
+ WHEN NO_DATA_FOUND THEN
+ begin
+ res:=res||'--- no_data_found cought ---';
+ lim:=-1;
+ goto lab9;
+ end;
+ end;
+ res:=res||'error';
+<<lab9_end>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f9(2, @res);
+SELECT 'f9',@res;
+CALL f9(0, @res);
+SELECT 'f9',@res;
+DROP PROCEDURE f9;
+
+DELIMITER $$;
+--echo #
+--echo # Jump from handler to handling bloc
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f10(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ begin
+<<lab10>>
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:='--- too_many_rows cought ---';
+ goto lab10;
+ end;
+ WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
+ end;
+ return ;
+END;
+$$
+
+--echo #
+--echo # Jump from cascaded block with handler
+--echo #
+CREATE or replace procedure f11(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab11a>>
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought 1 ---';
+ goto lab11b;
+ end;
+ WHEN NO_DATA_FOUND THEN
+ begin
+ res:=res||'--- no_data_found cought 1 ---';
+ lim:=2;
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought 2 ---';
+ goto lab11a;
+ end;
+ WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought 2 ---';
+ end;
+ end;
+ set res:=res||' error ';
+<<lab11b>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f11(0, @res);
+SELECT 'f11',@res;
+DROP PROCEDURE f11;
+
+DELIMITER $$;
+--echo #
+--echo # Jump inside handler
+--echo #
+CREATE or replace procedure f21(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ <<retry>>
+ lim:=lim-1;
+ loop
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ lim:=lim-1;
+ goto retry;
+ end;
+ end;
+ exit ;
+ end loop;
+ end;
+ end;
+ res:=lim;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f21(10, @res);
+SELECT 'f21',@res;
+drop procedure f21;
+
+DELIMITER $$;
+--echo #
+--echo # Jump beetween handler
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f22(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ res:='ok';
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ goto nodata ;
+ WHEN NO_DATA_FOUND THEN
+ begin
+<<nodata>>
+ res:='error';
+ end;
+ end;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--echo #
+--echo # Duplicate label in same bloc
+--error 1309
+CREATE or replace procedure f12(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab12>>
+ res:='error';
+<<lab12>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # Duplicate label in different block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f13(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+<<lab13>>
+ a:=a+1;
+ begin
+ <<lab13>>
+ a:=a+1;
+ if (a<10) then
+ goto lab13;
+ end if;
+ end;
+ res:=a;
+ if (a=10) then
+ goto lab13;
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f13(0, @res);
+SELECT 'f13',@res;
+DROP PROCEDURE f13;
+
+
+--echo #
+--echo # Jump outside unlabeled block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f14(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+ loop
+ a:=a+1;
+ if (a<10) then
+ continue;
+ end if;
+ if (a>=lim) then
+ goto lab14;
+ end if;
+ if (a>=20) then
+ exit;
+ end if;
+ end loop;
+<<lab14>>
+ res:=a;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f14(15, @res);
+SELECT 'f14',@res;
+CALL f14(8, @res);
+SELECT 'f14',@res;
+CALL f14(25, @res);
+SELECT 'f14',@res;
+DROP PROCEDURE f14;
+
+--echo #
+--echo # Jump inside/outside labeled block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f15(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+ <<looplabel>> loop
+ <<beginlooplabel>>
+ a:=a+1;
+ if (a<10) then
+ continue looplabel;
+ end if;
+ if (a>=lim) then
+ goto lab15;
+ end if;
+ if (a>=20) then
+ exit looplabel;
+ end if;
+ goto beginlooplabel;
+ end loop;
+<<lab15>>
+ res:=a;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f15(15, @res);
+SELECT 'f15',@res;
+CALL f15(8, @res);
+SELECT 'f15',@res;
+CALL f15(25, @res);
+SELECT 'f15',@res;
+DROP PROCEDURE f15;
+
+--echo #
+--echo # Jump from if / else
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f16(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ if (lim<10) then
+ goto lab16_1;
+ else
+ goto lab16_2;
+ end if;
+<<lab16_1>>
+ res:='if lab16_1';
+ goto lab16_3;
+<<lab16_2>>
+ res:='else lab16_2';
+ goto lab16_3;
+ res:='error lab16_3';
+<<lab16_3>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f16(15, @res);
+SELECT 'f16',@res;
+CALL f16(8, @res);
+SELECT 'f16',@res;
+DROP PROCEDURE f16;
+
+--echo #
+--echo # Jump with cursors
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f17(lim INT, res OUT VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR cur1 IS SELECT 1 FROM dual where 1=2;
+BEGIN
+ OPEN cur1;
+ LOOP
+ FETCH cur1 INTO v_a;
+ EXIT WHEN cur1%NOTFOUND;
+ END LOOP;
+ CLOSE cur1;
+ <<lab17>>
+ lim:=lim-1;
+ begin
+ declare
+ CURSOR cur1 IS SELECT 1 FROM dual;
+ CURSOR cur2 IS SELECT 1 FROM dual where 1=2;
+ begin
+ LOOP
+ OPEN cur1;
+ FETCH cur1 INTO v_a;
+ EXIT WHEN cur1%NOTFOUND;
+ res:=res||'-'||lim ;
+ close cur1;
+ if (lim>0) then
+ goto lab17;
+ else
+ goto lab17_end;
+ end if;
+ END LOOP;
+ end;
+ <<lab17_end>>
+ null;
+ end;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f17(5, @res);
+SELECT 'f17',@res;
+DROP PROCEDURE f17;
+
+--echo #
+--echo # Jump outside case
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f18(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ case lim
+ when 1 then
+ res:='case branch 18_1';
+ goto lab18_1;
+ res:='error';
+ when 2 then
+ res:='case branch 18_2';
+ goto lab18_2;
+ res:='error';
+ else
+ res:='default branch 18';
+ end case;
+<<lab18_1>>
+ null;
+<<lab18_2>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f18(0, @res);
+SELECT 'f18',@res;
+CALL f18(1, @res);
+SELECT 'f18',@res;
+CALL f18(2, @res);
+SELECT 'f18',@res;
+DROP PROCEDURE f18;
+
+--echo #
+--echo # Jump inside/outside case block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f19(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=1;
+ case lim
+ when 1 then
+<<lab19_0>>
+ a:=a+1;
+ if (a<10) then
+ goto lab19_0;
+ else
+ goto lab19_1;
+ end if;
+ res:='case branch 19_1';
+ else
+ res:='default branch 18';
+ end case;
+ goto lab19_end;
+<<lab19_1>>
+ res:=a;
+<<lab19_end>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f19(1, @res);
+SELECT 'f19',@res;
+DROP PROCEDURE f19;
+
+DELIMITER $$;
+--echo #
+--echo # Jump outside labeled loop
+--echo #
+CREATE OR REPLACE PROCEDURE f20(res OUT VARCHAR)
+AS
+ a INT := 1;
+BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ IF i = 5 THEN
+ a:= a+1;
+ goto lab;
+ END IF;
+ END LOOP;
+ res:=a;
+END;
+$$
+DELIMITER ;$$
+CALL f20(@res);
+SELECT 'f20',@res;
+DROP PROCEDURE f20;
+
+DELIMITER $$;
+--echo #
+--echo # Jump (continue) labeled loop
+--echo #
+CREATE OR REPLACE PROCEDURE f23(res OUT VARCHAR)
+AS
+ a INT := 1;
+BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ IF i = 5 THEN
+ a:= a+1;
+ continue lab;
+ END IF;
+ END LOOP;
+ res:=a;
+END;
+$$
+DELIMITER ;$$
+CALL f23(@res);
+SELECT 'f23',@res;
+DROP PROCEDURE f23;
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label (backward jump)
+--echo #
+CREATE OR REPLACE PROCEDURE p24(action IN INT, res OUT varchar) AS
+ a integer;
+BEGIN
+ <<lab1>>
+ <<lab2>>
+ if (action = 1) then
+ res:=res||' '||action;
+ action:=2;
+ goto lab1;
+ end if;
+ if (action = 2) then
+ res:=res||' '||action;
+ action:=3;
+ goto lab2;
+ end if;
+END;
+$$
+DELIMITER ;$$
+call p24(1,@res);
+select 'p24',@res;
+DROP PROCEDURE p24;
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label (backward and forward jump)
+--echo #
+CREATE OR REPLACE PROCEDURE p25(action IN INT, res OUT varchar) AS
+ a integer;
+BEGIN
+ if (action = 1) then
+ res:=res||' '||action;
+ action:=2;
+ goto lab2;
+ end if;
+ goto lab_end;
+ <<lab1>>
+ <<lab2>>
+ res:=res||' '||action;
+ if (action = 2) then
+ res:=res||' '||action;
+ action:=3;
+ goto lab1;
+ end if;
+<<lab_end>>
+ null;
+END;
+$$
+DELIMITER ;$$
+call p25(1,@res);
+select 'p25',@res;
+DROP PROCEDURE p25;
+
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label, continue to wrong label
+--error ER_SP_LILABEL_MISMATCH
+CREATE OR REPLACE PROCEDURE p26(action IN INT, res OUT varchar) AS
+BEGIN
+ <<lab1>>
+ <<lab2>>
+ FOR i IN 1..10 LOOP
+ continue lab1;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Consecutive goto label and block label
+--echo #
+CREATE OR REPLACE PROCEDURE p27(action IN INT, res OUT varchar) AS
+BEGIN
+ res:='';
+ <<lab1>>
+ <<lab2>>
+ FOR i IN 1..10 LOOP
+ if (action = 1) then
+ res:=res||' '||action||'-'||i;
+ action:=2;
+ continue lab2;
+ end if;
+ if (action = 2) then
+ res:=res||' '||action||'-'||i;
+ action:='3';
+ goto lab2;
+ end if;
+ if (action = 3) then
+ res:=res||' '||action||'-'||i;
+ action:='4';
+ goto lab1;
+ end if;
+ if (action = 4) then
+ res:=res||' '||action||'-'||i;
+ exit lab2;
+ end if;
+ END LOOP;
+END;
+$$
+
+DELIMITER ;$$
+call p27(1,@res);
+select 'p27',@res;
+DROP PROCEDURE p27;
+
+--echo # ----------------------
+--echo # -- TEST IN FUNCTION --
+--echo # ----------------------
+
+--echo #
+--echo # FUNCTION : Backward jump
+--echo #
+DELIMITER $$;
+CREATE or replace function func1()
+return varchar
+AS
+ p2 varchar(10);
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='a') then
+ p2:=p2||'b';
+ goto lab1;
+ end if;
+ if (p2='ab') then
+ p2:=p2||'c';
+ end if;
+ return p2;
+END;
+$$
+DELIMITER ;$$
+select 'func1',func1();
+DROP function func1;
+
+--echo #
+--echo # FUNCTION : forward jump
+--echo #
+DELIMITER $$;
+CREATE or replace function func2()
+return varchar
+AS
+ p2 varchar(10);
+BEGIN
+ p2:='a';
+ if (p2='a') then
+ goto lab1;
+ end if;
+ p2:='b';
+<<lab1>>
+ return p2;
+END;
+$$
+DELIMITER ;$$
+select 'func2',func2();
+DROP function func2;
+
+--echo # ---------------------
+--echo # -- TEST IN TRIGGER --
+--echo # ---------------------
+
+--echo #
+--echo # TRIGGER : forward jump
+--echo #
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW
+BEGIN
+ IF :NEW.a IS NULL
+ THEN
+ :NEW.a:= 15;
+ goto end_trg;
+ END IF;
+ :NEW.a:= 10;
+<<end_trg>>
+ null;
+END;
+$$
+DELIMITER ;$$
+insert into t1 values (1);
+insert into t1 values (null);
+SELECT * FROM t1;
+DROP TRIGGER trg1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-20667 Server crash on pop_cursor
+--echo #
+
+CREATE TABLE t1 (a VARCHAR(6));
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<< iac_err >>
+ RETURN;
+END//
+DELIMITER ;//
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<< iac_err >>
+ RETURN ;
+END//
+DELIMITER ;//
+CALL p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CURSOR cur1 IS SELECT a FROM t1 ;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ GOTO iac_err;
+<< iac_err >>
+ RETURN ;
+END//
+DELIMITER ;//
+CALL p1;
+DROP PROCEDURE p1;
+
+
+DELIMITER //;
+CREATE PROCEDURE p1() IS
+BEGIN
+ IF 1=2 THEN
+ BEGIN
+ DECLARE
+ CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
+ BEGIN
+ GOTO iac_err;
+ END;
+ END;
+ END IF;
+ IF 1=1 THEN
+ GOTO iac_err;
+ END IF;
+<<iac_err >>
+ RETURN;
+END//
+DELIMITER ;//
+CALL p1;
+DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-inout.test b/mysql-test/suite/compat/oracle/t/sp-inout.test
new file mode 100644
index 00000000..d605be64
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-inout.test
@@ -0,0 +1,2501 @@
+--echo #
+--echo # MDEV-10654 IN, OUT, INOUT parameters in CREATE FUNCTION
+--echo #
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # CREATE PACKAGE with procedure and function with IN, OUT, INOUT qualifiers
+--echo # And SHOW CREATE PACKAGE
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT);
+ FUNCTION func_sub(d OUT INT, a IN INT, b IN INT, c INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(d, a, b, c);
+ d := d + c + res;
+ END;
+ FUNCTION func_sub(d OUT INT, a IN INT, b IN INT, c INOUT INT) RETURN INT
+ AS
+ BEGIN
+ c := c + 6;
+ d := 10;
+ RETURN a - b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SHOW CREATE PACKAGE pkg2;
+SHOW CREATE PACKAGE BODY pkg2;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # CREATE FUNCTION with IN, OUT, INOUT qualifiers
+--echo # SHOW CREATE FUNCTION
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE FUNCTION add_func(a IN INT, b IN INT, c OUT INT, d INOUT INT) RETURN INT
+AS
+ BEGIN
+ c := 100;
+ d := d + 1;
+ RETURN a + b;
+ END;
+$$
+DELIMITER ;$$
+
+SHOW CREATE FUNCTION add_func;
+DROP FUNCTION add_func;
+
+--echo #
+--echo # CREATE PROCEDURE with IN, OUT, INOUT qualifiers
+--echo # SHOW CREATE PROCEDURE
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE add_proc(a IN INT, b IN INT, c INOUT INT, d OUT INT)
+AS
+ BEGIN
+ d := a + b + c + d;
+ END;
+$$
+DELIMITER ;$$
+
+SHOW CREATE PROCEDURE add_proc;
+DROP PROCEDURE add_proc;
+
+--echo #
+--echo # Call function from SELECT query
+--echo # SELECT > FUNCTION(IN)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION add_func2 (a IN INT, b IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION add_func2(a IN INT, b IN INT) RETURN INT
+ AS
+ BEGIN
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+select pkg2.add_func2(@a, @b);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from SELECT query
+--echo # SELECT > FUNCTION(OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION add_func3 (a IN INT, b IN INT, c OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION add_func3(a IN INT, b IN INT, c OUT INT) RETURN INT
+ AS
+ BEGIN
+ c := 100;
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.add_func3(@a, @b, @c);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from SELECT query
+--echo # SELECT > FUNCTION(INOUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION add_func4 (a IN INT, b IN INT, c OUT INT, d INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION add_func4(a IN INT, b IN INT, c OUT INT, d INOUT INT) RETURN INT
+ AS
+ BEGIN
+ c := 100;
+ d := d + 1;
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+set @d = 9;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.add_func4(@a, @b, @c, @d);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call from procedure
+--echo # PROCEDURE(OUT) > FUNCTION(IN)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE add_proc2 (a IN INT, b IN INT, c OUT INT);
+ FUNCTION add_func2 (a IN INT, b IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE add_proc2(a IN INT, b IN INT, c OUT INT)
+ AS
+ BEGIN
+ c := add_func2(a, b);
+ END;
+
+ FUNCTION add_func2(a IN INT, b IN INT) RETURN INT
+ AS
+ BEGIN
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+call pkg2.add_proc2(@a, @b, @c);
+select @c;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call from procedure
+--echo # PROCEDURE(OUT) > FUNCTION(OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE add_proc3 (a IN INT, b IN INT, c OUT INT);
+ FUNCTION add_func3 (a IN INT, b IN INT, c OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE add_proc3(a IN INT, b IN INT, c OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := add_func3(a, b, c);
+ END;
+ FUNCTION add_func3(a IN INT, b IN INT, c OUT INT) RETURN INT
+ AS
+ BEGIN
+ c := 100;
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+call pkg2.add_proc3(@a, @b, @c);
+select @c;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call from procedure
+--echo # PROCEDURE(OUT) > FUNCTION(INOUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE add_proc4 (a IN INT, b IN INT, c OUT INT);
+ FUNCTION add_func4 (a IN INT, b IN INT, c OUT INT, d INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE add_proc4(a IN INT, b IN INT, res OUT INT)
+ AS
+ c INT;
+ d INT;
+ BEGIN
+ d := 30;
+ res := add_func4(a, b, c, d);
+ res := c + d;
+ END;
+ FUNCTION add_func4(a IN INT, b IN INT, c OUT INT, d INOUT INT) RETURN INT
+ AS
+ BEGIN
+ c := 100;
+ d := d + 1;
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @res = 0;
+call pkg2.add_proc4(@a, @b, @res);
+select @res;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call from procedure
+--echo # PROCEDURE(OUT) > PROCEDURE(OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE test_proc1 (a IN INT, b IN INT, c OUT INT);
+ PROCEDURE add_proc (a IN INT, b IN INT, c OUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE test_proc1(a IN INT, b IN INT, c OUT INT)
+ AS
+ BEGIN
+ call pkg2.add_proc(a, b, c);
+ END;
+ PROCEDURE add_proc(a IN INT, b IN INT, c OUT INT)
+ AS
+ BEGIN
+ c := a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+call pkg2.test_proc1(@a, @b, @c);
+select @c;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Argument's order change
+--echo # PROCEDURE(a IN, b IN, c OUT) > FUNCTION(b IN, a IN, c OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c OUT INT);
+ FUNCTION func_sub(b IN INT, a IN INT, c OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(b, a, c);
+ END;
+ FUNCTION func_sub(b IN INT, a IN INT, c OUT INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ c := a - b;
+ res := a;
+ RETURN res;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+call pkg2.proc_main(@a, @b, @c);
+select @c;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Argument's order change
+--echo # PROCEDURE(a IN, b IN, c OUT) > FUNCTION(c OUT, b IN, a IN)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c OUT INT);
+ FUNCTION func_sub(c OUT INT, b IN INT, a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(c, b, a);
+ END;
+ FUNCTION func_sub(c OUT INT, b IN INT, a IN INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ c := a - b;
+ res := a;
+ RETURN res;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+call pkg2.proc_main(@a, @b, @c);
+select @c;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Argument's order change
+--echo # PROCEDURE(a IN, b IN, c INOUT, d OUT) > FUNCTION(d OUT, a IN, b IN, c INOUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT);
+ FUNCTION func_sub(d OUT INT, a IN INT, b IN INT, c INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(d, a, b, c);
+ d := d + c + res;
+ END;
+ FUNCTION func_sub(d OUT INT, a IN INT, b IN INT, c INOUT INT) RETURN INT
+ AS
+ BEGIN
+ c := c + 6;
+ d := 10;
+ RETURN a - b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 15;
+set @b = 5;
+set @c = 4;
+set @d= 0;
+call pkg2.proc_main(@a, @b, @c, @d);
+select @d;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Argument's order change
+--echo # PROCEDURE(a IN INT, b IN INT, c INOUT INT, d OUT INT) > FUNCTION1(c INOUT INT, b IN INT) > FUNCTION2(d OUT INT, a IN INT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT);
+ FUNCTION func_sub1(c INOUT INT, b IN INT) RETURN INT;
+ FUNCTION func_sub2(d OUT INT, a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b IN INT, c INOUT INT, d OUT INT)
+ AS
+ res1 INT;
+ res2 INT;
+ BEGIN
+ res1 := func_sub1(c, b);
+ res2 := func_sub2(d, a);
+ d := d + c;
+ END;
+ FUNCTION func_sub1(c INOUT INT, b IN INT) RETURN INT
+ AS
+ BEGIN
+ c := c + b;
+ RETURN 0;
+ END;
+ FUNCTION func_sub2(d OUT INT, a IN INT) RETURN INT
+ AS
+ BEGIN
+ d := 5 + a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 15;
+set @b = 6;
+set @c = 4;
+set @d= 0;
+call pkg2.proc_main(@a, @b, @c, @d);
+select @d;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Argument's order change
+--echo # FUNCTION1(a IN, b IN) > FUNCTION2(b IN, c OUT, a IN)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT, b IN INT) RETURN INT;
+ FUNCTION func_sub(b IN INT, c OUT INT, a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT, b IN INT) RETURN INT
+ AS
+ c INT;
+ res INT;
+ BEGIN
+ res := func_sub(b, c, a);
+ RETURN res + c;
+ END;
+ FUNCTION func_sub(b IN INT, c OUT INT, a IN INT) RETURN INT
+ AS
+ BEGIN
+ c := 100;
+ RETURN a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+select pkg2.func_main(@a, @b);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call procedure inside function
+--echo # FUNCTION1(a IN, b IN) > PROCEDURE(a IN, b IN, c OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT) RETURN INT;
+ PROCEDURE proc_sub(a IN INT, b IN INT, c OUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT) RETURN INT
+ AS
+ c INT;
+ BEGIN
+ call proc_sub(a, b, c);
+ RETURN c;
+ END;
+ PROCEDURE proc_sub(a IN INT, b IN INT, c OUT INT)
+ AS
+ BEGIN
+ c := a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+select pkg2.func_main(@a, @b);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call procedure inside function
+--echo # FUNCTION1(a IN, b IN) > PROCEDURE(a IN, b INOUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT) RETURN INT;
+ PROCEDURE proc_sub(a IN INT, b INOUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT) RETURN INT
+ AS
+ BEGIN
+ call proc_sub(a, b);
+ RETURN b;
+ END;
+ PROCEDURE proc_sub(a IN INT, b INOUT INT)
+ AS
+ BEGIN
+ b := a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+select pkg2.func_main(@a, @b);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call procedure inside function
+--echo # FUNCTION1(a IN, b IN, c OUT) > PROCEDURE(a IN, b IN, c OUT)
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT, c OUT INT) RETURN INT;
+ PROCEDURE proc_sub(a IN INT, b IN INT, c OUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(b IN INT, a IN INT, c OUT INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ call proc_sub(a, b, c);
+ RETURN 0;
+ END;
+ PROCEDURE proc_sub(a IN INT, b IN INT, c OUT INT)
+ AS
+ BEGIN
+ c := a + b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+set @b = 3;
+set @c = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a, @b, @c);
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from UPDATE query
+--echo # UPDATE <table> SET <column> = FUNCTION(a IN)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT
+ AS
+ BEGIN
+ RETURN a * 10;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 5;
+UPDATE Persons SET Age = pkg2.func(@a) WHERE ID = 1;
+SELECT * FROM Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from UPDATE query
+--echo # UPDATE <table> SET <column> = FUNCTION(a OUT)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 5;
+ RETURN 80;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+UPDATE Persons SET Age = pkg2.func(@a) WHERE ID = 1;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from INSERT query
+--echo # INSERT INTO <table> SELECT <val1>, <val2>, FUNCTION(a IN)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT
+ AS
+ BEGIN
+ RETURN a * 10;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 4;
+INSERT INTO Persons SELECT 4, 'DDD', PKG2.func(@a);
+SELECT * FROM Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from INSERT query
+--echo # INSERT INTO <table> SELECT <val1>, <val2>, FUNCTION(a OUT)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 45;
+ RETURN 40;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+set @a = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+INSERT INTO Persons SELECT 5, 'EEE', PKG2.func(@a);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from DELETE query
+--echo # DELETE FROM <table> WHERE <column> = FUNCTION(a IN)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT
+ AS
+ BEGIN
+ RETURN a;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+set @a = 4;
+DELETE FROM Persons WHERE ID = PKG2.func(@a);
+SELECT * FROM Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Call function from DELETE query
+--echo # DELETE FROM <table> WHERE <column> = FUNCTION(a OUT)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 40;
+ RETURN 4;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+set @a = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+DELETE FROM Persons WHERE ID = PKG2.func(@a);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # SELECT query inside function
+--echo # FUNCTION(a IN) > SELECT … FROM <table>
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ c INT;
+ BEGIN
+ SELECT AGE INTO c FROM Persons WHERE ID = a;
+ RETURN c;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 3;
+select pkg2.func_main(@a);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # SELECT query inside function
+--echo # FUNCTION(a OUT) > SELECT … FROM <table>
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ SELECT AGE INTO a FROM Persons WHERE ID = 3;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # SELECT query inside function
+--echo # FUNCTION(a INOUT) > SELECT … FROM <table>
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a INOUT INT) RETURN INT
+ AS
+ BEGIN
+ SELECT AGE INTO a FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 1;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # SELECT query inside function
+--echo # FUNCTION(a IN) > FUNCTION(a IN, b OUT) > SELECT … FROM <table>
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ b INT;
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ RETURN b;
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 2;
+select pkg2.func_main(@a);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # UPDATE query inside function
+--echo # FUNCTION(a IN) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ c INT;
+ BEGIN
+ UPDATE Persons SET AGE = 50 WHERE ID = a;
+
+ SELECT AGE INTO c FROM Persons WHERE ID = a;
+ RETURN c;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 5;
+select pkg2.func_main(@a);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # UPDATE query inside function
+--echo # FUNCTION(a IN, b OUT) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE Persons SET AGE = 60 WHERE ID = a;
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 5;
+set @b = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a, @b);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # UPDATE query inside function
+--echo # FUNCTION(a IN, b INOUT) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT, b INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT, b INOUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE Persons SET AGE = 60 WHERE ID = a;
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+set @a = 5;
+set @b = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a, @b);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # UPDATE query inside function
+--echo # FUNCTION(a IN) > FUNCTION(a IN, b OUT) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 80);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ b INT;
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ RETURN b;
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE Persons SET AGE = 10 WHERE ID = a;
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 1;
+select pkg2.func_main(@a);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # INSERT query inside function
+--echo # FUNCTION(a IN) > INSERT INTO <table> VALUES …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 50);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ b INT;
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'FFF', 60);
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+--disable_ps2_protocol
+select pkg2.func_main(@a);
+--enable_ps2_protocol
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # INSERT query inside function
+--echo # FUNCTION(a IN, b OUT) > INSERT INTO <table> VALUES …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 50);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'FFF', 60);
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+set @b = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a, @b);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # INSERT query inside function
+--echo # FUNCTION(a IN, b INOUT) > INSERT INTO <table> VALUES …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT, b INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT, b INOUT INT) RETURN INT
+ AS
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'FFF', 60);
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+set @b = 0;
+--error ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
+select pkg2.func_main(@a, @b);
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # INSERT query inside function
+--echo # FUNCTION(a IN) > FUNCTION(a IN, b OUT) > INSERT INTO <table> VALUES …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'EEE', 40);
+
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func_main(a IN INT) RETURN INT
+ AS
+ b INT;
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ RETURN b;
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'FFF', 60);
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+--disable_ps2_protocol
+select pkg2.func_main(@a);
+--enable_ps2_protocol
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(IN) > SELECT FROM <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ BEGIN
+ b := func_sub(a);
+ END;
+ FUNCTION func_sub(a IN INT) RETURN INT
+ AS
+ b INT;
+ BEGIN
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN b;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 2;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select @b;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(OUT) > SELECT FROM <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ SELECT AGE INTO b FROM Persons WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 1;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select @b;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(INOUT) > SELECT FROM <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ c INT;
+ res INT;
+ BEGIN
+ c := 5;
+ res := func_sub(a, c);
+ b := c;
+ END;
+ FUNCTION func_sub(a IN INT, c INOUT INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ SELECT AGE INTO res FROM Persons WHERE ID = a;
+ c := c * 100;
+ RETURN res;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 2;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select @b;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(IN) > INSESRT INTO <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ BEGIN
+ b := func_sub(a);
+ END;
+ FUNCTION func_sub(a IN INT) RETURN INT
+ AS
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'FFF', 50);
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 5;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(OUT) > INSESRT INTO <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'FFF', 50);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'GGG', 60);
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(INOUT) > INSESRT INTO <table> …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'FFF', 50);
+INSERT INTO Persons VALUES (6, 'GGG', 60);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ c INT;
+ res INT;
+ BEGIN
+ c := 5;
+ res := func_sub(a, c);
+ b := c;
+ END;
+ FUNCTION func_sub(a IN INT, c INOUT INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ INSERT INTO Persons VALUE (a, 'HHH', 70);
+ c := c * 100;
+ RETURN res;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 7;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(IN) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'FFF', 50);
+INSERT INTO Persons VALUES (6, 'GGG', 60);
+INSERT INTO Persons VALUES (7, 'HHH', 70);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ BEGIN
+ b := func_sub(a);
+ END;
+ FUNCTION func_sub(a IN INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE Persons SET AGE = 100 WHERE ID = a;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 5;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(OUT) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'FFF', 100);
+INSERT INTO Persons VALUES (6, 'GGG', 60);
+INSERT INTO Persons VALUES (7, 'HHH', 70);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func_sub(a, b);
+ END;
+ FUNCTION func_sub(a IN INT, b OUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE Persons SET AGE = 100 WHERE ID = a;
+ b := 1;
+ RETURN 0;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 6;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # PROCEDURE > FUNCTION > SQL query
+--echo # PROCEDURE(OUT) > FUNCTION(INOUT) > UPDATE <table> SET …
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+INSERT INTO Persons VALUES (4, 'DDD', 40);
+INSERT INTO Persons VALUES (5, 'FFF', 100);
+INSERT INTO Persons VALUES (6, 'GGG', 100);
+INSERT INTO Persons VALUES (7, 'HHH', 70);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT);
+ FUNCTION func_sub(a IN INT, b INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc_main(a IN INT, b OUT INT)
+ AS
+ c INT;
+ res INT;
+ BEGIN
+ c := 5;
+ res := func_sub(a, c);
+ b := c;
+ END;
+ FUNCTION func_sub(a IN INT, c INOUT INT) RETURN INT
+ AS
+ res INT;
+ BEGIN
+ UPDATE Persons SET AGE = 100 WHERE ID = a;
+ c := c * 100;
+ RETURN res;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+select * from Persons;
+set @a = 7;
+set @b = 0;
+call pkg2.proc_main(@a, @b);
+select * from Persons;
+DROP TABLE Persons;
+DROP PACKAGE pkg2;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 20 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > FUNCTION(IN) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a IN INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+DECLARE
+ a INT;
+ res INT;
+BEGIN
+ a := 10;
+ res := 0;
+ res := pkg2.func(a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 30 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > FUNCTION(OUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 40);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ a := 100;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+DECLARE
+ a INT;
+ res INT;
+BEGIN
+ a := 10;
+ res := 0;
+ res := pkg2.func(a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 50 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > FUNCTION(INOUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 50);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ FUNCTION func(a INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ FUNCTION func(a INOUT INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ a := 100;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+DECLARE
+ a INT;
+ res INT;
+BEGIN
+ a := 10;
+ res := 0;
+ res := pkg2.func(a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 60 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(IN) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a IN INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a IN INT)
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 30 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(OUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a OUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a OUT INT)
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 50 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(INOUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a INOUT INT);
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a INOUT INT)
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ a := 100;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ set @a = 2;
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 50 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(OUT) > FUNCTION(IN) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a OUT INT);
+ FUNCTION func(a IN INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a OUT INT)
+ AS
+ res INT;
+ BEGIN
+ a := 100;
+ res := func(a);
+ END;
+ FUNCTION func(a IN INT) RETURN INT
+ AS
+ BEGIN
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 60 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(OUT) > FUNCTION(OUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a OUT INT);
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a OUT INT)
+ AS
+ res INT;
+ BEGIN
+ a := 100;
+ res := func(a);
+ END;
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 200;
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 80 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(OUT) > FUNCTION(INOUT) > UPDATE TABLE2
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a OUT INT);
+ FUNCTION func(a INOUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a OUT INT)
+ AS
+ res INT;
+ BEGIN
+ a := 100;
+ res := func(a);
+ END;
+ FUNCTION func(a INOUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 200;
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 90 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+--echo #
+--echo # Trigger
+--echo # TRIGGER AFTER UPDATE ON TABLE1 > PROCEDURE(OUT) > FUNCTION(OUT) > UPDATE TABLE2 with OUT argument (to check if OUT is returning by reference)
+--echo #
+
+CREATE TABLE Persons (
+ ID int,
+ Name varchar(255),
+ Age int
+);
+INSERT INTO Persons VALUES (1, 'AAA', 10);
+INSERT INTO Persons VALUES (2, 'BBB', 20);
+INSERT INTO Persons VALUES (3, 'CCC', 30);
+
+CREATE TABLE PersonsLog (
+ UpdateCount int
+);
+INSERT INTO PersonsLog VALUES (0);
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg2
+AS
+ PROCEDURE proc(a OUT INT);
+ FUNCTION func(a OUT INT) RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg2
+AS
+ PROCEDURE proc(a OUT INT)
+ AS
+ res INT;
+ BEGIN
+ res := func(a);
+ UPDATE PersonsLog SET UpdateCount = a;
+ END;
+ FUNCTION func(a OUT INT) RETURN INT
+ AS
+ BEGIN
+ a := 111;
+ UPDATE PersonsLog SET UpdateCount = UpdateCount+1;
+ RETURN 0;
+ END;
+END;
+$$
+CREATE OR REPLACE TRIGGER my_trigger
+AFTER UPDATE ON Persons
+FOR EACH ROW
+BEGIN
+ call pkg2.proc(@a);
+END;
+$$
+DELIMITER ;$$
+
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+UPDATE Persons SET Age = 80 WHERE ID = 1;
+SELECT * FROM Persons;
+SELECT * FROM PersonsLog;
+DROP TRIGGER my_trigger;
+DROP PACKAGE pkg2;
+DROP TABLE Persons;
+DROP TABLE PersonsLog;
+
+
+--echo #
+--echo # Package BODY variables as OUT parameters
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ FUNCTION f1(b IN OUT INT) RETURN INT;
+ FUNCTION show_private_variables() RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ pa INT:= 0;
+ pb INT:= 10;
+ FUNCTION f1(b IN OUT INT) RETURN INT AS
+ BEGIN
+ b:= b + 100;
+ RETURN 500+b-100;
+ END;
+
+ FUNCTION show_private_variables() RETURN TEXT AS
+ BEGIN
+ RETURN 'Private variables: pa=' || pa || ' pb=' || pb;
+ END;
+BEGIN
+ SET pa=f1(pb);
+END;
+$$
+DELIMITER ;$$
+SELECT pkg1.show_private_variables();
+DROP PACKAGE pkg1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-memory-leak.test b/mysql-test/suite/compat/oracle/t/sp-memory-leak.test
new file mode 100644
index 00000000..015169c2
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-memory-leak.test
@@ -0,0 +1,35 @@
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+--echo #
+--echo # MDEV-26186 280 Bytes lost in mysys/array.c, mysys/hash.c, sql/sp.cc, sql/sp.cc, sql/item_create.cc, sql/item_create.cc, sql/sql_yacc.yy:10748 when using oracle sql_mode
+--echo #
+
+SET sql_mode= 'oracle';
+--error ER_SP_LILABEL_MISMATCH
+BEGIN CONTINUE WHEN f0();
+
+SET sql_mode= 'oracle';
+--error ER_SP_LILABEL_MISMATCH
+BEGIN CONTINUE label WHEN f0();
+
+SET sql_mode= 'oracle';
+--error ER_SP_LILABEL_MISMATCH
+BEGIN EXIT WHEN f0();
+
+SET sql_mode= 'oracle';
+--error ER_SP_LILABEL_MISMATCH
+BEGIN EXIT label WHEN f0();
+
+SET sql_mode= 'oracle';
+--error ER_PARSE_ERROR
+--query WHILE f(8)<1 DO SELECT 1;
+
+SET sql_mode= 'oracle';
+--error ER_SP_BADRETURN
+BEGIN DECLARE CONTINUE HANDLER FOR SQLEXCEPTION RETURN f0();
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-code.test b/mysql-test/suite/compat/oracle/t/sp-package-code.test
new file mode 100644
index 00000000..9cca5396
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-code.test
@@ -0,0 +1,182 @@
+-- source include/have_debug.inc
+
+SET sql_mode=ORACLE;
+
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p2show;
+ PROCEDURE p2public;
+ FUNCTION f2public RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ a INT:=10;
+ PROCEDURE p1 AS
+ b INT:=20;
+ BEGIN
+ b:=a;
+ b:=a+1;
+ a:=b;
+ a:=b+1;
+ a:=a+1;
+ SET @a:=@a+2;
+ SELECT f1() FROM DUAL;
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN a;
+ END;
+ PROCEDURE p2private AS
+ BEGIN
+ SELECT 'This is p2private';
+ END;
+ PROCEDURE p2public AS
+ BEGIN
+ SELECT 'This is p2public';
+ END;
+ FUNCTION f2private RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2private';
+ END;
+ FUNCTION f2public RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2public';
+ END;
+ PROCEDURE p2show AS
+ BEGIN
+ SHOW FUNCTION CODE f2public;
+ SHOW FUNCTION CODE f2private;
+ SHOW PROCEDURE CODE p2public;
+ SHOW PROCEDURE CODE p2private;
+ SHOW PROCEDURE CODE p2show;
+ END;
+BEGIN
+ a:=a+1;
+ DECLARE
+ b INT;
+ BEGIN
+ b:=a;
+ b:=a+1;
+ a:=b;
+ a:=b+1;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+SHOW PROCEDURE CODE pkg1.p1;
+SHOW FUNCTION CODE pkg1.f1;
+SHOW PACKAGE BODY CODE pkg1;
+CALL pkg1.p2show;
+
+DROP PACKAGE pkg1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ a t1.a%TYPE:=10;
+ PROCEDURE p1 AS
+ b t1.a%TYPE:=20;
+ BEGIN
+ b:=a;
+ b:=a+1;
+ b:=b+1;
+ a:=b;
+ a:=b+1;
+ a:=a+1;
+ END;
+BEGIN
+ a:=a+1;
+ DECLARE
+ b t1.a%TYPE;
+ BEGIN
+ b:=a;
+ b:=a+1;
+ a:=b;
+ a:=b+1;
+ END;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE pkg1.p1;
+SHOW PACKAGE BODY CODE pkg1;
+DROP PACKAGE pkg1;
+DROP TABLE t1;
+
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ a ROW(a INT,b TEXT):=ROW(10,'x10');
+ PROCEDURE p1 AS
+ b ROW(a INT,b TEXT):=ROW(20,'x20');
+ BEGIN
+ b:=a;
+ a:=b;
+ b.a:=a.a+1;
+ a.a:=b.a+1;
+ a.a:=a.a+1;
+ END;
+BEGIN
+ a.a:=a.a+1;
+ DECLARE
+ b ROW(a INT,b TEXT):=ROW(30,'x30');
+ BEGIN
+ b:=a;
+ b.a:=a.a+1;
+ a:=b;
+ a.a:=b.a+1;
+ END;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE pkg1.p1;
+SHOW PACKAGE BODY CODE pkg1;
+DROP PACKAGE pkg1;
+
+
+CREATE TABLE t1 (a INT, b TEXT);
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ a t1%ROWTYPE:=ROW(10,'x10');
+ PROCEDURE p1 AS
+ b t1%ROWTYPE:=ROW(20,'x20');
+ BEGIN
+ b:=a;
+ a:=b;
+ b.a:=a.a+1;
+ a.a:=b.a+1;
+ a.a:=a.a+1;
+ END;
+BEGIN
+ a.a:=a.a+1;
+ DECLARE
+ b t1%ROWTYPE:=ROW(30,'x30');
+ BEGIN
+ b:=a;
+ b.a:=a.a+1;
+ a:=b;
+ a.a:=b.a+1;
+ END;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE pkg1.p1;
+SHOW PACKAGE BODY CODE pkg1;
+DROP PACKAGE pkg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-db.test b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-db.test
new file mode 100644
index 00000000..0528e6cb
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-db.test
@@ -0,0 +1,6 @@
+--echo #
+--echo # MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+--echo #
+
+SET @object_type='db';
+--source sp-package-concurrent-dml.inc
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-package.test b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-package.test
new file mode 100644
index 00000000..0f1a0ef3
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-package.test
@@ -0,0 +1,10 @@
+--echo #
+--echo # MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+--echo #
+
+SET @object_type='package_replace_pkg1';
+--source sp-package-concurrent-dml.inc
+
+SET @object_type='package_body_replace_pkg1';
+--source sp-package-concurrent-dml.inc
+
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-trigger.test b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-trigger.test
new file mode 100644
index 00000000..09ba1d70
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-trigger.test
@@ -0,0 +1,6 @@
+--echo #
+--echo # MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+--echo #
+
+SET @object_type='trigger';
+--source sp-package-concurrent-dml.inc
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-view.test b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-view.test
new file mode 100644
index 00000000..d2c2041a
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml-view.test
@@ -0,0 +1,6 @@
+--echo #
+--echo # MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+--echo #
+
+SET @object_type='view';
+--source sp-package-concurrent-dml.inc
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml.inc b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml.inc
new file mode 100644
index 00000000..8ee96d1e
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-concurrent-dml.inc
@@ -0,0 +1,107 @@
+--echo #
+--echo # Start of sp-package-concurrent-dml.inc
+--echo #
+
+--source include/count_sessions.inc
+
+let $object_type= `SELECT @object_type`;
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p2 AS
+ BEGIN
+ SELECT 'This is p2' AS msg;
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is p1' AS msg;
+ DO GET_LOCK('mdev15070',120);
+ CALL p2();
+ DO RELEASE_LOCK('mdev15070');
+ END;
+END;
+$$
+DELIMITER ;$$
+
+connect (con2,localhost,root);
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+
+connection default;
+send CALL pkg1.p1;
+
+connection con2;
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE state = "User lock" AND info LIKE "%GET_LOCK%mdev15070%";
+--source include/wait_condition.inc
+
+
+if ($object_type==view)
+{
+ CREATE VIEW v1 AS SELECT 1 AS c;
+ DROP VIEW v1;
+}
+
+
+if ($object_type==package_replace_pkg1)
+{
+ SET sql_mode=ORACLE;
+ DELIMITER $$;
+ CREATE OR REPLACE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ END;
+ $$
+ DELIMITER ;$$
+ DROP PACKAGE pkg1;
+}
+
+
+if ($object_type==package_body_replace_pkg1)
+{
+ SET sql_mode=ORACLE;
+ DELIMITER $$;
+ CREATE OR REPLACE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is p1 version 2' AS msg;
+ END;
+ END;
+ $$
+ DELIMITER ;$$
+ DROP PACKAGE pkg1;
+}
+
+
+if ($object_type==trigger)
+{
+ CREATE TABLE t1 (a INT);
+ CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=1;
+ DROP TRIGGER tr1;
+ DROP TABLE t1;
+}
+
+
+if ($object_type=='db')
+{
+ CREATE DATABASE test1;
+ CREATE FUNCTION test1.f1() RETURNS INT RETURN 10;
+ DROP DATABASE test1;
+}
+
+
+DO RELEASE_LOCK('mdev15070');
+
+disconnect con2;
+
+connection default;
+reap;
+
+DROP PACKAGE IF EXISTS pkg1;
+
+--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-i_s.test b/mysql-test/suite/compat/oracle/t/sp-package-i_s.test
new file mode 100644
index 00000000..a355e484
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-i_s.test
@@ -0,0 +1,69 @@
+--source include/default_charset.inc
+
+--echo #
+--echo # Start of 10.5 tests
+--echo #
+
+
+--echo #
+--echo # MDEV-30662 SQL/PL package body does not appear in I_S.ROUTINES.ROUTINE_DEFINITION
+--echo #
+
+# Testing a package without the executable section
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg1 AS
+ FUNCTION f1() RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ FUNCTION f1() RETURN INT AS
+ BEGIN
+ RETURN 1;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+--vertical_results
+SELECT routine_name, routine_type, routine_definition
+FROM information_schema.routines
+WHERE routine_type LIKE 'PACKAGE%'
+ORDER BY routine_type;
+--horizontal_results
+
+DROP PACKAGE pkg1;
+
+# Testing a package with the executable section
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg1 AS
+ FUNCTION f1() RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ FUNCTION f1() RETURN INT AS
+ BEGIN
+ RETURN 1;
+ END;
+BEGIN
+ SET @a=10;
+ SET @a=f1();
+END;
+$$
+DELIMITER ;$$
+
+--vertical_results
+SELECT routine_name, routine_type, routine_definition
+FROM information_schema.routines
+WHERE routine_type LIKE 'PACKAGE%'
+ORDER BY routine_type;
+--horizontal_results
+
+DROP PACKAGE pkg1;
+
+--echo #
+--echo # End of 10.5 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-innodb.test b/mysql-test/suite/compat/oracle/t/sp-package-innodb.test
new file mode 100644
index 00000000..94c7b714
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-innodb.test
@@ -0,0 +1,66 @@
+-- source include/have_innodb.inc
+
+SET default_storage_engine=InnoDB;
+
+SET sql_mode=ORACLE;
+
+CREATE TABLE t1 (a INT, routine TEXT);
+SELECT ENGINE FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
+INSERT INTO t1 VALUES (10,'none');
+
+--enable_prepare_warnings
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ a INT;
+ PROCEDURE p1 AS
+ BEGIN
+ a:=a+1;
+ INSERT INTO t1 VALUES (a,'p1');
+ END;
+BEGIN
+ SELECT MAX(t1.a) FROM t1 INTO a;
+ a:=a+1;
+ INSERT INTO t1 VALUES (a,'pkg1 initialization');
+END;
+$$
+DELIMITER ;$$
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+DELETE FROM t1;
+
+--source sp-cache-invalidate.inc
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+ROLLBACK;
+SELECT * FROM t1 ORDER BY a;
+DELETE FROM t1;
+
+--source sp-cache-invalidate.inc
+INSERT INTO t1 VALUES (20,'none');
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+COMMIT;
+SELECT * FROM t1 ORDER BY a;
+DELETE FROM t1;
+
+--source sp-cache-invalidate.inc
+INSERT INTO t1 VALUES (20,'none');
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+ROLLBACK;
+SELECT * FROM t1 ORDER BY a;
+DELETE FROM t1;
+
+--disable_prepare_warnings
+
+DROP PACKAGE pkg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-mdl.test b/mysql-test/suite/compat/oracle/t/sp-package-mdl.test
new file mode 100644
index 00000000..de4f7aaa
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-mdl.test
@@ -0,0 +1,110 @@
+--source include/have_metadata_lock_info.inc
+
+#
+# This test demonstrates that:
+# - A call to a package routine acquires a shared MDL lock on the entire package
+# - "DROP PACKAGE" waits until the currently running package routines end
+#
+
+SET sql_mode=ORACLE;
+DO GET_LOCK('lock',300);
+
+
+#
+# conn1 will execute package pkg1 routines and
+# and therefore acquire a shared MDL on "package body pkg1"
+#
+
+connect (conn1,localhost,root,,);
+SET sql_mode=ORACLE;
+let $conn1_id= `SELECT CONNECTION_ID()`;
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ DO GET_LOCK('lock',300);
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ CALL p1;
+ RETURN 1;
+ END;
+END;
+$$
+DELIMITER ;$$
+send SELECT pkg1.f1();
+
+#
+# wait for conn1 to actually start execution of pkg1.p1
+#
+
+connection default;
+let $wait_timeout= 60;
+let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+WHERE ID=$conn1_id AND INFO LIKE '%GET_LOCK%' AND STATE='User lock';
+--source include/wait_condition.inc
+
+
+#
+# conn2 will do "DROP PACKAGE pkg1".
+# It will acquire an exclusive MDL on "package body pkg1", and therefore
+# it should wait until conn1 ends the package routine execution
+#
+
+connect (conn2,localhost,root,,);
+let $conn2_id= `SELECT CONNECTION_ID()`;
+SET sql_mode=ORACLE;
+send DROP PACKAGE pkg1;
+
+#
+# wait for conn2 to actually enter the "DROP" statement and get locked by conn1
+#
+connection default;
+let $wait_timeout= 60;
+let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+WHERE ID=$conn2_id
+ AND INFO LIKE '%DROP PACKAGE%'
+ AND STATE='Waiting for stored package body metadata lock';
+--source include/wait_condition.inc
+
+#
+# Now we have three threads involved.
+# The following I_S query will check that the threads are in these states:
+#
+# default (0) - is holding a user lock 'lock'
+# conn1 (1) - is executing the package procedure test.pkg1.p1,
+# is holding a shared MDL on 'package body pkg1',
+# is waiting for the user lock 'lock' to be released
+# conn2 (2) - is waiting for 'conn1' to end execution of test.pkg1.* routines,
+# to acquire an exclusive MDL on 'package body pkg1',
+# to DROP the package pkg1
+#
+--vertical_results
+SELECT ID-CONNECTION_ID() AS CONN,INFO,STATE,LOCK_MODE,LOCK_TYPE,TABLE_NAME
+ FROM INFORMATION_SCHEMA.PROCESSLIST
+ LEFT JOIN INFORMATION_SCHEMA.METADATA_LOCK_INFO
+ ON (ID=THREAD_ID)
+ ORDER BY ID,TABLE_NAME,LOCK_MODE,LOCK_TYPE;
+--horizontal_results
+
+#
+# Now let conn1 finish the package routine execution
+#
+DO RELEASE_LOCK('lock');
+connection conn1;
+reap;
+disconnect conn1;
+
+#
+# Now conn2 should actually DROP the package
+#
+connection conn2;
+reap;
+disconnect conn2;
+
+connection default;
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-mysqldump.test b/mysql-test/suite/compat/oracle/t/sp-package-mysqldump.test
new file mode 100644
index 00000000..c4e4b2a1
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-mysqldump.test
@@ -0,0 +1,94 @@
+--source include/have_utf8mb4.inc
+--source include/not_embedded.inc
+
+SET sql_mode=ORACLE;
+
+#
+# Create a standalone procedure test.p1 and a package pkg1.
+# The standalone routine test.p1 and the package routines call each other.
+#
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ SELECT pkg1.f1(); -- a standalone routine calls a package routine
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ CALL test.p1; -- a package routine calls a standalone routine
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+CALL p1;
+CALL pkg1.p1;
+SELECT pkg1.f1();
+
+
+#
+# Create specifications for one more package, without a BODY
+#
+DELIMITER $$;
+CREATE PACKAGE pkg2 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+
+--exec $MYSQL_DUMP --skip-comments --routines --default-character-set=utf8mb4 test
+--exec $MYSQL_DUMP --skip-comments --routines --xml test
+
+let $dump = $MYSQLTEST_VARDIR/tmp/sp-package-mysqldump.sql;
+
+--exec $MYSQL_DUMP --compact --routines test > $dump
+
+DROP PACKAGE pkg1;
+DROP PACKAGE pkg2;
+DROP PROCEDURE p1;
+
+--exec $MYSQL test < $dump
+
+--vertical_results
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE STATUS;
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE BODY STATUS;
+--horizontal_results
+
+SHOW CREATE PACKAGE pkg1;
+SHOW CREATE PACKAGE pkg2;
+SHOW CREATE PACKAGE BODY pkg1;
+
+CALL p1;
+CALL pkg1.p1;
+SELECT pkg1.f1();
+
+DROP PACKAGE pkg1;
+DROP PACKAGE pkg2;
+DROP PROCEDURE p1;
+
+--echo # removing the dump file
+--error 0,1
+--remove_file $dump
diff --git a/mysql-test/suite/compat/oracle/t/sp-package-security.test b/mysql-test/suite/compat/oracle/t/sp-package-security.test
new file mode 100644
index 00000000..583f70af
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-security.test
@@ -0,0 +1,332 @@
+--source include/not_embedded.inc
+--source include/default_charset.inc
+
+SET sql_mode=ORACLE;
+
+CREATE DATABASE db1;
+CREATE USER u1@localhost IDENTIFIED BY '';
+GRANT SELECT ON db1.* TO u1@localhost;
+
+connect (conn1,localhost,u1,,db1);
+SELECT CURRENT_USER;
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # User u1 cannot drop PROCEDURE, PACKAGE, PACKAGE BODY by default
+--echo #
+
+--error ER_PROCACCESS_DENIED_ERROR
+DROP PROCEDURE p1;
+--error ER_PROCACCESS_DENIED_ERROR
+DROP PACKAGE pkg1;
+--error ER_PROCACCESS_DENIED_ERROR
+DROP PACKAGE BODY pkg1;
+
+--echo #
+--echo # User u1 cannot create PROCEDURE, PACKAGE, PACKAGE BODY by default
+--echo #
+
+DELIMITER $$;
+--error ER_DBACCESS_DENIED_ERROR
+CREATE PROCEDURE p1 AS
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_DBACCESS_DENIED_ERROR
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+DELIMITER ;$$
+
+# TODO: this should probably return ER_DBACCESS_DENIED_ERROR
+DELIMITER $$;
+--error ER_SP_DOES_NOT_EXIST
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Now create a PACKAGE by root
+--echo #
+
+connection default;
+USE db1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1root AS
+BEGIN
+ SELECT 1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+DELIMITER ;$$
+SHOW CREATE PACKAGE pkg1;
+
+--echo #
+--echo # u1 cannot SHOW yet:
+--echo # - the standalone procedure earlier created by root
+--echo # - the package specifications earlier create by root
+--echo #
+
+connection conn1;
+--error ER_SP_DOES_NOT_EXIST
+SHOW CREATE PROCEDURE p1root;
+--error ER_SP_DOES_NOT_EXIST
+SHOW CREATE PACKAGE pkg1;
+
+
+--echo #
+--echo # User u1 still cannot create a PACKAGE BODY
+--echo #
+
+connection conn1;
+DELIMITER $$;
+--error ER_DBACCESS_DENIED_ERROR
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+ FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is f1'; END;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Now grant EXECUTE:
+--echo # - on the standalone procedure earlier created by root
+--echo # - on the package specification earlier created by root
+--echo #
+connection default;
+GRANT EXECUTE ON PROCEDURE db1.p1root TO u1@localhost;
+GRANT EXECUTE ON PACKAGE db1.pkg1 TO u1@localhost;
+
+--echo #
+--echo # Now u1 can do SHOW for:
+--echo # - the standalone procedure earlier created by root
+--echo # - the package specification earlier created by root
+--echo #
+
+disconnect conn1;
+connect (conn1,localhost,u1,,db1);
+SET sql_mode=ORACLE;
+SHOW CREATE PROCEDURE db1.p1root;
+SHOW CREATE PACKAGE db1.pkg1;
+
+
+--echo #
+--echo # Now revoke EXECUTE and grant CREATE ROUTINE instead
+--echo #
+
+connection default;
+REVOKE EXECUTE ON PROCEDURE db1.p1root FROM u1@localhost;
+REVOKE EXECUTE ON PACKAGE db1.pkg1 FROM u1@localhost;
+GRANT CREATE ROUTINE ON db1.* TO u1@localhost;
+
+--echo #
+--echo # Reconnect u1 to make new grants have effect
+--echo #
+
+disconnect conn1;
+connect (conn1,localhost,u1,,db1);
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Now u1 can SHOW:
+--echo # - standalone routines earlier created by root
+--echo # - package specifications earlier created by root
+--echo #
+SHOW CREATE PROCEDURE p1root;
+SHOW CREATE PACKAGE pkg1;
+
+--echo #
+--echo # Now u1 can CREATE, DROP and EXECUTE its own standalone procedures
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+SHOW GRANTS;
+CALL p1;
+DROP PROCEDURE p1;
+SHOW GRANTS;
+
+--echo #
+--echo # Now u1 can also CREATE, DROP its own package specifications
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE pkg2 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+DELIMITER ;$$
+SHOW CREATE PACKAGE pkg2;
+SHOW GRANTS;
+DROP PACKAGE pkg2;
+SHOW GRANTS;
+
+
+--echo #
+--echo # Now u1 can also CREATE, DROP package bodies and EXECUTE package body routines
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS BEGIN SELECT 'This is pkg1.p1' AS `comment`; END;
+ FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is pkg1.f1'; END;
+END;
+$$
+DELIMITER ;$$
+SHOW CREATE PACKAGE pkg1;
+SHOW CREATE PACKAGE BODY pkg1;
+SHOW GRANTS;
+CALL pkg1.p1;
+SELECT pkg1.f1();
+DROP PACKAGE BODY pkg1;
+SHOW GRANTS;
+
+--echo #
+--echo # Now create a PACKAGE BODY by root.
+--echo # u1 does not have EXECUTE access by default.
+--echo #
+
+connection default;
+DELIMITER $$;
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS BEGIN SELECT 'This is pkg1.p1' AS `comment`; END;
+ FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is pkg1.f1'; END;
+END;
+$$
+DELIMITER ;$$
+
+connection conn1;
+SHOW CREATE PACKAGE pkg1;
+SHOW CREATE PACKAGE BODY pkg1;
+--error ER_PROCACCESS_DENIED_ERROR
+CALL pkg1.p1;
+--error ER_PROCACCESS_DENIED_ERROR
+SELECT pkg1.f1();
+
+--echo #
+--echo # Now grant EXECUTE to u1 on the PACKAGE BODY created by root
+--echo #
+
+connection default;
+GRANT EXECUTE ON PACKAGE BODY db1.pkg1 TO u1@localhost;
+disconnect conn1;
+connect (conn1,localhost,u1,,db1);
+SELECT CURRENT_USER;
+SET sql_mode=ORACLE;
+SHOW GRANTS;
+CALL pkg1.p1;
+SELECT pkg1.f1();
+
+connection default;
+DROP PACKAGE BODY pkg1;
+
+
+--echo #
+--echo # u1 still cannot DROP the package specification earlier created by root.
+--echo #
+
+connection conn1;
+--error ER_PROCACCESS_DENIED_ERROR
+DROP PACKAGE pkg1;
+
+--echo #
+--echo # Grant ALTER ROUTINE to u1
+--echo #
+
+connection default;
+GRANT ALTER ROUTINE ON db1.* TO u1@localhost;
+
+--echo #
+--echo # Now u1 can DROP:
+--echo # - the standalone procedure earlier created by root
+--echo # - the package specification earlier created by root
+--echo #
+
+disconnect conn1;
+connect (conn1,localhost,u1,,db1);
+SET sql_mode=ORACLE;
+DROP PACKAGE pkg1;
+DROP PROCEDURE p1root;
+
+disconnect conn1;
+connection default;
+
+DROP USER u1@localhost;
+DROP DATABASE db1;
+USE test;
+
+
+--echo #
+--echo # Creator=root, definer=xxx
+--echo #
+
+CREATE USER xxx@localhost;
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT SESSION_USER(), CURRENT_USER(), 'p1.p1' AS msg;
+ END;
+BEGIN
+ SELECT SESSION_USER(), CURRENT_USER(), 'package body p1' AS msg;
+END;
+$$
+DELIMITER ;$$
+--error ER_PROCACCESS_DENIED_ERROR
+CALL p1.p1;
+GRANT EXECUTE ON PACKAGE BODY test.p1 TO xxx@localhost;
+CALL p1.p1;
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+
+
+--echo #
+--echo # Creator=root, definer=xxx, SQL SECURITY INVOKER
+--echo #
+
+CREATE USER xxx@localhost;
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT SESSION_USER(), CURRENT_USER(), 'p1.p1' AS msg;
+ END;
+BEGIN
+ SELECT SESSION_USER(), CURRENT_USER(), 'package body p1' AS msg;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
diff --git a/mysql-test/suite/compat/oracle/t/sp-package.test b/mysql-test/suite/compat/oracle/t/sp-package.test
new file mode 100644
index 00000000..b6310eed
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package.test
@@ -0,0 +1,3092 @@
+--source include/default_charset.inc
+
+SET sql_mode=ORACLE;
+
+--enable_prepare_warnings
+--disable_ps2_protocol
+
+--echo #
+--echo # Creating a body of a non-existing package
+--echo #
+DELIMITER $$;
+--error ER_SP_DOES_NOT_EXIST
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # Dropping a non-existing package
+--echo #
+--error ER_SP_DOES_NOT_EXIST
+DROP PACKAGE test2;
+DROP PACKAGE IF EXISTS test2;
+--error ER_SP_DOES_NOT_EXIST
+DROP PACKAGE BODY test2;
+
+
+--echo #
+--echo # Bad combinations of OR REPLACE and IF EXISTS
+--echo #
+
+DELIMITER $$;
+--error ER_WRONG_USAGE
+CREATE OR REPLACE PACKAGE IF NOT EXISTS pkg AS
+ PROCEDURE p1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_WRONG_USAGE
+CREATE OR REPLACE PACKAGE BODY IF NOT EXISTS pkg AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # PACKAGE and PS
+--echo #
+
+PREPARE stmt FROM 'CREATE PACKAGE test2 AS FUNCTION f1 RETURN INT; END test2';
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+PREPARE stmt FROM 'CREATE PACKAGE BODY test2 AS'
+ ' FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;'
+ 'END test2';
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Package and READ ONLY transactions
+--echo #
+
+SET SESSION TRANSACTION READ ONLY;
+
+DELIMITER $$;
+--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END
+$$
+DELIMITER ;$$
+
+SET SESSION TRANSACTION READ WRITE;
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f2 RETURN INT;
+END;
+$$
+SET SESSION TRANSACTION READ ONLY
+$$
+--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ FUNCTION f2 RETURN INT AS BEGIN RETURN f1(); END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT f2();
+ END;
+END;
+$$
+DELIMITER ;$$
+SET SESSION TRANSACTION READ WRITE;
+DROP PACKAGE test2;
+
+SET SESSION TRANSACTION READ ONLY;
+--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
+DROP PACKAGE test2;
+--error ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
+DROP PACKAGE BODY test2;
+
+SET SESSION TRANSACTION READ WRITE;
+
+
+--echo #
+--echo # Syntax error inside a CREATE PACKAGE, inside a routine definition
+--echo #
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f2 RETURN INT;
+ FUNCTION f3;
+ FUNCTION f4 RETURN INT;
+END
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Syntax error inside a CREATE PACKAGE, outside of a routine definition
+--echo #
+
+# The definition "FUNCTION f3 RETURN INT AS BEGIN RETURN 10; END;"
+# is valid in CREATE PACKAGE BODY, but not in CREATE PACKAGE.
+# Syntax error happens after parsing "FUNCTION f3 RETURN INT".
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f2 RETURN INT;
+ FUNCTION f3 RETURN INT AS BEGIN RETURN 10; END;
+ FUNCTION f4 RETURN INT;
+END
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Syntax error inside a CREATE PACKAGE BODY, inside a routine definition
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f2 RETURN INT;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ FUNCTION f2 RETURN INT SA BEGIN RETURN 10; END; -- Notice "SA" vs "AS"
+END
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+--echo #
+--echo # Syntax error inside a CREATE PACKAGE BODY, outside a routine definition
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f2 RETURN INT;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ SOME SYNTAX ERROR;
+ FUNCTION f2 RETURN INT AS BEGIN RETURN 10; END;
+END
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Syntax error inside a CREATE PACKAGE BODY executable section
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+BEGIN
+ SOME SYNTAX ERROR;
+END
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # CREATE PROCEDURE inside a package PROCEDURE is not allowed
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_NO_RECURSIVE_CREATE
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS
+ BEGIN
+ CREATE PROCEDURE p1 AS BEGIN NULL; END;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # CREATE PACKAGE inside a package PROCEDURE is not allowed
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_NO_RECURSIVE_CREATE
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS
+ BEGIN
+ CREATE PACKAGE p1 AS PROCEDURE p1; END;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # CREATE PROCEDURE inside a package executable section is not allowed
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_NO_RECURSIVE_CREATE
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+ CREATE PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # CREATE FUNCTION inside a package executable section is not allowed
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_NO_RECURSIVE_CREATE
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+ CREATE FUNCTION f1 RETURN INT AS BEGIN RETURN 0; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # CREATE PACKAGE inside a package executable section is not allowed
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_NO_RECURSIVE_CREATE
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+ CREATE PACKAGE p1 AS PROCEDURE p1; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Broken CREATE PACKAGE at CREATE PACKAGE BODY time
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+
+DELIMITER $$;
+--error ER_SP_PROC_TABLE_CORRUPT
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT
+ AS BEGIN
+ RETURN f2();
+ END;
+END;
+$$
+DELIMITER ;$$
+show warnings;
+
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Broken CREATE PACKAGE at a package function call time
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT
+ AS BEGIN
+ RETURN f2();
+ END;
+END;
+$$
+DELIMITER ;$$
+
+--error ER_SP_DOES_NOT_EXIST
+SELECT test2.f1();
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+--source sp-cache-invalidate.inc
+--error ER_SP_PROC_TABLE_CORRUPT
+SELECT test2.f1();
+show warnings;
+--error ER_SP_PROC_TABLE_CORRUPT
+SELECT test2.f1();
+show warnings;
+--error ER_SP_PROC_TABLE_CORRUPT
+SELECT test2.f1();
+show warnings;
+
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Broken CREATE PACKAGE at a package procedure call time
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1
+ AS BEGIN
+ CALL p2;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+--error ER_SP_DOES_NOT_EXIST
+CALL test2.f1();
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+--source sp-cache-invalidate.inc
+--error ER_SP_PROC_TABLE_CORRUPT
+CALL test2.p1();
+show warnings;
+--error ER_SP_PROC_TABLE_CORRUPT
+CALL test2.p1();
+show warnings;
+--error ER_SP_PROC_TABLE_CORRUPT
+CALL test2.p1();
+show warnings;
+
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Bad routine names
+--echo #
+
+DELIMITER $$;
+--error ER_TOO_LONG_IDENT
+CREATE PACKAGE p1 AS
+ PROCEDURE pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_TOO_LONG_IDENT
+CREATE PACKAGE p1 AS
+ FUNCTION fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1
+ RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_WRONG_NAME
+CREATE PACKAGE p1 AS
+ PROCEDURE "p1 ";
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_WRONG_NAME
+CREATE PACKAGE p1 AS
+ FUNCTION "f1 " RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_WRONG_NAME
+CREATE PACKAGE p1 AS
+ PROCEDURE "p1.p1";
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_WRONG_NAME
+CREATE PACKAGE p1 AS
+ FUNCTION "f1.f1" RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Duplicate PROCEDURE in CREATE PACKAGE
+--echo #
+
+DELIMITER $$;
+--error ER_SP_ALREADY_EXISTS,
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+ PROCEDURE p1;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_ALREADY_EXISTS,
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+ PROCEDURE P1;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Duplicate FUNCTION in CREATE PACKAGE
+--echo #
+
+DELIMITER $$;
+--error ER_SP_ALREADY_EXISTS,
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_SP_ALREADY_EXISTS,
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ FUNCTION F1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Duplicate PROCEDURE in CREATE PACKAGE BODY
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_SP_ALREADY_EXISTS
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+ PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+--error ER_SP_ALREADY_EXISTS
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+ PROCEDURE P1 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Duplicate FUNCTION in CREATE PACKAGE BODY
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+--error ER_SP_ALREADY_EXISTS
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 0; END;
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 0; END;
+END;
+$$
+--error ER_SP_ALREADY_EXISTS
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 0; END;
+ FUNCTION F1 RETURN INT AS BEGIN RETURN 0; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Routines declared in CREATE PACKAGE missing in CREATE PACKAGE BODY
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p2 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f2 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+CREATE PACKAGE BODY test2 AS
+ FUNCTION p1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1(a INT) AS BEGIN NULL; END; -- Notice different prototype
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+--echo #
+--echo # Forward declarations in CREATE PACKAGE BODY with missing implementations
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS BEGIN NULL; END;
+ PROCEDURE p2;
+END;
+$$
+--error ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Creating a new package
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 COMMENT 'package-test2-comment' AS
+ FUNCTION f1 RETURN INT DETERMINISTIC;
+ FUNCTION f2(a INT) RETURN INT;
+ FUNCTION concat RETURN INT;
+ PROCEDURE p1;
+ PROCEDURE p2(a INT);
+END
+$$
+DELIMITER ;$$
+
+--vertical_results
+--replace_column 13 # 14 #
+SELECT * FROM mysql.proc WHERE db='test' AND name='test2';
+--replace_column 24 # 25 #
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME='test2';
+--horizontal_results
+
+DELIMITER $$;
+CREATE PACKAGE IF NOT EXISTS test2 AS
+ FUNCTION f1 RETURN INT;
+END test2
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2 COMMENT 'package-body-test2-comment' AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ FUNCTION f2(a INT) RETURN INT AS BEGIN RETURN f1()+a; END;
+ FUNCTION concat RETURN INT AS BEGIN RETURN 1; END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT f2(0);
+ END;
+ PROCEDURE p2(a INT) AS
+ BEGIN
+ SELECT f2(a);
+ END;
+END;
+$$
+DELIMITER ;$$
+
+# This should do nothing and return a warning
+DELIMITER $$;
+CREATE PACKAGE BODY IF NOT EXISTS test2 AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 20; END;
+ FUNCTION f2(a INT) RETURN INT AS BEGIN RETURN f1()+a; END;
+ FUNCTION concat RETURN INT AS BEGIN RETURN 1; END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT f2(0);
+ END;
+ PROCEDURE p2(a INT) AS
+ BEGIN
+ SELECT f2(a);
+ END;
+END;
+$$
+DELIMITER ;$$
+
+#
+# The next query issues a warning about "concat" name collision,
+# raised during compilation of the package body.
+# However, "mtr --ps" does not produce the warning.
+# It's not a package specific issue. The same difference exists for
+# standalone functions. So just suppress warning for now.
+#
+--disable_warnings
+SELECT test2.f1();
+--enable_warnings
+SELECT test2.f2(1);
+CALL test2.p1();
+CALL test2.p2(1);
+
+--vertical_results
+--replace_column 13 # 14 #
+SELECT * FROM mysql.proc WHERE db='test' AND name LIKE 'test2.%';
+--replace_column 24 # 25 #
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME='test2';
+--replace_column 24 # 25 #
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME LIKE 'test2.%';
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE STATUS;
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE BODY STATUS;
+SHOW CREATE PACKAGE test2;
+SHOW CREATE PACKAGE BODY test2;
+--horizontal_results
+
+
+
+DROP PACKAGE BODY test2;
+--error ER_SP_DOES_NOT_EXIST
+SELECT test2.f1();
+--error ER_SP_DOES_NOT_EXIST
+SELECT test2.f2();
+--error ER_SP_DOES_NOT_EXIST
+CALL test2.p1();
+
+DROP PACKAGE BODY IF EXISTS test2;
+
+--error ER_SP_DOES_NOT_EXIST
+DROP PACKAGE BODY test2;
+
+
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Creating a new package in a remote database
+--echo #
+
+CREATE DATABASE test2;
+
+DELIMITER $$;
+CREATE PACKAGE test2.test2 COMMENT 'package-test2-comment' AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2.test2 COMMENT 'package-body-test2-comment' AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+ PROCEDURE p1 AS BEGIN SELECT f1(); END;
+END;
+$$
+DELIMITER ;$$
+
+--vertical_results
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE STATUS;
+--replace_column 4 'root@localhost' 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+SHOW PACKAGE BODY STATUS;
+--horizontal_results
+
+USE test2;
+SELECT test2.f1();
+CALL test2.p1();
+USE test;
+DROP PACKAGE BODY test2.test2;
+DROP PACKAGE test2.test2;
+DROP DATABASE test2;
+
+
+--echo #
+--echo # Only public routines are available outside
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ -- Public routines
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is test2.f1';
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is test2.p1';
+ END;
+ -- Private routines
+ FUNCTION f2 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is test2.f2';
+ END;
+ PROCEDURE p2 AS
+ BEGIN
+ SELECT 'This is test2.p2';
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT test2.f1();
+CALL test2.p1();
+--error ER_SP_DOES_NOT_EXIST
+SELECT test2.f2();
+--error ER_SP_DOES_NOT_EXIST
+CALL test2.p2();
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # PACKAGE BODY with forward declarations
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ -- Forward declarations
+ FUNCTION f2private RETURN TEXT;
+ PROCEDURE p2private;
+ -- Public routines
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN f2private();
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p2private;
+ END;
+ -- Definitions for the forward declarations
+ FUNCTION f2private RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2private';
+ END;
+ PROCEDURE p2private AS
+ BEGIN
+ SELECT 'This is p2private';
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT test2.f1();
+CALL test2.p1();
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Calling private routines with forward declarations,
+--echo # using qualified notation, e.g. "CALL pkg.proc"
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ -- Forward declarations
+ FUNCTION f2private RETURN TEXT;
+ PROCEDURE p2private;
+ -- Public routines
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN test2.f2private();
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL test2.p2private;
+ END;
+ -- Definitions for the forward declarations
+ FUNCTION f2private RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2private';
+ END;
+ PROCEDURE p2private AS
+ BEGIN
+ SELECT 'This is p2private' AS msg;
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT test2.f1();
+CALL test2.p1();
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Calling private routines, using qualified notation, e.g. "pkg.proc"
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ -- Private routines
+ FUNCTION f2private RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2private';
+ END;
+ PROCEDURE p2private AS
+ BEGIN
+ SELECT 'This is p2private' AS msg;
+ END;
+ -- Public routines
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN test2.f2private();
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL test2.p2private;
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT test2.f1();
+CALL test2.p1();
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Calling private routines from the package initialization section,
+--echo # using qualified notation, e.g. "pkg.proc"
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ -- Private routines
+ FUNCTION f2private RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f2private';
+ END;
+ PROCEDURE p2private AS
+ BEGIN
+ SELECT 'This is p2private' AS msg;
+ END;
+ -- Public routines
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is p1' AS msg;
+ END;
+BEGIN
+ SELECT test2.f2private();
+ CALL test2.p2private();
+END;
+$$
+DELIMITER ;$$
+CALL test2.p1();
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # Testing OR REPLACE
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg AS
+ FUNCTION f0 RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE pkg AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE BODY pkg AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+DELIMITER ;$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+SELECT pkg.f1();
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE BODY pkg AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 20; END;
+END;
+$$
+DELIMITER ;$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+SELECT pkg.f1();
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg AS
+ FUNCTION f1 RETURN BIGINT;
+END;
+$$
+DELIMITER ;$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+--error ER_SP_DOES_NOT_EXIST
+SELECT pkg.f1();
+
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE BODY pkg AS
+ FUNCTION f1 RETURN INT AS BEGIN RETURN 30; END;
+END;
+$$
+DELIMITER ;$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+SELECT pkg.f1();
+
+DROP PACKAGE pkg;
+
+
+--echo #
+--echo # Package routines accessing tables
+--echo #
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ PROCEDURE p1(a INT);
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1(a INT) AS
+ BEGIN
+ INSERT INTO t1 VALUES (10);
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL test2.p1(10);
+SELECT * FROM t1;
+DROP PACKAGE test2;
+DROP TABLE t1;
+
+
+--echo #
+--echo # CREATE PACKAGE: Optional package name after the "END" keyword
+--echo #
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END test2.test2
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END test3
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END test2
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+
+--echo #
+--echo # MDEV-12089 sql_mode=ORACLE: Understand optional routine name after the END keyword
+--echo #
+
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+ PROCEDURE p1;
+END test2;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END f1.f1;
+END test2;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END f2;
+END test2;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END p1.p1;
+END test2;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PACKAGE BODY test2 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END p2;
+END test2;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN 10;
+ END f1;
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END p1;
+END test2;
+$$
+DELIMITER ;$$
+DROP PACKAGE test2;
+
+--echo #
+--echo # Package and package routine name and end name are case insensitive
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN TEXT;
+ PROCEDURE p1;
+END TEST2;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+CREATE PACKAGE BODY test2 AS
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f1';
+ END F1;
+ PROCEDURE P1 AS
+ BEGIN
+ SELECT 'This is p1' AS msg;
+ END p1;
+END TEST2;
+$$
+DELIMITER ;$$
+SELECT TEST2.F1();
+SELECT test2.f1();
+CALL TEST2.p1();
+CALL test2.P1();
+DROP PACKAGE BODY TEST2;
+DROP PACKAGE TEST2;
+
+
+--echo #
+--echo # Testing various qualified/non-qualified db/package SP call chains
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f3() RETURN TEXT AS
+BEGIN
+ SET @track= @track || ' ' || 'test.f3()';
+ RETURN '';
+END;
+$$
+CREATE PROCEDURE p3() AS
+BEGIN
+ SET @track= @track || ' ' || 'test.p3()';
+END;
+$$
+CREATE FUNCTION ff2(task TEXT) RETURN TEXT AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+BEGIN
+ SET @track= @track || ' ' || 'test.ff2()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+ RETURN '';
+END;
+$$
+CREATE PROCEDURE pp2(task TEXT) AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+BEGIN
+ SET @track= @track || ' ' || 'test.pp2()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+END;
+$$
+CREATE PACKAGE pack AS
+ PROCEDURE p1(task TEXT);
+ PROCEDURE p2(task TEXT);
+ FUNCTION f1(task TEXT) RETURN TEXT;
+ FUNCTION f2(step2 TEXT) RETURN TEXT;
+ FUNCTION f3 RETURN TEXT;
+ PROCEDURE p3;
+END;
+$$
+CREATE PACKAGE BODY pack AS
+ PROCEDURE p1(task TEXT) AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+ BEGIN
+ SET @track= 'test.pack.p1()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p2' THEN CALL p2(tail);
+ WHEN 'f2' THEN rc:= f2(tail);
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'px' THEN CALL px();
+ WHEN 'fx' THEN rc:= fx();
+ WHEN 'pp2' THEN CALL pp2(tail);
+ WHEN 'ff2' THEN rc:= ff2(tail);
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'pack.px' THEN CALL pack.px();
+ WHEN 'pack.fx' THEN rc:= pack.fx();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+ SELECT @track;
+ END;
+
+ FUNCTION f1(task TEXT) RETURN TEXT AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+ BEGIN
+ SET @track= 'test.pack.f1()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p2' THEN CALL p2(tail);
+ WHEN 'f2' THEN rc:= f2(tail);
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'px' THEN CALL px();
+ WHEN 'fx' THEN rc:= fx();
+ WHEN 'pp2' THEN CALL pp2(tail);
+ WHEN 'ff2' THEN rc:= ff2(tail);
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'pack.px' THEN CALL pack.px();
+ WHEN 'pack.fx' THEN rc:= pack.fx();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+ SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT=@track;
+ RETURN '';
+ END;
+
+ PROCEDURE p2(task TEXT) AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+ BEGIN
+ SET @track= @track || ' ' || 'test.pack.p2()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p2' THEN CALL p2(tail);
+ WHEN 'f2' THEN rc:= f2(tail);
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'px' THEN CALL px();
+ WHEN 'fx' THEN rc:= fx();
+ WHEN 'pp2' THEN CALL pp2(tail);
+ WHEN 'ff2' THEN rc:= ff2(tail);
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'pack.px' THEN CALL pack.px();
+ WHEN 'pack.fx' THEN rc:= pack.fx();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+ END;
+
+ FUNCTION f2(task TEXT) RETURN TEXT AS
+ step TEXT := REGEXP_SUBSTR(task,'^[^ ]*');
+ tail TEXT := REGEXP_REPLACE(task,'^[^ ]*[ ]*(.*)','\\1');
+ rc TEXT;
+ BEGIN
+ SET @track= @track || ' ' || 'test.pack.f2()';
+ CASE step
+ WHEN '' THEN NULL;
+ WHEN 'p2' THEN CALL p2(tail);
+ WHEN 'f2' THEN rc:= f2(tail);
+ WHEN 'p3' THEN CALL p3();
+ WHEN 'f3' THEN rc:= f3();
+ WHEN 'px' THEN CALL px();
+ WHEN 'fx' THEN rc:= fx();
+ WHEN 'pp2' THEN CALL pp2(tail);
+ WHEN 'ff2' THEN rc:= ff2(tail);
+ WHEN 'pack.p2' THEN CALL pack.p2(tail);
+ WHEN 'pack.f2' THEN rc:= pack.f2(tail);
+ WHEN 'pack.p3' THEN CALL pack.p3();
+ WHEN 'pack.f3' THEN rc:= pack.f3();
+ WHEN 'pack.px' THEN CALL pack.px();
+ WHEN 'pack.fx' THEN rc:= pack.fx();
+ WHEN 'test.p3' THEN CALL test.p3();
+ WHEN 'test.f3' THEN rc:= test.f3();
+ WHEN 'test.pp2' THEN CALL test.pp2(tail);
+ WHEN 'test.ff2' THEN rc:= test.ff2(tail);
+ ELSE SET @track= @track || ' ' || step || ' [unknown step]';
+ END CASE;
+ RETURN '';
+ END;
+ PROCEDURE p3 AS
+ BEGIN
+ SET @track= @track || ' ' || 'test.pack.p3()';
+ END;
+ FUNCTION f3 RETURN TEXT AS
+ BEGIN
+ SET @track= @track || ' ' || 'test.pack.f3()';
+ RETURN '';
+ END;
+
+END pack;
+$$
+DELIMITER ;$$
+
+SET max_sp_recursion_depth=10;
+
+--echo # pack.routine -> *
+
+CALL pack.p1('p2');
+CALL pack.p1('f2');
+--error ER_SP_DOES_NOT_EXIST
+CALL pack.p1('px');
+--error ER_SP_DOES_NOT_EXIST
+CALL pack.p1('fx');
+
+CALL pack.p1('pp2');
+CALL pack.p1('ff2');
+
+CALL pack.p1('pack.p2');
+CALL pack.p1('pack.f2');
+--error ER_SP_DOES_NOT_EXIST
+CALL pack.p1('pack.px');
+--error ER_SP_DOES_NOT_EXIST
+CALL pack.p1('pack.fx');
+
+CALL pack.p1('test.pp2');
+CALL pack.p1('test.ff2');
+
+DO pack.f1('p2');
+DO pack.f1('f2');
+--error ER_SP_DOES_NOT_EXIST
+DO pack.p1('px');
+--error ER_SP_DOES_NOT_EXIST
+DO pack.p1('fx');
+
+DO pack.f1('pp2');
+DO pack.f1('ff2');
+
+DO pack.f1('pack.p2');
+DO pack.f1('pack.f2');
+--error ER_SP_DOES_NOT_EXIST
+SELECT pack.f1('pack.px');
+--error ER_SP_DOES_NOT_EXIST
+SELECT pack.f1('pack.fx');
+
+DO pack.f1('test.pp2');
+DO pack.f1('test.ff2');
+
+--echo #
+--echo # Qualified_package_routine -> Non_qualified_package_routine
+--echo #
+
+--echo # pack.routine -> [pack.]routine -> pack.routine
+
+CALL pack.p1('p2 pack.p3');
+CALL pack.p1('p2 pack.f3');
+CALL pack.p1('f2 pack.p3');
+CALL pack.p1('f2 pack.f3');
+
+DO pack.f1('p2 pack.p3');
+DO pack.f1('p2 pack.f3');
+DO pack.f1('f2 pack.p3');
+DO pack.f1('f2 pack.f3');
+
+--echo # pack.routine -> [pack.]routine -> [pack]routine
+
+CALL pack.p1('p2 p3');
+CALL pack.p1('p2 f3');
+CALL pack.p1('f2 p3');
+CALL pack.p1('f2 f3');
+
+DO pack.f1('p2 p3');
+DO pack.f1('p2 f3');
+DO pack.f1('f2 p3');
+DO pack.f1('f2 f3');
+
+--echo # pack.routine -> [pack.]routine -> test.routine
+
+CALL pack.p1('p2 test.p3');
+CALL pack.p1('p2 test.f3');
+CALL pack.p1('f2 test.p3');
+CALL pack.p1('f2 test.f3');
+
+DO pack.f1('p2 test.p3');
+DO pack.f1('p2 test.f3');
+DO pack.f1('f2 test.p3');
+DO pack.f1('f2 test.f3');
+
+--echo # pack.routine -> [pack.]routine -> [test.]routine
+
+CALL pack.p1('p2 pp2');
+CALL pack.p1('p2 ff2');
+CALL pack.p1('f2 pp2');
+CALL pack.p1('f2 ff2');
+
+DO pack.f1('p2 pp2');
+DO pack.f1('p2 ff2');
+DO pack.f1('f2 pp2');
+DO pack.f1('f2 ff2');
+
+
+--echo #
+--echo # Qualified_package_routine -> Non_qualified_database_routine
+--echo #
+
+--echo # pack.routine -> [test.]routine -> pack.routine
+
+CALL pack.p1('pp2 pack.p3');
+CALL pack.p1('pp2 pack.f3');
+CALL pack.p1('ff2 pack.p3');
+CALL pack.p1('ff2 pack.f3');
+
+DO pack.f1('pp2 pack.p3');
+DO pack.f1('pp2 pack.f3');
+DO pack.f1('ff2 pack.p3');
+DO pack.f1('ff2 pack.f3');
+
+--echo # pack.routine -> [test.]routine -> test.routine
+
+CALL pack.p1('pp2 test.p3');
+CALL pack.p1('pp2 test.f3');
+CALL pack.p1('ff2 test.p3');
+CALL pack.p1('ff2 test.f3');
+
+DO pack.f1('pp2 test.p3');
+DO pack.f1('pp2 test.f3');
+DO pack.f1('ff2 test.p3');
+DO pack.f1('ff2 test.f3');
+
+--echo # pack.routine -> [test.]routine -> [test.]routine
+
+CALL pack.p1('pp2 p3');
+CALL pack.p1('pp2 f3');
+CALL pack.p1('ff2 p3');
+CALL pack.p1('ff2 f3');
+
+DO pack.f1('pp2 p3');
+DO pack.f1('pp2 f3');
+DO pack.f1('ff2 p3');
+DO pack.f1('ff2 f3');
+
+
+--echo #
+--echo # Qualified_package_routine -> Qualified_package_routine
+--echo #
+
+--echo # pack.routine -> pack.routine -> pack.routine
+
+CALL pack.p1('pack.p2 pack.p3');
+CALL pack.p1('pack.p2 pack.f3');
+CALL pack.p1('pack.f2 pack.p3');
+CALL pack.p1('pack.f2 pack.f3');
+
+DO pack.f1('pack.p2 pack.p3');
+DO pack.f1('pack.p2 pack.f3');
+DO pack.f1('pack.f2 pack.p3');
+DO pack.f1('pack.f2 pack.f3');
+
+--echo # pack.routine -> pack.routine -> [pack.]routine
+
+CALL pack.p1('pack.p2 p3');
+CALL pack.p1('pack.p2 f3');
+CALL pack.p1('pack.f2 p3');
+CALL pack.p1('pack.f2 f3');
+
+DO pack.f1('pack.p2 p3');
+DO pack.f1('pack.p2 f3');
+DO pack.f1('pack.f2 p3');
+DO pack.f1('pack.f2 f3');
+
+--echo # pack.routine -> pack.routine -> test.routine
+
+CALL pack.p1('pack.p2 test.p3');
+CALL pack.p1('pack.p2 test.f3');
+CALL pack.p1('pack.f2 test.p3');
+CALL pack.p1('pack.f2 test.f3');
+
+DO pack.f1('pack.p2 test.p3');
+DO pack.f1('pack.p2 test.f3');
+DO pack.f1('pack.f2 test.p3');
+DO pack.f1('pack.f2 test.f3');
+
+--echo # pack.routine -> pack.routine -> [test.]routine
+
+CALL pack.p1('pack.p2 pp2');
+CALL pack.p1('pack.p2 ff2');
+CALL pack.p1('pack.f2 pp2');
+CALL pack.p1('pack.f2 ff2');
+
+DO pack.f1('pack.p2 pp2');
+DO pack.f1('pack.p2 ff2');
+DO pack.f1('pack.f2 pp2');
+DO pack.f1('pack.f2 ff2');
+
+
+--echo #
+--echo # Qualified_package_routine -> Qualified_database_routine
+--echo #
+
+--echo pack.routine -> test.routine -> pack.routine
+
+CALL pack.p1('test.pp2 pack.p3');
+CALL pack.p1('test.pp2 pack.f3');
+CALL pack.p1('test.ff2 pack.p3');
+CALL pack.p1('test.ff2 pack.f3');
+
+DO pack.f1('test.pp2 pack.p3');
+DO pack.f1('test.pp2 pack.f3');
+DO pack.f1('test.ff2 pack.p3');
+DO pack.f1('test.ff2 pack.f3');
+
+--echo pack.routine -> test.routine -> test.routine
+
+CALL pack.p1('test.pp2 test.p3');
+CALL pack.p1('test.pp2 test.f3');
+CALL pack.p1('test.ff2 test.p3');
+CALL pack.p1('test.ff2 test.f3');
+
+DO pack.f1('test.pp2 test.p3');
+DO pack.f1('test.pp2 test.f3');
+DO pack.f1('test.ff2 test.p3');
+DO pack.f1('test.ff2 test.f3');
+
+--echo pack.routine -> test.routine -> [test.]routine
+
+CALL pack.p1('test.pp2 p3');
+CALL pack.p1('test.pp2 f3');
+CALL pack.p1('test.ff2 p3');
+CALL pack.p1('test.ff2 f3');
+
+DO pack.f1('test.pp2 p3');
+DO pack.f1('test.pp2 f3');
+DO pack.f1('test.ff2 p3');
+DO pack.f1('test.ff2 f3');
+
+
+--echo # Longer chains
+
+CALL pack.p1('p2 f2 p2 test.pp2 test.ff2 pack.p3');
+CALL pack.p1('p2 test.pp2 pack.p2 pack.f2 test.ff2 pack.p3');
+
+
+DROP PACKAGE pack;
+DROP FUNCTION f3;
+DROP PROCEDURE p3;
+DROP FUNCTION ff2;
+DROP PROCEDURE pp2;
+
+
+--echo #
+--echo # Calling a standalone function from a non-current database,
+--echo # which calls a package routine from the same non-current database.
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ CALL pkg1.p1;
+END;
+$$
+CREATE PACKAGE pkg1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT database();
+ END;
+END;
+$$
+DELIMITER ;$$
+# Current database
+CALL p1;
+CREATE DATABASE test2;
+USE test2;
+# Non-current database
+CALL test.p1;
+DROP DATABASE test2;
+# No current database at all
+CALL test.p1;
+USE test;
+DROP PACKAGE pkg1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Creating a package with a different DEFINER
+--echo #
+
+CREATE USER xxx@localhost;
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+
+--echo #
+--echo # Creating a package with a different DEFINER, with SQL SECURITY INVOKER
+--echo #
+
+CREATE USER xxx@localhost;
+DELIMITER $$;
+CREATE DEFINER=xxx@localhost PACKAGE p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1;
+END;
+$$
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+
+--echo #
+--echo # A package with an initialization section
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS BEGIN SET @a=@a+1; SELECT @a; END;
+ FUNCTION f1 RETURN INT AS BEGIN SET @a=@a+1; RETURN @a; END;
+BEGIN
+ SET @a:=10;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # A package with an initialization section calling
+--echo # routines from the same package, and standalone routines.
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE init20 AS
+BEGIN
+ SET @msg= @msg || '[init20]';
+END;
+$$
+CREATE PACKAGE p1 AS
+ PROCEDURE init1;
+ PROCEDURE init2;
+ FUNCTION init3 RETURN INT;
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE init1 AS
+ BEGIN
+ SET @msg= @msg || '[p1.init1]';
+ END;
+ PROCEDURE init2 AS
+ BEGIN
+ SET @msg= @msg || '[p1.init2]';
+ END;
+ FUNCTION init3 RETURN INT AS
+ BEGIN
+ SET @msg= @msg || '[p1.init3]';
+ RETURN 0;
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ SET @msg= @msg || '[p1.p1]';
+ SELECT @msg;
+ END;
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ SET @msg= @msg || '[p1.f1]';
+ RETURN @msg;
+ END;
+BEGIN
+ SET @msg= '';
+ init1();
+ init2();
+ DO init3();
+ init20();
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+DROP PROCEDURE init20;
+
+
+--echo #
+--echo # EXECUTE IMMEDIATE in the package initialization section
+--echo #
+
+SET @a=1000;
+CREATE TABLE t1 AS SELECT 10 AS a;
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS BEGIN SET @a=@a+1; SELECT @a; END;
+ FUNCTION f1 RETURN INT AS BEGIN SET @a=@a+1; RETURN @a; END;
+BEGIN
+ EXECUTE IMMEDIATE 'SELECT MAX(a) FROM t1 INTO @a';
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
+SELECT p1.f1();
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # A package with an initialization section, loading table data into a user variable
+--echo #
+
+SET @a=1000;
+CREATE TABLE t1 AS SELECT 10 AS a;
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS BEGIN SET @a=@a+1; SELECT @a; END;
+ FUNCTION f1 RETURN INT AS BEGIN SET @a=@a+1; RETURN @a; END;
+BEGIN
+ SELECT MAX(a) FROM t1 INTO @a;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # A package with an initialization section producing an error
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS BEGIN SELECT 'This is p1' AS msg; END;
+ FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is f1'; END;
+BEGIN
+ SELECT 1 FROM t1 INTO @a;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1.p1();
+--error ER_NO_SUCH_TABLE
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+--error ER_NO_SUCH_TABLE
+SELECT p1.f1();
+--error ER_NO_SUCH_TABLE
+CALL p1.p1();
+--error ER_NO_SUCH_TABLE
+SELECT p1.f1();
+CREATE TABLE t1 (a INT) AS SELECT 1;
+CALL p1.p1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+CALL p1.p1();
+DROP TABLE t1;
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # A package with SF-unsafe statements in the initialization section
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS BEGIN SELECT 'This is p1' AS msg; END;
+ FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is f1'; END;
+BEGIN
+ CREATE TABLE IF NOT EXISTS t1 (a INT);
+ DROP TABLE IF EXISTS t1;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # MDEV-13139 Package-wide variables in CREATE PACKAGE
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+--error ER_SP_DUP_VAR
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ a INT;
+ PROCEDURE p1 AS
+ BEGIN
+ CREATE VIEW v1 AS SELECT a;
+ END;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ PROCEDURE p1 AS
+ BEGIN
+ NULL;
+ END;
+ b INT; -- Variables cannot go after routine definitions
+END;
+$$
+--error ER_VIEW_SELECT_VARIABLE
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ PROCEDURE p1 AS
+ BEGIN
+ CREATE VIEW v1 AS SELECT a;
+ END;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT:=NULL;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT a;
+ a:=COALESCE(a,0)+100;
+ SET a=a+1;
+ END;
+ FUNCTION f1 RETURN INT AS
+ BEGIN
+ RETURN a;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+CALL p1.p1;
+CALL p1.p1;
+SELECT p1.f1();
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # One package variable with a default value
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT:=10;
+ PROCEDURE p1 AS BEGIN a:=a+1; SELECT a; END;
+ FUNCTION f1 RETURN INT AS BEGIN a:=a+1; RETURN a; END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a ROW (a INT, b TEXT):=ROW(10,'bbb');
+ PROCEDURE p1 AS
+ BEGIN
+ a.a:= a.a+1;
+ a.b:= a.b || 'B';
+ SELECT a.a, a.b;
+ END;
+ FUNCTION f1 RETURN INT AS BEGIN a.a:= a.a+1; RETURN a.a; END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a t1.a%TYPE:=10;
+ PROCEDURE p1 AS BEGIN a:=a+1; SELECT a; END;
+ FUNCTION f1 RETURN INT AS BEGIN a:=a+1; RETURN a; END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT, b TEXT);
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a t1%ROWTYPE:=ROW(10,'bbb');
+ PROCEDURE p1 AS
+ BEGIN
+ a.a:= a.a+1;
+ a.b:= a.b || 'B';
+ SELECT a.a, a.b;
+ END;
+ FUNCTION f1 RETURN INT AS BEGIN a.a:= a.a+1; RETURN a.a; END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # One package variable, set in the package initialization section
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ PROCEDURE p1 AS BEGIN a:=a+1; SELECT a; END;
+ FUNCTION f1 RETURN INT AS BEGIN a:=a+1; RETURN a; END;
+BEGIN
+ a:=10;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+CALL p1.p1();
+SELECT p1.f1();
+CALL p1.p1();
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # A package with an initialization section,
+--echo # loading table data into a package variable
+--echo #
+
+CREATE TABLE t1 AS SELECT 10 AS a;
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ PROCEDURE p1 AS BEGIN SET a=a+1; SELECT a; END;
+ FUNCTION f1 RETURN INT AS BEGIN SET a=a+1; RETURN a; END;
+BEGIN
+ a:=(SELECT MAX(t1.a) FROM t1);
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT p1.f1();
+SELECT p1.f1();
+--source sp-cache-invalidate.inc
+SELECT p1.f1();
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Package variables and XPath
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ i INT:=0;
+ xml TEXT:= '<a><b>b1</b><b>b2</b><b>b3</b></a>';
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ SET i=i+1;
+ RETURN ExtractValue(xml, '/a/b[$i]');
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT p1.f1();
+SELECT p1.f1();
+SELECT p1.f1();
+DROP PACKAGE p1;
+
+--echo #
+--echo # Package variables as OUT routine parameter
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ b INT;
+ c INT:=10;
+ PROCEDURE p2(a OUT INT) AS
+ BEGIN
+ a:=c;
+ c:=c+1;
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p2(b);
+ SELECT a,b;
+ END;
+BEGIN
+ CALL p2(a);
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a ROW(a INT, b TEXT);
+ b ROW(a INT, b TEXT);
+ c ROW(a INT, b TEXT):=ROW(1,'b');
+ PROCEDURE p2(x OUT ROW(a INT,b TEXT)) AS
+ BEGIN
+ x:=c;
+ x.a:=c.a+100;
+ x.b:=c.b||'X';
+ c.a:=c.a+1;
+ c.b:=c.b||'B';
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p2(b);
+ SELECT a.a,a.b,b.a,b.b;
+ END;
+BEGIN
+ CALL p2(a);
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+
+
+CREATE TABLE t1 (a INT,b TEXT);
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a t1%ROWTYPE;
+ b t1%ROWTYPE;
+ c t1%ROWTYPE:=ROW(1,'b');
+ PROCEDURE p2(x OUT t1%ROWTYPE) AS
+ BEGIN
+ x:=c;
+ x.a:=c.a+100;
+ x.b:=c.b||'X';
+ c.a:=c.a+1;
+ c.b:=c.b||'B';
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p2(b);
+ SELECT a.a,a.b,b.a,b.b;
+ END;
+BEGIN
+ CALL p2(a);
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Package variable fields as OUT routine parameters
+--echo #
+
+CREATE TABLE t1 (a INT,b TEXT);
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a t1%ROWTYPE;
+ x t1%ROWTYPE:=ROW(10,'b');
+ PROCEDURE p2(a OUT INT,b OUT TEXT) AS
+ BEGIN
+ a:=x.a;
+ b:=x.b;
+ x.a:=x.a+1;
+ x.b:=x.b||'B';
+ END;
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p2(a.a, a.b);
+ SELECT a.a,a.b;
+ END;
+BEGIN
+ CALL p2(a.a, a.b);
+ SELECT a.a, a.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Package variables as SELECT INTO targets
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a INT;
+ b INT;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 2 INTO b;
+ SELECT a,b;
+ END;
+BEGIN
+ SELECT 1 INTO a;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+
+
+CREATE TABLE t1 (a INT, b TEXT);
+INSERT INTO t1 VALUES (10,'b');
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a t1%ROWTYPE;
+ b t1%ROWTYPE;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT * FROM t1 INTO a;
+ SELECT a.a,a.b;
+ END;
+BEGIN
+ SELECT * FROM t1 INTO b;
+ SELECT b.a, b.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Package variable fields as SELECT INTO targets
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ a ROW(a INT, b TEXT);
+ b ROW(a INT, b TEXT);
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 20,'x2' INTO b.a,b.b;
+ SELECT a.a,a.b,b.a,b.b;
+ END;
+BEGIN
+ SELECT 10,'x1' INTO a.a,a.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # Recursive package procedure calls
+--echo # Makes sure that the non-top sp_head instances created by
+--echo # sp_clone_and_link_routine() correctly reproduce the package context:
+--echo # package variables, package routines.
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1(c INT);
+END p1;
+$$
+CREATE PACKAGE BODY p1 AS
+ pv1 INT:=10;
+ FUNCTION f1 RETURN INT AS BEGIN RETURN pv1+100; END;
+ PROCEDURE p1(c INT) AS
+ BEGIN
+ SELECT c, pv1, f1();
+ IF c>0 THEN
+ pv1:=pv1+1;
+ CALL p1(c-1);
+ END IF;
+ END;
+END;
+$$
+DELIMITER ;$$
+SET max_sp_recursion_depth=5;
+CALL p1.p1(5);
+SET max_sp_recursion_depth=0;
+CALL p1.p1(0);
+--error ER_SP_RECURSION_LIMIT
+CALL p1.p1(1);
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # Non-reserved keywords as package body variable names
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+END p1;
+$$
+CREATE PACKAGE BODY p1 AS
+ ascii INT:=10;
+ action INT:=20;
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT ascii, action;
+ END;
+BEGIN
+ ascii := ascii + 1;
+ action := action + 1;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+DROP PACKAGE p1;
+
+
+--echo #
+--echo # Package routines calling routines of another package
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE p1 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE p2 AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is p1.p1' AS msg;
+ END;
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is p1.f1';
+ END;
+END;
+$$
+CREATE PACKAGE BODY p2 AS
+ PROCEDURE p1 AS
+ BEGIN
+ CALL p1.p1;
+ END;
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN p1.f1();
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1.p1;
+CALL p2.p1;
+SELECT p1.f1(), p2.f1();
+DROP PACKAGE p2;
+DROP PACKAGE p1;
+
+--echo #
+--echo # Package names with dot characters
+--echo #
+
+DELIMITER $$;
+CREATE PACKAGE "p1.p1" AS
+ PROCEDURE p1;
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY "p1.p1" AS
+ PROCEDURE p1 AS
+ BEGIN
+ SELECT 'This is p1' AS msg;
+ END;
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is f1';
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL "p1.p1"."p1";
+SELECT "p1.p1"."f1"();
+DROP PACKAGE "p1.p1";
+
+
+--echo #
+--echo # MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg1 AS
+ PROCEDURE p00();
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg1 AS
+ PROCEDURE p01() AS
+ BEGIN
+ SELECT 'This is p01' AS msg;
+ END;
+ PROCEDURE p00() AS
+ BEGIN
+ CREATE OR REPLACE VIEW v1 AS SELECT 1;
+ DROP VIEW v1;
+ CALL p01();
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL pkg1.p00;
+DROP PACKAGE pkg1;
+
+
+CREATE OR REPLACE TABLE t1 (a INT);
+CREATE OR REPLACE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=1;
+DELIMITER $$;
+CREATE OR REPLACE PACKAGE pkg1 AS
+ PROCEDURE p00();
+END;
+$$
+CREATE OR REPLACE PACKAGE BODY pkg1 AS
+ PROCEDURE p01() AS
+ BEGIN
+ SELECT 'This is p01' AS msg;
+ END;
+ PROCEDURE p00() AS
+ BEGIN
+ DROP TRIGGER tr1;
+ CALL p01();
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL pkg1.p00;
+DROP PACKAGE pkg1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-17387 MariaDB Server giving wrong error while executing select query from procedure
+--echo #
+
+CREATE TABLE t1 (
+ CTR varchar(2) NOT NULL,
+ COR varchar(3) NOT NULL,
+ DATE datetime NOT NULL,
+ CHAN varchar(4) NOT NULL,
+ CNO varchar(20) NOT NULL,
+ JOBN varchar(18) NOT NULL,
+ C1 varchar(30) DEFAULT NULL,
+ C2 varchar(30) DEFAULT NULL,
+ TIME datetime DEFAULT NULL,
+ AMT decimal(12,2) DEFAULT NULL,
+ DT datetime NOT NULL,
+ pk int(11) NOT NULL,
+ PRIMARY KEY (pk),
+ KEY Indx1 (JOBN)
+);
+
+DELIMITER $$;
+
+CREATE PACKAGE xyz IS
+ PROCEDURE xyz123(ctr IN VARCHAR2,Jn IN VARCHAR2,R OUT VARCHAR2);
+END;
+$$
+
+CREATE OR REPLACE PACKAGE BODY xyz IS
+ PROCEDURE xyz123(
+ ctr IN VARCHAR2,
+ Jn IN VARCHAR2,
+ R OUT VARCHAR2)
+ AS
+ lS NUMBER(10) :=0;
+ CURSOR cBPD IS
+ SELECT CTR, COR, DATE, CHAN, CNO, C1, C2, TIME, AMT
+ FROM t1 WHERE JOBN=Jn;
+ BEGIN
+ FOR lbpd IN cBPD
+ LOOP
+ lS:=lS+1;
+ END LOOP;
+ EXCEPTION
+ WHEN OTHERS THEN
+ BEGIN
+ SELECT SQLERRM;
+ END;
+ END;
+END $$
+DELIMITER ;$$
+
+CALL xyz.xyz123(17,18,@R);
+DROP PACKAGE xyz;
+DROP TABLE t1;
+--disable_prepare_warnings
+
+
+--echo #
+--echo # MDEV-28166 sql_mode=ORACLE: fully qualified package function calls do not work: db.pkg.func()
+--echo #
+
+--error ER_WRONG_DB_NAME
+SELECT `db `.pkg.func();
+--error ER_SP_WRONG_NAME
+SELECT db.`pkg `.func();
+--error ER_SP_WRONG_NAME
+SELECT db.pkg.`func `();
+
+
+CREATE DATABASE db1;
+USE db1;
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ FUNCTION f1 RETURN TEXT;
+ FUNCTION f2_db1_pkg1_f1 RETURN TEXT;
+ FUNCTION f2_pkg1_f1 RETURN TEXT;
+ FUNCTION f2_f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY pkg1
+AS
+ FUNCTION f1 RETURN TEXT IS
+ BEGIN
+ RETURN 'This is db1.pkg1.f1';
+ END;
+ FUNCTION f2_db1_pkg1_f1 RETURN TEXT IS
+ BEGIN
+ RETURN db1.pkg1.f1();
+ END;
+ FUNCTION f2_pkg1_f1 RETURN TEXT IS
+ BEGIN
+ RETURN pkg1.f1();
+ END;
+ FUNCTION f2_f1 RETURN TEXT IS
+ BEGIN
+ RETURN f1();
+ END;
+END;
+$$
+DELIMITER ;$$
+
+USE db1;
+SELECT pkg1.f2_db1_pkg1_f1();
+SELECT pkg1.f2_pkg1_f1();
+SELECT pkg1.f2_f1();
+
+SELECT db1.pkg1.f2_db1_pkg1_f1();
+SELECT db1.pkg1.f2_pkg1_f1();
+SELECT db1.pkg1.f2_f1();
+
+USE test;
+SELECT db1.pkg1.f2_db1_pkg1_f1();
+SELECT db1.pkg1.f2_pkg1_f1();
+SELECT db1.pkg1.f2_f1();
+
+DROP DATABASE db1;
+
+
+#
+# Testing db.pkg.func() in the package initialization section
+#
+
+CREATE DATABASE db1;
+CREATE DATABASE db2;
+
+DELIMITER $$;
+CREATE PACKAGE db1.pkg1 AS
+ FUNCTION f1 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY db1.pkg1 AS
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is db1.pkg1.f1';
+ END;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PACKAGE db2.pkg1 AS
+ FUNCTION f1 RETURN TEXT;
+ FUNCTION var1 RETURN TEXT;
+ FUNCTION var2 RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY db2.pkg1 AS
+ m_var1 TEXT;
+ m_var2 TEXT;
+ FUNCTION f1 RETURN TEXT AS
+ BEGIN
+ RETURN 'This is db2.pkg1.f1';
+ END;
+ FUNCTION var1 RETURN TEXT AS
+ BEGIN
+ RETURN m_var1;
+ END;
+ FUNCTION var2 RETURN TEXT AS
+ BEGIN
+ RETURN m_var2;
+ END;
+BEGIN
+ m_var1:= db1.pkg1.f1();
+ m_var2:= db2.pkg1.f1();
+END;
+$$
+DELIMITER ;$$
+
+SELECT db2.pkg1.var1(), db2.pkg1.var2();
+
+DROP DATABASE db1;
+DROP DATABASE db2;
+
+#
+# Make sure fully qualified package function call does not support AS syntax:
+# SELECT db.pkg.func(10 AS a);
+#
+
+DELIMITER $$;
+CREATE PACKAGE pkg1 AS
+ FUNCTION f1(a TEXT) RETURN TEXT;
+END;
+$$
+CREATE PACKAGE BODY pkg1 AS
+ FUNCTION f1(a TEXT) RETURN TEXT AS
+ BEGIN
+ RETURN a;
+ END;
+END;
+$$
+DELIMITER ;$$
+SELECT test.pkg1.f1('xxx');
+--error ER_PARSE_ERROR
+SELECT test.pkg1.f1('xxx' AS a);
+DROP PACKAGE pkg1;
+
+
+--echo #
+--echo # MDEV-19328 sql_mode=ORACLE: Package function in VIEW
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PACKAGE test1 AS
+ FUNCTION f_test RETURN number;
+END test1;
+$$
+CREATE PACKAGE BODY test1
+AS
+ FUNCTION f_test RETURN NUMBER IS
+ BEGIN
+ RETURN 1;
+ END;
+END test1;
+$$
+DELIMITER ;$$
+
+
+SET sql_mode=ORACLE;
+CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test();
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+SET sql_mode=DEFAULT;
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+DROP VIEW v_test;
+
+
+SET sql_mode=DEFAULT;
+--error ER_SP_DOES_NOT_EXIST
+CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test1.f_test();
+
+
+SET sql_mode=ORACLE;
+CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test();
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+SET sql_mode=DEFAULT;
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+DROP VIEW v_test;
+
+
+SET sql_mode=DEFAULT;
+CREATE VIEW v_test AS SELECT 1 AS c1 FROM DUAL WHERE 1=test.test1.f_test();
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+SET sql_mode=ORACLE;
+SELECT * FROM v_test;
+--vertical_results
+SHOW CREATE VIEW v_test;
+--horizontal_results
+DROP VIEW v_test;
+
+SET sql_mode=ORACLE;
+DROP PACKAGE test1;
+
+
+--echo #
+--echo # MDEV-19804 sql_mode=ORACLE: call procedure in packages
+--echo #
+
+--error ER_WRONG_DB_NAME
+CALL `db1 `.pkg.p;
+--error ER_SP_WRONG_NAME
+CALL db1.`pkg `.p;
+--error ER_SP_WRONG_NAME
+CALL db1.pkg.`p `;
+
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE PACKAGE pkg1 as
+ PROCEDURE p1();
+END;
+$$
+CREATE PACKAGE BODY pkg1 as
+ PROCEDURE p1() as
+ BEGIN
+ SELECT 'test-function' AS c1;
+ END;
+END;
+$$
+DELIMITER ;$$
+
+CALL pkg1.p1;
+CALL test.pkg1.p1;
+
+# In sql_mode=DEFAULT we support fully qualified package function names
+# (this is needed for VIEWs). Let's make sure we also support fully
+# qualified package procedure names, for symmetry
+
+SET sql_mode=DEFAULT;
+CALL test.pkg1.p1;
+SET sql_mode=ORACLE;
+
+DELIMITER $$;
+BEGIN
+ CALL pkg1.p1;
+ CALL test.pkg1.p1;
+END
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+BEGIN
+ pkg1.p1;
+ test.pkg1.p1;
+END
+$$
+DELIMITER ;$$
+
+DROP PACKAGE pkg1;
+
+
+#
+# Testing packages in different databases calling each other
+# in routines and in the initialization section.
+#
+
+CREATE DATABASE db1;
+DELIMITER $$;
+CREATE PACKAGE db1.pkg1 AS
+ PROCEDURE p1(a OUT TEXT);
+END;
+$$
+CREATE PACKAGE BODY db1.pkg1 AS
+ PROCEDURE p1(a OUT TEXT) AS
+ BEGIN
+ a:= 'This is db1.pkg1.p1';
+ END;
+END;
+$$
+DELIMITER ;$$
+
+CREATE DATABASE db2;
+DELIMITER $$;
+CREATE PACKAGE db2.pkg1 AS
+ FUNCTION var1 RETURN TEXT;
+ PROCEDURE p1(a OUT TEXT);
+ PROCEDURE p2_db1_pkg1_p1;
+END;
+$$
+CREATE PACKAGE BODY db2.pkg1 AS
+ m_var1 TEXT;
+ FUNCTION var1 RETURN TEXT AS
+ BEGIN
+ RETURN m_var1;
+ END;
+ PROCEDURE p1(a OUT TEXT) AS
+ BEGIN
+ a:= 'This is db2.pkg1.p1';
+ END;
+ PROCEDURE p2_db1_pkg1_p1 AS
+ a TEXT;
+ BEGIN
+ db1.pkg1.p1(a);
+ SELECT a;
+ END;
+BEGIN
+ db1.pkg1.p1(m_var1);
+END;
+$$
+DELIMITER ;$$
+
+SELECT db2.pkg1.var1();
+CALL db2.pkg1.p2_db1_pkg1_p1;
+--enable_ps2_protocol
+
+DROP DATABASE db1;
+DROP DATABASE db2;
+
+
+--echo #
+--echo # MDEV-29370 Functions in packages are slow and seems to ignore deterministic
+--echo #
+
+SET SQL_MODE=ORACLE;
+
+CREATE TABLE t1 (c1 CHAR(1));
+
+DELIMITER //;
+CREATE FUNCTION f1_deterministic()
+RETURN CHAR(1)
+DETERMINISTIC
+IS
+BEGIN
+ RETURN 'X';
+END;
+//
+
+CREATE FUNCTION f2_not_deterministic()
+RETURN CHAR(1)
+IS
+BEGIN
+ RETURN 'X';
+END;
+//
+
+CREATE PACKAGE pkg1
+IS
+ PROCEDURE t1_populate(numrows INTEGER);
+ FUNCTION f3_deterministic() RETURN CHAR(1) DETERMINISTIC;
+ FUNCTION f4_not_deterministic() RETURN CHAR(1);
+END;
+//
+
+CREATE PACKAGE BODY pkg1
+IS
+ PROCEDURE t1_populate(numrounds INTEGER)
+ IS
+ i INTEGER;
+ BEGIN
+ INSERT INTO t1 VALUES('Y');
+ FOR i IN 1..numrounds LOOP
+ INSERT INTO t1 SELECT * FROM t1;
+ END LOOP;
+ END;
+ FUNCTION f3_deterministic() RETURN CHAR(1) DETERMINISTIC COMMENT 'xxx'
+ IS
+ BEGIN
+ RETURN 'X';
+ END;
+ FUNCTION f4_not_deterministic() RETURN CHAR(1)
+ IS
+ BEGIN
+ RETURN 'X';
+ END;
+END;
+//
+DELIMITER ;//
+
+CALL pkg1.t1_populate(3);
+
+EXPLAIN EXTENDED SELECT 'Deterministic function', COUNT(*) FROM t1 WHERE c1 = f1_deterministic();
+EXPLAIN EXTENDED SELECT 'Non-deterministic function', COUNT(*) FROM t1 WHERE c1 = f2_not_deterministic();
+EXPLAIN EXTENDED SELECT 'Deterministic package function', COUNT(*) FROM t1 WHERE c1 = pkg1.f3_deterministic();
+EXPLAIN EXTENDED SELECT 'Non-deterministic package function', COUNT(*) FROM t1 WHERE c1 = pkg1.f4_not_deterministic();
+
+DROP TABLE t1;
+DROP FUNCTION f1_deterministic;
+DROP FUNCTION f2_not_deterministic;
+DROP PACKAGE pkg1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-param.inc b/mysql-test/suite/compat/oracle/t/sp-param.inc
new file mode 100644
index 00000000..35bce8ac
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-param.inc
@@ -0,0 +1,9 @@
+--eval CREATE FUNCTION f1(param $type) RETURN $type AS BEGIN RETURN param; END;
+SHOW CREATE FUNCTION f1;
+
+--eval SELECT LENGTH(f1(REPEAT('a',$length)));
+--eval CREATE TABLE t1 AS SELECT f1(REPEAT('a',$length)) AS a;
+
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP FUNCTION f1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-param.test b/mysql-test/suite/compat/oracle/t/sp-param.test
new file mode 100644
index 00000000..b887858d
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-param.test
@@ -0,0 +1,363 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10596 Allow VARCHAR and VARCHAR2 without length as a data type of routine parameters and in RETURN clause
+--echo #
+
+--let type = CHAR
+--let length = 2000
+--source sp-param.inc
+
+--let type = NCHAR
+--let length = 2000
+--source sp-param.inc
+
+--let type = BINARY
+--let length = 2000
+--source sp-param.inc
+
+--let type = VARCHAR
+--let length = 4000
+--source sp-param.inc
+
+--let type = VARCHAR2
+--let length = 4000
+--source sp-param.inc
+
+--let type = NVARCHAR
+--let length = 4000
+--source sp-param.inc
+
+--let type = VARBINARY
+--let length = 4000
+--source sp-param.inc
+
+--let type = RAW
+--let length = 4000
+--source sp-param.inc
+
+--echo
+--echo MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
+--echo
+set sql_mode= 'oracle,strict_trans_tables';
+delimiter /;
+CREATE OR REPLACE PROCEDURE p1(pinout INOUT varchar, pin IN varchar)
+AS
+BEGIN
+ pinout:=pin;
+END;
+/
+call p1(@w,'0123456789')
+/
+declare w varchar(10);
+begin
+ call p1(w,'0123456789');
+end;
+/
+--error ER_DATA_TOO_LONG
+declare w varchar(5);
+begin
+ call p1(w,'0123456789');
+end;
+/
+declare w varchar(20);
+begin
+ w:='aaa';
+ call p1(w,'0123456789');
+end;
+/
+--error ER_DATA_TOO_LONG
+declare w varchar(8);
+begin
+ w:='aaa';
+ call p1(w,'0123456789');
+end;
+/
+declare str varchar(6000);
+ pout varchar(6000);
+begin
+ str:=lpad('x',6000,'y');
+ call p1(pout,str);
+ select length(pout);
+end;
+/
+--error ER_DATA_TOO_LONG
+declare str varchar(6000);
+ pout varchar(4000);
+begin
+ str:=lpad('x',6000,'y');
+ call p1(pout,str);
+ select length(pout);
+end;
+/
+declare str varchar(40000);
+ pout varchar(60000);
+begin
+ str:=lpad('x',40000,'y');
+ call p1(pout,str);
+ select length(pout);
+end;
+/
+--error ER_DATA_TOO_LONG
+declare str text(80000);
+ pout text(80000);
+begin
+ str:=lpad('x',80000,'y');
+ call p1(pout,str);
+ select length(pout);
+end;
+/
+declare str text(80000);
+ pout text(80000);
+begin
+ str:=lpad('x',60000,'y');
+ call p1(pout,str);
+ select length(pout);
+end;
+/
+drop procedure p1
+/
+DELIMITER ;/
+
+
+#
+# Procedure, non-strict mode
+#
+
+SET sql_mode=ORACLE;
+DELIMITER /;
+CREATE PROCEDURE p1(pinout INOUT varchar, pin IN varchar)
+AS
+BEGIN
+ pinout:=pin;
+END;
+/
+CREATE PROCEDURE p2(len INT)
+AS
+ pinout VARCHAR(10);
+ pin VARCHAR(30);
+BEGIN
+ pin:= REPEAT('x', len);
+ p1(pinout, pin);
+ SELECT LENGTH(pinout);
+END;
+/
+DELIMITER ;/
+CALL p2(10);
+CALL p2(11);
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+
+#
+# Function, not-strict mode
+#
+
+SET sql_mode=ORACLE;
+DELIMITER /;
+CREATE FUNCTION f1(pin VARCHAR, padlen INT) RETURN TEXT
+AS
+BEGIN
+ pin:=LPAD(pin, padlen);
+ RETURN pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT :='x';
+BEGIN
+ SELECT LENGTH(f1(str,padlen));
+END;
+/
+DELIMITER ;/
+CALL p2(65535);
+CALL p2(65536);
+DROP PROCEDURE p2;
+DROP FUNCTION f1;
+
+
+#
+# Procedure, utf8 formal parameter, latin actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE PROCEDURE p1(pinout INOUT VARCHAR CHARACTER SET utf8,
+ pin IN VARCHAR CHARACTER SET utf8)
+AS
+BEGIN
+ pinout:=pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str VARCHAR(40000) CHARACTER SET latin1;
+ pout VARCHAR(60000) CHARACTER SET latin1;
+BEGIN
+ str:=lpad('x',padlen,'y');
+ p1(pout,str);
+ SELECT length(pout);
+END;
+/
+DELIMITER ;/
+CALL p2(21844);
+--error ER_DATA_TOO_LONG
+CALL p2(21845);
+--error ER_DATA_TOO_LONG
+CALL p2(21846);
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+#
+# Procedure, utf8 formal parameter, utf8 actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE PROCEDURE p1(pinout INOUT VARCHAR CHARACTER SET utf8,
+ pin IN VARCHAR CHARACTER SET utf8)
+AS
+BEGIN
+ pinout:=pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT CHARACTER SET utf8;
+ pout TEXT CHARACTER SET utf8;
+BEGIN
+ str:=lpad('x',padlen,'y');
+ p1(pout,str);
+ SELECT length(pout);
+END;
+/
+DELIMITER ;/
+CALL p2(21844);
+--error ER_DATA_TOO_LONG
+CALL p2(21845);
+--error ER_DATA_TOO_LONG
+CALL p2(21846);
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+#
+# Function, latin1 formal parameter, latin1 actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE FUNCTION f1(pin VARCHAR CHARACTER SET latin1, padlen INT) RETURN TEXT
+AS
+BEGIN
+ pin:=LPAD(pin, padlen);
+ RETURN pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT CHARACTER SET latin1 :='x';
+BEGIN
+ SELECT LENGTH(f1(str,padlen));
+END;
+/
+DELIMITER ;/
+CALL p2(65532);
+--error ER_DATA_TOO_LONG
+CALL p2(65533);
+--error ER_DATA_TOO_LONG
+CALL p2(65534);
+--error ER_DATA_TOO_LONG
+CALL p2(65535);
+--error ER_DATA_TOO_LONG
+CALL p2(65536);
+DROP PROCEDURE p2;
+DROP FUNCTION f1;
+
+
+#
+# Function, utf8 formal parameter, utf8 actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE FUNCTION f1(pin VARCHAR CHARACTER SET utf8, padlen INT) RETURN TEXT
+AS
+BEGIN
+ pin:=LPAD(pin, padlen);
+ RETURN pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT CHARACTER SET utf8 := 'x';
+BEGIN
+ SELECT LENGTH(f1(str,padlen));
+END;
+/
+DELIMITER ;/
+CALL p2(21844);
+--error ER_DATA_TOO_LONG
+CALL p2(21845);
+--error ER_DATA_TOO_LONG
+CALL p2(21846);
+DROP PROCEDURE p2;
+DROP FUNCTION f1;
+
+
+#
+# Function, utf8 formal parameter, latin1 actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE FUNCTION f1(pin VARCHAR CHARACTER SET utf8, padlen INT) RETURN TEXT
+AS
+BEGIN
+ pin:=LPAD(pin, padlen);
+ RETURN pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT CHARACTER SET latin1 := 'x';
+BEGIN
+ SELECT LENGTH(f1(str,padlen));
+END;
+/
+DELIMITER ;/
+CALL p2(21844);
+--error ER_DATA_TOO_LONG
+CALL p2(21845);
+--error ER_DATA_TOO_LONG
+CALL p2(21846);
+DROP PROCEDURE p2;
+DROP FUNCTION f1;
+
+
+#
+# Function, latin1 formal parameter, utf8 actual parameter
+#
+
+SET sql_mode='ORACLE,STRICT_TRANS_TABLES';
+DELIMITER /;
+CREATE FUNCTION f1(pin VARCHAR CHARACTER SET latin1, padlen INT) RETURN TEXT
+AS
+BEGIN
+ pin:=LPAD(pin, padlen);
+ RETURN pin;
+END;
+/
+CREATE PROCEDURE p2(padlen INT) AS
+ str TEXT CHARACTER SET utf8 := 'x';
+BEGIN
+ SELECT LENGTH(f1(str,padlen));
+END;
+/
+DELIMITER ;/
+CALL p2(65532);
+--error ER_DATA_TOO_LONG
+CALL p2(65533);
+--error ER_DATA_TOO_LONG
+CALL p2(65534);
+--error ER_DATA_TOO_LONG
+CALL p2(65535);
+--error ER_DATA_TOO_LONG
+CALL p2(65536);
+DROP PROCEDURE p2;
+DROP FUNCTION f1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-row-vs-var.inc b/mysql-test/suite/compat/oracle/t/sp-row-vs-var.inc
new file mode 100644
index 00000000..14f6f7df
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-row-vs-var.inc
@@ -0,0 +1,6 @@
+--let $query= CREATE PROCEDURE p1() AS var $type; rec ROW(var $type); BEGIN CREATE TABLE t1 AS SELECT var,rec.var FROM DUAL;END
+--eval $query
+CALL p1();
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-row.test b/mysql-test/suite/compat/oracle/t/sp-row.test
new file mode 100644
index 00000000..c7658c76
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-row.test
@@ -0,0 +1,2418 @@
+
+SET sql_mode=ORACLE;
+
+
+--echo #
+--echo # MDEV-10914 ROW data type for stored routine variables
+--echo #
+
+
+
+--echo #
+--echo # ROW of ROWs is not supported yet
+--echo #
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1()
+AS
+ a ROW(a ROW(a INT));
+BEGIN
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Returning the entire ROW parameter from a function
+--echo #
+# TODO: this should probably return an error at compile time
+DELIMITER $$;
+CREATE FUNCTION f1(a ROW(a INT, b INT)) RETURN INT
+AS
+BEGIN
+ RETURN a;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT f1(ROW(10,20));
+DROP FUNCTION f1;
+
+
+
+--echo #
+--echo # ROW as an SP parameter
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(a ROW(a INT,b INT)) RETURN INT
+AS
+BEGIN
+ RETURN a.b;
+END;
+$$
+CREATE PROCEDURE p1()
+AS
+ a ROW(a INT,b INT):=(11,21);
+BEGIN
+ SELECT f1(a);
+END;
+$$
+DELIMITER ;$$
+SELECT f1(ROW(10,20));
+--error ER_OPERAND_COLUMNS
+SELECT f1(10);
+--error ER_OPERAND_COLUMNS
+SELECT f1(ROW(10,20,30));
+CALL p1();
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a ROW(a INT,b INT))
+AS
+BEGIN
+ SELECT a.a, a.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1(ROW(10,20));
+--error ER_OPERAND_COLUMNS
+CALL p1(10);
+--error ER_OPERAND_COLUMNS
+CALL p1(ROW(10,20,30));
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW as an SP OUT parameter
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT ROW(a INT,b INT))
+AS
+BEGIN
+ a.a:=10;
+ a.b:=20;
+END;
+$$
+CREATE PROCEDURE p2
+AS
+ a ROW(a INT,b INT):=(11,21);
+BEGIN
+ CALL p1(a);
+ SELECT a.a,a.b;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW as an SP return value is not supported yet
+--echo #
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE FUNCTION p1() RETURN ROW(a INT)
+AS
+BEGIN
+ RETURN NULL;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Diplicate row field
+--echo #
+DELIMITER $$;
+--error ER_DUP_FIELDNAME
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT, a DOUBLE);
+BEGIN
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Bad scalar default value
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT, b DOUBLE):= 1;
+BEGIN
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Bad ROW default value with a wrong number of fields
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT, b DOUBLE):= ROW(1,2,3);
+BEGIN
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Scalar variable vs table alias cause no ambiguity
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a INT;
+BEGIN
+ -- a.x is a table column here (not a row variable field)
+ SELECT a.x FROM a;
+ SELECT a.x FROM t1 a;
+END;
+$$
+DELIMITER ;$$
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Using the entire ROW variable in select list
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT);
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Using the entire ROW variable in functions
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT);
+BEGIN
+ SELECT COALESCE(a);
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT COALESCE(a);
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT);
+BEGIN
+ SELECT a+1;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT a+1;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Comparing the entire ROW to a scalar value
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT a=1;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT 1=a;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Passing the entire ROW to a stored function
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+BEGIN
+ RETURN a;
+END;
+$$
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT,b INT);
+BEGIN
+ SELECT f1(a);
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
+
+
+DELIMITER $$;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+BEGIN
+ RETURN a;
+END;
+$$
+CREATE PROCEDURE p1()
+AS
+ a ROW (a INT);
+BEGIN
+ SELECT f1(a);
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+CALL p1();
+DROP PROCEDURE p1;
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Assigning a scalar value to a ROW variable with 1 column
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec ROW(a INT);
+BEGIN
+ rec:=1;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning a scalar value to a ROW variable with 2 columns
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ rec:=1;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning a ROW value to a ROW variable with different number of columns
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ rec:=ROW(1,2,3);
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Returning the entire ROW from a function is not supported yet
+--echo # This syntax would be needed: SELECT f1().x FROM DUAL;
+--echo #
+DELIMITER $$;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ rec ROW(a INT);
+BEGIN
+ RETURN rec;
+END;
+$$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT f1(10);
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Using the entire ROW in SELECT..CREATE
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ CREATE TABLE t1 AS SELECT rec;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Using the entire ROW in LIMIT
+--echo #
+DELIMITER $$;
+--error ER_WRONG_SPVAR_TYPE_IN_LIMIT
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT);
+BEGIN
+ rec.a:= '10';
+ SELECT * FROM t1 LIMIT rec;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Setting ROW fields using a SET command
+--echo #
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec ROW(a INT,b DOUBLE,c VARCHAR(10));
+ a INT;
+BEGIN
+ SET @a= 10, rec.a=10, rec.b=20, rec.c= 'test', a= 5;
+ SELECT rec.a, rec.b, rec.c, a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning a ROW variable from a ROW value
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ rec:=ROW(1,2);
+ SELECT rec.a, rec.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning a ROW variable from another ROW value
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 ROW(a INT,b INT);
+ rec2 ROW(a INT,b INT);
+BEGIN
+ rec1:=ROW(1,2);
+ rec2:=rec1;
+ SELECT rec2.a, rec2.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Comparing a ROW variable to a ROW() function
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec ROW(a INT,b INT);
+BEGIN
+ rec.a:= 1;
+ rec.b:= 2;
+ SELECT rec=(0,0), rec=ROW(0,0), (0,0)=rec, ROW(0,0)=rec;
+ SELECT rec=(1,2), rec=ROW(1,2), (1,2)=rec, ROW(1,2)=rec;
+ SELECT rec=(NULL,0), rec=ROW(NULL,0);
+ SELECT rec=(NULL,2), rec=ROW(NULL,2);
+ SELECT rec<>(0,0), rec<>ROW(0,0);
+ SELECT rec<>(1,2), rec<>ROW(1,2);
+ SELECT rec<>(NULL,0), rec<>ROW(NULL,0);
+ SELECT rec<>(NULL,2), rec<>ROW(NULL,2);
+ SELECT rec IN ((0,0)), rec IN (ROW(0,0));
+ SELECT rec IN ((1,2)), rec IN (ROW(1,2));
+ SELECT rec IN ((0,NULL),(1,2));
+ SELECT rec NOT IN ((0,NULL),(1,1));
+ SELECT rec NOT IN ((1,NULL),(1,1));
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Comparing a ROW variable to another ROW variable
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1
+AS
+ rec1,rec2,rec3 ROW(a INT,b INT);
+BEGIN
+ rec1.a:= 1;
+ rec1.b:= 2;
+ rec2.a:= 11;
+ rec2.b:= 12;
+ rec3.a:= 11;
+ rec3.b:= 12;
+ SELECT rec1=rec2, rec2=rec1, rec2=rec3, rec3=rec2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Referencing a non-existing row variable
+--echo #
+DELIMITER $$;
+--error ER_UNKNOWN_STRUCTURED_VARIABLE
+CREATE PROCEDURE p1()
+AS
+BEGIN
+ SET a.b=1;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_UNKNOWN_STRUCTURED_VARIABLE
+CREATE PROCEDURE p1()
+AS
+BEGIN
+ a.b:=1;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Referencing a non-existing row field
+--echo #
+DELIMITER $$;
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CREATE PROCEDURE p1()
+AS
+ a ROW(a INT,b INT);
+BEGIN
+ SELECT a.c FROM t1;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # ROW and scalar variables with the same name shadowing each other
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW(a INT);
+BEGIN
+ a.a:=100;
+ DECLARE
+ a INT:= 200;
+ BEGIN
+ SELECT a;
+ DECLARE
+ a ROW(a INT);
+ BEGIN
+ a.a:=300;
+ SELECT a.a;
+ END;
+ SELECT a;
+ END;
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW with good default values
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a ROW(a INT,b INT):= (10,20);
+ b ROW(a INT,b INT):= (11,21);
+ c ROW(a INT,b INT):= a;
+BEGIN
+ SELECT a.a, a.b, b.a, b.b, c.a, c.b FROM DUAL;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW in WHERE clause
+--echo #
+
+CREATE TABLE t1 (a INT,b INT);
+INSERT INTO t1 VALUES (10,20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT,b INT):=ROW(10,20);
+BEGIN
+ SELECT * FROM t1 WHERE rec=ROW(a,b);
+ SELECT * FROM t1 WHERE ROW(a,b)=rec;
+ SELECT * FROM t1 WHERE rec=ROW(10,20);
+ SELECT * FROM t1 WHERE ROW(10,20)=rec;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW fields in WHERE clause
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT);
+BEGIN
+ rec.a:= 10;
+ SELECT * FROM t1 WHERE a=rec.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW fields in HAVING clause
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT);
+BEGIN
+ rec.a:= 10;
+ SELECT * FROM t1 HAVING a=rec.a;
+ SELECT * FROM t1 HAVING MIN(a)=rec.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW fields in LIMIT clause
+--echo #
+
+CREATE TABLE t1 (a INT);
+--error ER_SP_UNDECLARED_VAR
+SELECT 1 FROM t1 LIMIT t1.a;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT);
+BEGIN
+ rec.a:= 10;
+ SELECT * FROM t1 LIMIT rec.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+--error ER_WRONG_SPVAR_TYPE_IN_LIMIT
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a VARCHAR(10));
+BEGIN
+ rec.a:= '10';
+ SELECT * FROM t1 LIMIT rec.a;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # ROW fields in select list
+--echo #
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ t1 ROW(a INT);
+BEGIN
+ t1.a:= 10;
+ SELECT t1.a, 'This is the variable t1.a value, rather than the column t1.a' AS comm FROM t1;
+ SELECT t1.a, t2.a, t1.a+t2.a FROM t1 t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW fields as insert values
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec ROW(a INT, b VARCHAR(10));
+BEGIN
+ rec.a:= 10;
+ rec.b:= 'test';
+ INSERT INTO t1 VALUES (rec.a, rec.b);
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # ROW fields as SP out parameters
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT INT, b OUT VARCHAR)
+AS
+BEGIN
+ a:= 10;
+ b:= 'test';
+END;
+$$
+CREATE PROCEDURE p2
+AS
+ rec ROW(a INT, b VARCHAR(10));
+BEGIN
+ CALL p1(rec.a, rec.b);
+ SELECT rec.a, rec.b;
+END;
+$$
+DELIMITER ;$$
+CALL p2;
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+
+--echo #
+--echo # ROW fields as dynamic SQL out parameters
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT INT, b OUT VARCHAR)
+AS
+BEGIN
+ a:= 20;
+ b:= 'test-dynamic-sql';
+END;
+$$
+CREATE PROCEDURE p2
+AS
+ rec ROW(a INT, b VARCHAR(30));
+BEGIN
+ EXECUTE IMMEDIATE 'CALL p1(?,?)' USING rec.a, rec.b;
+ SELECT rec.a, rec.b;
+END;
+$$
+DELIMITER ;$$
+CALL p2;
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+
+--echo #
+--echo # ROW fields as SELECT..INTO targets
+--echo #
+
+--enable_prepare_warnings
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT, b VARCHAR(10));
+BEGIN
+ SELECT 10,'test' INTO rec.a,rec.b;
+ SELECT rec.a, rec.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+--disable_prepare_warnings
+
+--echo #
+--echo # Implicit default NULL handling
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec ROW(a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,0), e TIME, f DATETIME);
+BEGIN
+ SELECT rec.a, rec.b, rec.c, rec.d, rec.e, rec.f FROM DUAL;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # NULL handling
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1 ROW(a INT, b VARCHAR(10)):=(NULL,NULL);
+ rec2 ROW(a INT, b VARCHAR(10)):=rec1;
+BEGIN
+ SELECT rec1.a, rec1.b, rec2.a, rec2.b;
+
+ rec1:= (10,20);
+ rec2:= rec1;
+ SELECT rec1.a, rec1.b, rec2.a, rec2.b;
+
+ rec1:= (NULL,20);
+ rec2:= rec1;
+ SELECT rec1.a, rec1.b, rec2.a, rec2.b;
+
+ rec1:= (10,NULL);
+ rec2:= rec1;
+ SELECT rec1.a, rec1.b, rec2.a, rec2.b;
+
+ rec1:= (NULL,NULL);
+ rec2:= rec1;
+ SELECT rec1.a, rec1.b, rec2.a, rec2.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Testing multiple ROW variable declarations
+--echo # This makes sure that fill_field_definitions() is called only once
+--echo # per a ROW field, so create length is not converted to internal length
+--echo # multiple times.
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec1, rec2, rec3 ROW(a VARCHAR(10) CHARACTER SET utf8);
+BEGIN
+ CREATE TABLE t1 AS SELECT rec1.a, rec2.a, rec3.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # INT
+--echo #
+
+--let type=INT
+--source sp-row-vs-var.inc
+
+--let type=INT(1)
+--source sp-row-vs-var.inc
+
+--let type=INT(2)
+--source sp-row-vs-var.inc
+
+--let type=INT(3)
+--source sp-row-vs-var.inc
+
+--let type=INT(4)
+--source sp-row-vs-var.inc
+
+--let type=INT(5)
+--source sp-row-vs-var.inc
+
+--let type=INT(6)
+--source sp-row-vs-var.inc
+
+--let type=INT(7)
+--source sp-row-vs-var.inc
+
+--let type=INT(8)
+--source sp-row-vs-var.inc
+
+--let type=INT(9)
+--source sp-row-vs-var.inc
+
+--let type=INT(10)
+--source sp-row-vs-var.inc
+
+--let type=INT(11)
+--source sp-row-vs-var.inc
+
+--let type=INT(12)
+--source sp-row-vs-var.inc
+
+--let type=INT(13)
+--source sp-row-vs-var.inc
+
+--let type=INT(14)
+--source sp-row-vs-var.inc
+
+--let type=INT(20)
+--source sp-row-vs-var.inc
+
+--let type=INT(21)
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # TINYINT
+--echo #
+
+--let type=TINYINT
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(1)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(2)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(3)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(4)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(5)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(6)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(7)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(8)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(9)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(10)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(11)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(12)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(13)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(14)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(20)
+--source sp-row-vs-var.inc
+
+--let type=TINYINT(21)
+--source sp-row-vs-var.inc
+
+--echo #
+--echo # SMALLINT
+--echo #
+
+--let type=SMALLINT
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(1)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(2)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(3)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(4)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(5)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(6)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(7)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(8)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(9)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(10)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(11)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(12)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(13)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(14)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(20)
+--source sp-row-vs-var.inc
+
+--let type=SMALLINT(21)
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # MEDIUMINT
+--echo #
+
+--let type=MEDIUMINT
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(1)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(2)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(3)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(4)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(5)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(6)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(7)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(8)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(9)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(10)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(11)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(12)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(13)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(14)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(20)
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMINT(21)
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # BIGINT
+--echo #
+
+--let type=BIGINT
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(1)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(2)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(3)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(4)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(5)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(6)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(7)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(8)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(9)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(10)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(11)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(12)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(13)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(14)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(20)
+--source sp-row-vs-var.inc
+
+--let type=BIGINT(21)
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # DOUBLE
+--echo #
+
+--let type=DOUBLE
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,1)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,2)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,3)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,4)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,5)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,6)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,7)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,8)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,9)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,10)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,11)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,12)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,13)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,14)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,20)
+--source sp-row-vs-var.inc
+
+--let type=DOUBLE(30,21)
+--source sp-row-vs-var.inc
+
+--echo #
+--echo # VARCHAR
+--echo #
+
+--let type=CHAR
+--source sp-row-vs-var.inc
+
+--let type=BINARY
+--source sp-row-vs-var.inc
+
+--let type=CHAR(1)
+--source sp-row-vs-var.inc
+
+--let type=CHAR(10)
+--source sp-row-vs-var.inc
+
+--let type=NCHAR(10)
+--source sp-row-vs-var.inc
+
+--let type=BINARY(10)
+--source sp-row-vs-var.inc
+
+--let type=VARBINARY(10)
+--source sp-row-vs-var.inc
+
+--let type=VARCHAR(10)
+--source sp-row-vs-var.inc
+
+--let type=VARCHAR(10) CHARACTER SET utf8
+--source sp-row-vs-var.inc
+
+--let type=VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin
+--source sp-row-vs-var.inc
+
+--echo #
+--echo # TIME
+--echo #
+
+--let type=TIME
+--source sp-row-vs-var.inc
+
+--let type=TIME(1)
+--source sp-row-vs-var.inc
+
+--let type=TIME(2)
+--source sp-row-vs-var.inc
+
+--let type=TIME(3)
+--source sp-row-vs-var.inc
+
+--let type=TIME(4)
+--source sp-row-vs-var.inc
+
+--let type=TIME(5)
+--source sp-row-vs-var.inc
+
+--let type=TIME(6)
+--source sp-row-vs-var.inc
+
+--echo #
+--echo # DATETIME
+--echo #
+
+--let type=DATETIME
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(1)
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(2)
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(3)
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(4)
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(5)
+--source sp-row-vs-var.inc
+
+--let type=DATETIME(6)
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # LOB
+--echo #
+
+--let type=TEXT
+--source sp-row-vs-var.inc
+
+--let type=TINYTEXT
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMTEXT
+--source sp-row-vs-var.inc
+
+--let type=LONGTEXT
+--source sp-row-vs-var.inc
+
+--let type=TEXT CHARACTER SET utf8
+--source sp-row-vs-var.inc
+
+--let type=TINYTEXT CHARACTER SET utf8
+--source sp-row-vs-var.inc
+
+--let type=MEDIUMTEXT CHARACTER SET utf8
+--source sp-row-vs-var.inc
+
+--let type=LONGTEXT CHARACTER SET utf8
+--source sp-row-vs-var.inc
+
+
+--echo #
+--echo # End of MDEV-10914 ROW data type for stored routine variables
+--echo #
+
+
+--echo #
+--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
+--echo #
+
+--echo #
+--echo # Referring to a table in a non-existing database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec test2.t1%ROWTYPE;
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+--error ER_NO_SUCH_TABLE
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Referring to a table in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec t1%ROWTYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Referring to a table in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec test.t1%ROWTYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Referring to a view in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec v1%ROWTYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CREATE VIEW v1 AS SELECT * FROM t1;
+CALL p1();
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Referring to a view in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec test.v1%ROWTYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
+CREATE VIEW v1 AS SELECT * FROM t1;
+CALL p1();
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Checking that all table%ROWTYPE fields are NULL by default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ SELECT rec1.a, rec1.b, rec1.c, rec1.d;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A table%ROWTYPE variable with a ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb');
+BEGIN
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A table%ROWTYPE variable with an incompatible ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb','ccc');
+BEGIN
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A table%ROWTYPE variable with a ROW variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
+ rec2 t1%ROWTYPE DEFAULT rec1;
+BEGIN
+ SELECT rec2.a, rec2.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A ROW variable using a table%ROWTYPE variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE := ROW(10,'bbb');
+ rec2 ROW(a INT, b VARCHAR(10)):= rec1;
+BEGIN
+ SELECT rec2.a, rec2.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning table%ROWTYPE variables with a different column count
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE);
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+ rec2 t2%ROWTYPE;
+BEGIN
+ rec2:=rec1;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+ rec2 t2%ROWTYPE;
+BEGIN
+ rec1:=rec2;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning compatible table%ROWTYPE variables (equal number of fields)
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (x INT, y VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+ rec2 t2%ROWTYPE;
+BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ rec2:=rec1;
+ SELECT rec2.x, rec2.y;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning between incompatible table%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+ rec2 ROW(x INT,y INT,z INT);
+BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec2.z:= 30;
+ rec1:= rec2;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning between compatible table%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+ rec2 ROW(x INT,y INT);
+BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec1:= rec2;
+ SELECT rec1.a, rec1.b;
+ rec1.a:= 11;
+ rec1.b:= 21;
+ rec2:= rec1;
+ SELECT rec2.x, rec2.y;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning table%ROWTYPE from a ROW expression
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ rec1:= ROW(10,20);
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a table%ROWTYPE variable with a wrong field count
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE TABLE t2 (a INT, b VARCHAR(10));
+INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec2 t2%ROWTYPE;
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ CLOSE cur1;
+END;
+$$
+DELIMITER ;$$
+--error ER_SP_WRONG_NO_OF_FETCH_ARGS
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a table%ROWTYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE TABLE t2 LIKE t1;
+INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
+INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32);
+INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33);
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec t1%ROWTYPE;
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ OPEN cur;
+ LOOP
+ FETCH cur INTO rec;
+ EXIT WHEN cur%NOTFOUND;
+ SELECT rec.a, rec.b, rec.c, rec.d;
+ INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d);
+ END LOOP;
+ CLOSE cur;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+--echo #
+--echo # Fetching a cursor into a table%ROWTYPE variable with different column names
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (x INT, y VARCHAR(10));
+INSERT INTO t1 VALUES (10,'bbb');
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec2 t2%ROWTYPE;
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ SELECT rec2.x, rec2.y;
+ CLOSE cur1;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Fetching a cursor into a table%ROWTYPE variable, with truncation
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (a INT, b INT);
+INSERT INTO t1 VALUES (10,'11x');
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ rec2 t2%ROWTYPE;
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec2;
+ SELECT rec2.a, rec2.b;
+ CLOSE cur1;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t2;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # table%ROWTYPE variables are not allowed in LIMIT
+--echo #
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,2);
+DELIMITER $$;
+--error ER_WRONG_SPVAR_TYPE_IN_LIMIT
+CREATE PROCEDURE p1()
+AS
+ rec1 t1%ROWTYPE:=(1,2);
+BEGIN
+ SELECT * FROM t1 LIMIT rec1.a;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+--echo #
+--echo # table%ROWTYPE variable fields as OUT parameters
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
+AS
+BEGIN
+ a:=10;
+ b:='bb';
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ CALL p1(rec1.a, rec1.b);
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Passing the entire table%ROWTYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ SELECT a.a, a.b;
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ rec1 t1%ROWTYPE:= ROW(10,'bb');
+BEGIN
+ CALL p1(rec1);
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Passing the entire table%ROWTYPE variable as an OUT parameter
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ a:= ROW(10,'bb');
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ CALL p1(rec1);
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Assigning a table%ROWTYPE field to an OUT parameter
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1 (res IN OUT INTEGER)
+AS
+ rec1 t1%ROWTYPE:=ROW(10,'b0');
+BEGIN
+ res:=rec1.a;
+END;
+$$
+DELIMITER ;$$
+CALL p1(@res);
+SELECT @res;
+SET @res=NULL;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing Item_splocal_row_field_by_name::print
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec t1%ROWTYPE:=ROW(10,'bb');
+BEGIN
+ EXPLAIN EXTENDED SELECT rec.a, rec.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Non-existing field
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec t1%ROWTYPE;
+BEGIN
+ SELECT rec.c;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+ALTER TABLE t1 ADD c INT;
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that field names are case insensitive
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec t1%ROWTYPE:=ROW(10,'bb');
+BEGIN
+ SELECT rec.A, rec.B;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that table%ROWTYPE uses temporary tables vs shadowed real tables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ rec t1%ROWTYPE:=ROW(10,'bb');
+BEGIN
+ SELECT rec.A, rec.B;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+DROP TEMPORARY TABLE t1;
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing that the structure of table%ROWTYPE variables is determined at the very beginning and is not changed after ALTER
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ ALTER TABLE t1 ADD c INT;
+ DECLARE
+ rec t1%ROWTYPE; -- this will not have column "c"
+ BEGIN
+ rec.c:=10;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12291 Allow ROW variables as SELECT INTO targets
+--echo #
+
+
+--enable_prepare_warnings
+--echo # ROW variable with a wrong column count
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 ROW(a INT, b VARCHAR(32), c DOUBLE);
+BEGIN
+ SELECT * FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # Multiple ROW variables
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 ROW(a INT, b VARCHAR(32));
+BEGIN
+ SELECT * FROM t1 INTO rec1, rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # ROW variables working example
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 ROW(a INT, b VARCHAR(32));
+BEGIN
+ SELECT * FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # table%ROWTYPE variable with a wrong column count
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ SELECT 10,'a','b' FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # Multiple table%ROWTYPE variables
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ SELECT 10,'a' FROM t1 INTO rec1, rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # table%ROWTYPE working example
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ rec1 t1%ROWTYPE;
+BEGIN
+ SELECT * FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # cursor%ROWTYPE variable with a wrong column count
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur1 IS SELECT 10, 'b0', 'c0';
+ rec1 cur1%ROWTYPE;
+BEGIN
+ SELECT * FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # Multiple cursor%ROWTYPE variables
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ SELECT * FROM t1 INTO rec1, rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo # cursor%ROWTYPE working example
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ rec1 cur1%ROWTYPE;
+BEGIN
+ SELECT * FROM t1 INTO rec1;
+ SELECT rec1.a, rec1.b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+--disable_prepare_warnings
+
+--echo #
+--echo # MDEV-12347 Valgrind reports invalid read errors in Item_field_row::element_index_by_name
+--echo #
+
+# An additional test for MDEV-12347, to make sure that
+# Column_definition::interval creates a permanent copy of TYPELIB on
+# the memory root when processing %ROWTYPE for a table with ENUM/SET column,
+# rather than reuses the TYPELIB from table->field[i], which is freed in the
+# end of sp_rcontext::resolve_table_rowtype_ref().
+
+CREATE TABLE t1 (a INT, b ENUM('b0','b1','b12','b3'));
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ DECLARE
+ rec t1%ROWTYPE;
+ BEGIN
+ rec.b:='b0';
+ SELECT rec.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+CREATE TABLE t1 (a INT, b SET('b0','b1','b12','b3'));
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ DECLARE
+ rec t1%ROWTYPE;
+ BEGIN
+ rec.b:='b0';
+ SELECT rec.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-13273 Confusion between table alias and ROW type variable
+--echo #
+
+CREATE TABLE t1 (c1 INT, c2 INT);
+INSERT INTO t1 VALUES (0,0);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT;
+ b INT;
+BEGIN
+ -- a.c1 is a table column
+ SELECT a.c1 INTO b
+ FROM t1 a
+ WHERE a.c2 = 0;
+ SELECT b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 INT, c2 INT);
+INSERT INTO t1 VALUES (0,0);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a ROW (c1 INT, c2 INT) := ROW(101,102);
+ b INT;
+BEGIN
+ -- a.c1 is a ROW variable field
+ SELECT a.c1 INTO b
+ FROM t1 a
+ WHERE a.c2 = 102;
+ SELECT b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 INT, c2 INT);
+INSERT INTO t1 VALUES (0,0);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1%ROWTYPE := ROW (10,20);
+ b INT;
+BEGIN
+ -- a.c1 is a ROW variable field
+ SELECT a.c1 INTO b
+ FROM t1 a
+ WHERE a.c2 = 20;
+ SELECT b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (c1 INT, c2 INT);
+INSERT INTO t1 VALUES (0,0);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ a cur1%ROWTYPE := ROW (10,20);
+ b INT;
+BEGIN
+ -- a.c1 is a ROW variable field
+ SELECT a.c1 INTO b
+ FROM t1 a
+ WHERE a.c2 = 20;
+ SELECT b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-13527 Crash when EXPLAIN SELECT .. INTO row_sp_variable.field
+--echo #
+
+DELIMITER $$;
+DECLARE
+ a ROW(a INT);
+BEGIN
+ EXPLAIN SELECT 1 INTO a.a;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # MDEV-14139 Anchored data types for variables
+--echo #
+
+DELIMITER $$;
+DECLARE
+ row1 ROW(int11 INT,text1 TEXT);
+ a_row1 row1%TYPE;
+ aa_row1 a_row1%TYPE;
+BEGIN
+ CREATE TABLE t1 AS SELECT a_row1.int11 AS int11, a_row1.text1 AS text1;
+ SHOW CREATE TABLE t1;
+ DROP TABLE t1;
+ CREATE TABLE t1 AS SELECT aa_row1.int11 AS int11, aa_row1.text1 AS text1;
+ SHOW CREATE TABLE t1;
+ DROP TABLE t1;
+END;
+$$
+DELIMITER ;$$
diff --git a/mysql-test/suite/compat/oracle/t/sp-security.test b/mysql-test/suite/compat/oracle/t/sp-security.test
new file mode 100644
index 00000000..1732c0b8
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-security.test
@@ -0,0 +1,345 @@
+--source include/not_embedded.inc
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
+
+
+--echo #
+--echo # Initiation:
+--echo # - creating database db1
+--echo # - creating user user1 with access rights to db1
+--echo #
+
+CREATE DATABASE db1;
+CREATE TABLE db1.t1 (a INT, b VARCHAR(10));
+
+CREATE USER user1;
+
+GRANT ALL PRIVILEGES ON test.* TO user1;
+
+connect (conn1,localhost,user1,,test);
+SET sql_mode=ORACLE;
+
+SELECT database();
+SELECT user();
+
+--echo #
+--echo # Making sure that user1 does not have privileges to db1.t1
+--echo #
+
+--error ER_TABLEACCESS_DENIED_ERROR
+SHOW CREATE TABLE db1.t1;
+--error ER_TABLEACCESS_DENIED_ERROR
+SHOW FIELDS IN db1.t1;
+
+
+--echo #
+--echo # Trigger: using %TYPE with a table we don't have access to
+--echo #
+CREATE TABLE test.t1 (a INT, b INT);
+INSERT INTO test.t1 (a,b) VALUES (10,20);
+SELECT * FROM t1;
+DELIMITER $$;
+CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
+BEGIN
+ DECLARE b db1.t1.b%TYPE := 20;
+ BEGIN
+ :NEW.b := 10;
+ END;
+END
+$$
+DELIMITER ;$$
+--error ER_TABLEACCESS_DENIED_ERROR
+INSERT INTO t1 (a) VALUES (10);
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER user1, SQL SECURITY DEFAULT
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a db1.t1.a%TYPE := 10;
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+--error ER_TABLEACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a db1.t1%ROWTYPE;
+BEGIN
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+--error ER_TABLEACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER root, SQL SECURITY INVOKER
+--echo #
+
+connection default;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+SQL SECURITY INVOKER
+AS
+ a db1.t1.a%TYPE := 10;
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+--error ER_TABLEACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+
+connection default;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+SQL SECURITY INVOKER
+AS
+ a db1.t1%ROWTYPE;
+BEGIN
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+--error ER_TABLEACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER root, SQL SECURITY DEFINER
+--echo #
+
+connection default;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+SQL SECURITY DEFINER
+AS
+ a db1.t1.a%TYPE := 10;
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+CALL p1;
+DROP PROCEDURE p1;
+
+connection default;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+SQL SECURITY DEFINER
+AS
+ a db1.t1%ROWTYPE;
+BEGIN
+ a.a:= 10;
+ SELECT a.a;
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Stored function: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER user1, SQL SECURITY DEFAULT
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT
+AS
+ a db1.t1.a%TYPE:=0;
+BEGIN
+ RETURN OCTET_LENGTH(a);
+END;
+$$
+DELIMITER ;$$
+--error ER_TABLEACCESS_DENIED_ERROR
+SELECT f1();
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Stored function: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER root, SQL SECURITY INVOKER
+--echo #
+
+connection default;
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT
+SQL SECURITY INVOKER
+AS
+ a db1.t1.a%TYPE:=0;
+BEGIN
+ RETURN OCTET_LENGTH(a);
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+--error ER_TABLEACCESS_DENIED_ERROR
+SELECT f1();
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Stored function: Using %TYPE for with a table that we don't have access to
+--echo # DEFINER root, SQL SECURITY DEFINER
+--echo #
+
+connection default;
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN INT
+SQL SECURITY DEFINER
+AS
+ a db1.t1.a%TYPE:=0;
+BEGIN
+ RETURN OCTET_LENGTH(a);
+END;
+$$
+DELIMITER ;$$
+connection conn1;
+SELECT f1();
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+connection default;
+GRANT SELECT (a) ON db1.t1 TO user1;
+connection conn1;
+
+--echo #
+--echo # Making sure that user1 has access to db1.t1.a, but not to db1.t1.b
+--echo #
+
+--error ER_TABLEACCESS_DENIED_ERROR
+SHOW CREATE TABLE db1.t1;
+SHOW FIELDS IN db1.t1;
+
+--echo #
+--echo # Trigger: Per-column privileges
+--echo #
+CREATE TABLE test.t1 (a INT, b INT);
+INSERT INTO test.t1 (a,b) VALUES (10,20);
+SELECT * FROM t1;
+# %TYPE reference using a column we have access to
+DELIMITER $$;
+CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
+BEGIN
+ DECLARE a db1.t1.a%TYPE := 20;
+ BEGIN
+ :NEW.b := 10;
+ END;
+END
+$$
+DELIMITER ;$$
+INSERT INTO t1 (a) VALUES (10);
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+# %TYPE reference using a column that we don't have access to
+DELIMITER $$;
+CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
+BEGIN
+ DECLARE b db1.t1.b%TYPE := 20;
+ BEGIN
+ :NEW.b := 10;
+ END;
+END
+$$
+DELIMITER ;$$
+--error ER_COLUMNACCESS_DENIED_ERROR
+INSERT INTO t1 (a) VALUES (10);
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+
+
+--echo #
+--echo # Stored procedure: Per-column privileges
+--echo # DEFINER user1, SQL SECURITY DEFAULT
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ a db1.t1.a%TYPE := 10;
+BEGIN
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ b db1.t1.b%TYPE := 10;
+BEGIN
+ SELECT b;
+END;
+$$
+DELIMITER ;$$
+--error ER_COLUMNACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ b db1.t1%ROWTYPE;
+BEGIN
+ b.b:=10;
+ SELECT b.b;
+END;
+$$
+DELIMITER ;$$
+--error ER_COLUMNACCESS_DENIED_ERROR
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Clean up
+--echo #
+disconnect conn1;
+connection default;
+
+DROP USER user1;
+DROP DATABASE db1;
+
+--echo #
+--echo # End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test
new file mode 100644
index 00000000..69b8608b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp.test
@@ -0,0 +1,2430 @@
+--source include/default_charset.inc
+
+SET sql_mode=ORACLE;
+
+--echo # Testing routines with no parameters
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+AS
+BEGIN
+ RETURN 10;
+END;
+/
+DELIMITER ;/
+--vertical_results
+SHOW CREATE FUNCTION f1;
+--horizontal_results
+SELECT f1();
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ SET @a=10;
+END;
+/
+DELIMITER ;/
+--vertical_results
+SHOW CREATE PROCEDURE p1;
+--horizontal_results
+SET @a=0;
+CALL p1();
+SELECT @a;
+DROP PROCEDURE p1;
+
+--echo # Testing ":=" to set the default value of a variable
+DELIMITER /;
+CREATE FUNCTION f1 () RETURN NUMBER(10) AS
+ a NUMBER(10) := 10;
+BEGIN
+ DECLARE
+ b NUMBER(10) DEFAULT 3;
+ BEGIN
+ RETURN a+b;
+ END;
+END;
+/
+DELIMITER ;/
+SELECT f1();
+DROP FUNCTION f1;
+
+--echo # Testing labels
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT) RETURN CLOB AS
+BEGIN
+ <<label1>>
+ BEGIN
+ IF a = 1 THEN
+ LEAVE label1;
+ END IF;
+ RETURN 'IS NOT 1';
+ END label1;
+ RETURN 'IS 1';
+END;
+/
+DELIMITER ;/
+SELECT f1(1);
+SELECT f1(2);
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT) RETURN INT IS
+BEGIN
+ <<label1>>
+ LOOP
+ IF a = 2 THEN
+ LEAVE label1;
+ END IF;
+ SET a= a-1;
+ END LOOP;
+ RETURN a;
+END;
+/
+DELIMITER ;/
+SELECT f1(4);
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT) RETURN INT AS
+BEGIN
+ <<label1>>
+ WHILE a>0 LOOP
+ IF a = 2 THEN
+ LEAVE label1;
+ END IF;
+ SET a= a-1;
+ END LOOP label1;
+ RETURN a;
+END;
+/
+DELIMITER ;/
+SELECT f1(4);
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT) RETURN INT AS
+BEGIN
+ <<label1>>
+ REPEAT
+ IF a = 2 THEN
+ LEAVE label1;
+ END IF;
+ SET a= a-1;
+ UNTIL a=0 END REPEAT;
+ RETURN a;
+END;
+/
+DELIMITER ;/
+SELECT f1(4);
+DROP FUNCTION f1;
+
+--echo # Testing IN/OUT/INOUT
+
+DELIMITER /;
+CREATE PROCEDURE p1 (p1 IN VARCHAR2(10), p2 OUT VARCHAR2(10)) AS
+BEGIN
+ SET p1='p1new';
+ SET p2='p2new';
+END;
+/
+DELIMITER ;/
+SET @p1='p1', @p2='p2';
+CALL p1(@p1, @p2);
+SELECT @p1, @p2;
+DROP PROCEDURE p1;
+
+--echo # Testing Oracle-style assigment
+DELIMITER /;
+CREATE PROCEDURE p1 (p1 OUT VARCHAR2(10)) AS
+BEGIN
+ p1:= 'p1new';
+END;
+/
+DELIMITER ;/
+SET @p1='p1';
+CALL p1(@p1);
+SELECT @p1;
+DROP PROCEDURE p1;
+
+--echo # Testing that NULL is a valid statement
+DELIMITER /;
+CREATE PROCEDURE p1(a INT) AS
+BEGIN
+ NULL;
+END;
+/
+DELIMITER ;/
+DROP PROCEDURE p1;
+
+DELIMITER /;
+CREATE PROCEDURE p1(a INT) AS
+ a INT:=10;
+BEGIN
+ IF a=10 THEN NULL; ELSE NULL; END IF;
+END;
+/
+DELIMITER ;/
+DROP PROCEDURE p1;
+
+--echo # Keywords that are OK for table names, but not for SP variables
+CREATE TABLE function (function int);
+INSERT INTO function SET function=10;
+SELECT function.function FROM function;
+DROP TABLE function;
+
+--echo # Testing that (some) keyword_sp are allowed in Oracle-style assignments
+DELIMITER /;
+CREATE PROCEDURE p1 (action OUT INT) AS BEGIN action:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (clob OUT INT) AS BEGIN clob:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (enum OUT INT) AS BEGIN enum:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (via OUT INT) AS BEGIN via:=10; END;/
+DROP PROCEDURE p1/
+DELIMITER ;/
+
+--echo # Testing keyword_directly_assignable
+DELIMITER /;
+CREATE PROCEDURE p1 (ascii OUT INT) AS BEGIN ascii:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (backup OUT INT) AS BEGIN backup:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (binlog OUT INT) AS BEGIN binlog:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (byte OUT INT) AS BEGIN byte:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (cache OUT INT) AS BEGIN cache:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (checksum OUT INT) AS BEGIN checksum:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (checkpoint OUT INT) AS BEGIN checkpoint:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (column_add OUT INT) AS BEGIN column_add:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (column_check OUT INT) AS BEGIN column_check:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (column_create OUT INT) AS BEGIN column_create:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (column_delete OUT INT) AS BEGIN column_delete:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (column_get OUT INT) AS BEGIN column_get:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (deallocate OUT INT) AS BEGIN deallocate:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (examined OUT INT) AS BEGIN examined:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (execute OUT INT) AS BEGIN execute:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (flush OUT INT) AS BEGIN flush:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (format OUT INT) AS BEGIN format:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (get OUT INT) AS BEGIN get:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (help OUT INT) AS BEGIN help:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (host OUT INT) AS BEGIN host:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (install OUT INT) AS BEGIN install:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (option OUT INT) AS BEGIN option:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (options OUT INT) AS BEGIN options:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (owner OUT INT) AS BEGIN owner:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (parser OUT INT) AS BEGIN parser:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (port OUT INT) AS BEGIN port:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (prepare OUT INT) AS BEGIN prepare:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (remove OUT INT) AS BEGIN remove:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (reset OUT INT) AS BEGIN reset:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (restore OUT INT) AS BEGIN restore:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (security OUT INT) AS BEGIN security:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (server OUT INT) AS BEGIN server:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (signed OUT INT) AS BEGIN signed:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (socket OUT INT) AS BEGIN socket:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (slave OUT INT) AS BEGIN slave:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (slaves OUT INT) AS BEGIN slaves:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (soname OUT INT) AS BEGIN soname:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (start OUT INT) AS BEGIN start:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (stop OUT INT) AS BEGIN stop:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (stored OUT INT) AS BEGIN stored:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (unicode OUT INT) AS BEGIN unicode:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (uninstall OUT INT) AS BEGIN uninstall:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (upgrade OUT INT) AS BEGIN upgrade:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (wrapper OUT INT) AS BEGIN wrapper:=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (xa OUT INT) AS BEGIN xa:=10; END;/
+DROP PROCEDURE p1/
+DELIMITER ;/
+
+
+--echo # Testing that keyword_directly_not_assignable does not work in :=
+DELIMITER /;
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 (commit OUT INT) AS BEGIN commit:=10; END;/
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 (rollback OUT INT) AS BEGIN rollback:=10; END;/
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 (shutdown OUT INT) AS BEGIN shutdown:=10; END;/
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1 (exception OUT INT) AS BEGIN exception:=10; END;/
+DELIMITER ;/
+
+
+--echo # Testing that keyword_directly_not_assignable works in SET statements.
+DELIMITER /;
+CREATE PROCEDURE p1 (contains OUT INT) AS BEGIN SET contains=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (language OUT INT) AS BEGIN SET language=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (no OUT INT) AS BEGIN SET no=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (charset OUT INT) AS BEGIN SET charset=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (do OUT INT) AS BEGIN SET do=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (repair OUT INT) AS BEGIN SET repair=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (handler OUT INT) AS BEGIN SET handler=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (open OUT INT) AS BEGIN SET open=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (close OUT INT) AS BEGIN SET close=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (savepoint OUT INT) AS BEGIN SET savepoint=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (truncate OUT INT) AS BEGIN SET truncate=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (begin OUT INT) AS BEGIN SET begin=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (end OUT INT) AS BEGIN SET end=10; END;/
+DROP PROCEDURE p1/
+CREATE PROCEDURE p1 (exception OUT INT) AS BEGIN SET exception=10; END;/
+DROP PROCEDURE p1/
+DELIMITER ;/
+
+--echo # Testing that keyword_directly_not_assignable works in table/column names
+CREATE TABLE contains (contains INT);
+DROP TABLE contains;
+CREATE TABLE language (language INT);
+DROP TABLE language;
+CREATE TABLE no (no INT);
+DROP TABLE no;
+CREATE TABLE charset (charset INT);
+DROP TABLE charset;
+CREATE TABLE do (do INT);
+DROP TABLE do;
+CREATE TABLE repair (repair INT);
+DROP TABLE repair;
+CREATE TABLE handler (handler INT);
+DROP TABLE handler;
+CREATE TABLE open (open INT);
+DROP TABLE open;
+CREATE TABLE close (close INT);
+DROP TABLE close;
+CREATE TABLE savepoint (savepoint INT);
+DROP TABLE savepoint;
+CREATE TABLE truncate (truncate INT);
+DROP TABLE truncate;
+CREATE TABLE begin (begin INT);
+DROP TABLE begin;
+CREATE TABLE end (end INT);
+DROP TABLE end;
+CREATE TABLE exception (exception INT);
+DROP TABLE exception;
+
+--echo # Testing ELSIF
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN CLOB
+AS
+BEGIN
+ IF a=1 THEN RETURN 'a is 1';
+ ELSIF a=2 THEN RETURN 'a is 2';
+ ELSE RETURN 'a is unknown';
+ END IF;
+END;
+/
+DELIMITER ;/
+SELECT f1(2) FROM DUAL;
+DROP FUNCTION f1;
+
+
+
+--echo # Testing top-level declarations
+DELIMITER /;
+CREATE PROCEDURE p1 (p1 OUT VARCHAR2(10))
+AS
+ p2 VARCHAR(10);
+BEGIN
+ p2:='p1new';
+ p1:=p2;
+END;
+/
+DELIMITER ;/
+SET @p1='p1';
+CALL p1(@p1);
+SELECT @p1;
+DROP PROCEDURE p1;
+
+DELIMITER /;
+CREATE FUNCTION f1 (p1 VARCHAR2(10)) RETURN VARCHAR(20)
+AS
+ p2 VARCHAR(10);
+BEGIN
+ p2:='new';
+ RETURN CONCAT(p1, p2);
+END;
+/
+DELIMITER ;/
+SET @p1='p1';
+SELECT f1(@p1);
+DROP FUNCTION f1;
+
+--echo # Testing non-top declarations
+
+DELIMITER /;
+CREATE PROCEDURE p1 (p1 OUT VARCHAR2(10))
+AS
+BEGIN
+ DECLARE
+ p2 VARCHAR(10);
+ BEGIN
+ p2:='p1new';
+ p1:=p2;
+ END;
+ DECLARE
+ t1 VARCHAR(10);
+ t2 VARCHAR(10);
+ BEGIN
+ END;
+END;
+/
+DELIMITER ;/
+SET @p1='p1';
+CALL p1(@p1);
+SELECT @p1;
+DROP PROCEDURE p1;
+
+DELIMITER /;
+CREATE FUNCTION f1 (p1 VARCHAR2(10)) RETURN VARCHAR(20)
+AS
+BEGIN
+ DECLARE
+ p2 VARCHAR(10);
+ BEGIN
+ p2:='new';
+ RETURN CONCAT(p1, p2);
+ END;
+ DECLARE
+ t1 VARCHAR(10);
+ t2 VARCHAR(10);
+ BEGIN
+ END;
+END;
+/
+DELIMITER ;/
+SET @p1='p1';
+SELECT f1(@p1);
+DROP FUNCTION f1;
+
+
+--echo # Testing exceptions
+
+CREATE TABLE t1 (c1 INT);
+
+DELIMITER /;
+
+CREATE PROCEDURE sp1 (p1 IN VARCHAR2(20), p2 OUT VARCHAR2(30))
+IS
+ v1 INT;
+BEGIN
+ SELECT c1 INTO v1 FROM t1;
+ p2 := p1;
+EXCEPTION
+ WHEN NOT FOUND THEN
+ BEGIN
+ p2 := 'def';
+ END;
+END;
+/
+
+DELIMITER ;/
+
+CALL sp1('abc', @a);
+SELECT @a;
+
+DROP PROCEDURE sp1;
+DROP TABLE t1;
+
+
+DELIMITER /;
+CREATE PROCEDURE sp1 (v IN OUT INT, error IN INT)
+IS
+BEGIN
+ SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=error, MESSAGE_TEXT='User defined error!';
+ v:= 223;
+EXCEPTION
+ WHEN 30001 THEN
+ BEGIN
+ v:= 113;
+ END;
+END;
+/
+DELIMITER ;/
+SET @v=10;
+CALL sp1(@v, 30001);
+--error 30002
+CALL sp1(@v, 30002);
+SELECT @v;
+DROP PROCEDURE sp1;
+
+
+DELIMITER /;
+CREATE PROCEDURE sp1 (v IN OUT INT, error IN INT)
+IS
+BEGIN
+ BEGIN
+ BEGIN
+ SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=error, MESSAGE_TEXT='User defined error!';
+ v:= 223;
+ EXCEPTION
+ WHEN 30001 THEN
+ BEGIN
+ v:= 113;
+ END;
+ END;
+ END;
+END;
+/
+DELIMITER ;/
+SET @v=10;
+CALL sp1(@v, 30001);
+SELECT @v;
+SET @v=10;
+--error 30002
+CALL sp1(@v, 30002);
+SELECT @v;
+DROP PROCEDURE sp1;
+
+
+--echo #
+--echo # Testing EXIT statement
+--echo #
+
+DELIMITER /;
+--error ER_SP_LILABEL_MISMATCH
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ EXIT;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+--error ER_SP_LILABEL_MISMATCH
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ <<lable1>>
+ BEGIN
+ <<label2>>
+ LOOP
+ EXIT label1;
+ END LOOP;
+ END;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ LOOP
+ i:= i + 1;
+ IF i >= 5 THEN
+ EXIT;
+ END IF;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ <<label1>>
+ LOOP
+ <<label2>>
+ LOOP
+ i:= i + 1;
+ IF i >= 5 THEN
+ EXIT label2;
+ END IF;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ <<label1>>
+ LOOP
+ <<label2>>
+ LOOP
+ i:= i + 1;
+ IF i >= 5 THEN
+ EXIT label1;
+ END IF;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ LOOP
+ i:= i + 1;
+ EXIT WHEN i >=5;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ <<label1>>
+ LOOP
+ <<label2>>
+ LOOP
+ i:= i + 1;
+ EXIT label2 WHEN i >= 5;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+IS
+ i INT := 0;
+BEGIN
+ <<label1>>
+ LOOP
+ <<label2>>
+ LOOP
+ i:= i + 1;
+ EXIT label1 WHEN i >= 5;
+ END LOOP;
+ i:= i + 100;
+ EXIT;
+ END LOOP;
+ RETURN i;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing CURSOR declaration
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+AS
+ v_a INT:=10;
+ CURSOR c IS SELECT a FROM t1;
+BEGIN
+ OPEN c;
+ FETCH c INTO v_a;
+ CLOSE c;
+ RETURN v_a;
+EXCEPTION
+ WHEN OTHERS THEN RETURN -1;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo # Testing RETURN in procedures
+
+DELIMITER /;
+--error ER_SP_BADRETURN
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+BEGIN
+ RETURN 10;
+END;
+/
+DELIMITER ;/
+
+DELIMITER /;
+--error ER_PARSE_ERROR
+CREATE FUNCTION f1 (a INT) RETURN INT
+AS
+BEGIN
+ RETURN;
+END;
+/
+DELIMITER ;/
+
+DELIMITER /;
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+BEGIN
+ IF a < 10 THEN
+ BEGIN
+ a:= a - 1;
+ RETURN;
+ END;
+ END IF;
+ a:= a + 1;
+EXCEPTION
+ WHEN OTHERS THEN RETURN;
+END;
+/
+DELIMITER ;/
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+SET @v=9;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+DELIMITER /;
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+BEGIN
+ DROP TABLE t1_non_existent;
+EXCEPTION
+ WHEN OTHERS THEN
+ BEGIN
+ a:= 100;
+ RETURN;
+ END;
+END;
+/
+DELIMITER ;/
+SET @v=10;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Testing WHILE loop
+
+DELIMITER /;
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+ i INT:= 1;
+ j INT:= 3;
+BEGIN
+ WHILE i<=j
+ LOOP
+ a:= a + i;
+ i:= i + 1;
+ END LOOP;
+END;
+/
+DELIMITER ;/
+SET @v=0;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+DELIMITER /;
+CREATE PROCEDURE p1 (a IN OUT INT)
+AS
+ i INT:= 1;
+ j INT:= 3;
+BEGIN
+ <<label>>
+ WHILE i<=j
+ LOOP
+ a:= a + i;
+ i:= i + 1;
+ END LOOP label;
+END;
+/
+DELIMITER ;/
+SET @v=0;
+CALL p1(@v);
+SELECT @v;
+DROP PROCEDURE p1;
+
+
+--echo # Testing the FOR loop statement
+
+CREATE TABLE t1 (a INT);
+DELIMITER /;
+FOR i IN 1..3
+LOOP
+ INSERT INTO t1 VALUES (i);
+END LOOP;
+/
+DELIMITER ;/
+SELECT * FROM t1;
+DROP TABLE t1;
+
+
+DELIMITER /;
+--error ER_PARSE_ERROR
+CREATE FUNCTION f1 (lower_bound INT, upper_bound INT, lim INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN lower_bound . . upper_bound
+ LOOP
+ NULL
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+
+
+DELIMITER /;
+CREATE FUNCTION f1 (lower_bound INT, upper_bound INT, lim INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN lower_bound .. upper_bound
+ LOOP
+ total:= total + i;
+ IF i = lim THEN
+ EXIT;
+ END IF;
+ -- Bounds are calculated only once.
+ -- The below assignments have no effect on the loop condition
+ lower_bound:= 900;
+ upper_bound:= 1000;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(1, 3, 100) FROM DUAL;
+SELECT f1(1, 3, 2) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN 1 .. 5
+ LOOP
+ total:= total + 1000;
+ FOR j IN 1 .. 5
+ LOOP
+ total:= total + 1;
+ IF j = 3 THEN
+ EXIT; -- End the internal loop
+ END IF;
+ END LOOP;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1() FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, b INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ FOR i IN REVERSE 1..a
+ LOOP
+ total:= total + i;
+ IF i = b THEN
+ EXIT;
+ END IF;
+ END LOOP;
+ RETURN total;
+END
+/
+DELIMITER ;/
+SELECT f1(3, 100) FROM DUAL;
+SELECT f1(3, 2) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing labeled FOR LOOP statement
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, limita INT, b INT, limitb INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ <<la>>
+ FOR ia IN 1 .. a
+ LOOP
+ total:= total + 1000;
+ <<lb>>
+ FOR ib IN 1 .. b
+ LOOP
+ total:= total + 1;
+ EXIT lb WHEN ib = limitb;
+ EXIT la WHEN ia = limita;
+ END LOOP lb;
+ END LOOP la;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(1, 1, 1, 1) FROM DUAL;
+SELECT f1(1, 2, 1, 2) FROM DUAL;
+SELECT f1(2, 1, 2, 1) FROM DUAL;
+SELECT f1(2, 1, 2, 2) FROM DUAL;
+SELECT f1(2, 2, 2, 2) FROM DUAL;
+SELECT f1(2, 3, 2, 3) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing labeled ITERATE in a labeled FOR LOOP statement
+
+DELIMITER /;
+CREATE FUNCTION f1 (a INT, b INT, blim INT) RETURN INT
+AS
+ total INT := 0;
+BEGIN
+ <<la>>
+ FOR ia IN 1 .. a
+ LOOP
+ total:= total + 1000;
+ DECLARE
+ ib INT:= 1;
+ BEGIN
+ WHILE ib <= b
+ LOOP
+ IF ib > blim THEN
+ ITERATE la;
+ END IF;
+ ib:= ib + 1;
+ total:= total + 1;
+ END LOOP;
+ END;
+ END LOOP la;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(3,3,0), f1(3,3,1), f1(3,3,2), f1(3,3,3), f1(3,3,4) FROM DUAL;
+DROP FUNCTION f1;
+
+
+--echo # Testing CONTINUE statement
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ FOR i IN 1 .. a
+ LOOP
+ IF i=5 THEN
+ CONTINUE;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ <<lj>>
+ FOR j IN 1 .. 2
+ LOOP
+ FOR i IN 1 .. a
+ LOOP
+ IF i=5 THEN
+ CONTINUE lj;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(3), f1(4), f1(5) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+BEGIN
+ <<lj>>
+ FOR j IN 1 .. 2
+ LOOP
+ FOR i IN 1 .. a
+ LOOP
+ CONTINUE lj WHEN i=5;
+ total:= total + 1;
+ END LOOP;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(3), f1(4), f1(5) FROM DUAL;
+DROP FUNCTION f1;
+
+
+DELIMITER /;
+CREATE FUNCTION f1(a INT) RETURN INT
+AS
+ total INT:= 0;
+ i INT:= 1;
+BEGIN
+ WHILE i <= a
+ LOOP
+ i:= i + 1;
+ IF i=6 THEN
+ CONTINUE;
+ END IF;
+ total:= total + 1;
+ END LOOP;
+ RETURN total;
+END;
+/
+DELIMITER ;/
+SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
+DROP FUNCTION f1;
+
+--echo #
+--echo # Testing behaviour of unknown identifiers in EXIT and CONTINUE statements
+--echo #
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+CREATE PROCEDURE p1
+AS
+BEGIN
+ LOOP
+ EXIT WHEN unknown_ident IS NULL;
+ END LOOP;
+END$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+CREATE PROCEDURE p1
+AS
+BEGIN
+ <<label>>
+ LOOP
+ EXIT label WHEN unknown_ident IS NULL;
+ END LOOP;
+END$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+CREATE PROCEDURE p1
+AS
+BEGIN
+ LOOP
+ CONTINUE WHEN unknown_ident IS NULL;
+ END LOOP;
+END$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+CREATE PROCEDURE p1
+AS
+BEGIN
+ <<label>>
+ LOOP
+ CONTINUE label WHEN unknown_ident IS NULL;
+ END LOOP;
+END$$
+DELIMITER ;$$
+
+--echo #
+--echo # MDEV-10583 sql_mode=ORACLE: SQL%ROWCOUNT
+--echo #
+
+EXPLAIN EXTENDED SELECT sql%rowcount;
+CREATE TABLE t1 AS SELECT SQL%ROWCOUNT;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+--echo #
+--echo # UPDATE
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ UPDATE t1 SET a=30;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ UPDATE t1 SET a=30;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # DELETE
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ DELETE FROM t1;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ DELETE FROM t1;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # SELECT ... INTO var FROM ... - one row found
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ va INT;
+BEGIN
+ SELECT a INTO va FROM t1 LIMIT 1;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # SELECT ... INTO var FROM ... - no rows found
+--echo #
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ va INT;
+BEGIN
+ SELECT a INTO va FROM t1;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ va INT;
+BEGIN
+ SELECT a INTO va FROM t1;
+ SELECT SQL%ROWCOUNT;
+EXCEPTION
+ WHEN NO_DATA_FOUND THEN SELECT SQL%ROWCOUNT||' (EXCEPTION)';
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # SELECT ... INTO var FROM ... - multiple rows found
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ va INT:=1;
+BEGIN
+ SELECT a INTO va FROM t1;
+ SELECT SQL%ROWCOUNT;
+EXCEPTION
+ WHEN TOO_MANY_ROWS THEN SELECT SQL%ROWCOUNT||' (EXCEPTION) va='||va;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+
+--echo #
+--echo # INSERT INTO t2 SELECT ...
+--echo #
+
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (10);
+INSERT INTO t1 VALUES (20);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ INSERT INTO t2 SELECT * FROM t1;
+ SELECT SQL%ROWCOUNT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1, t2;
+
+--echo #
+--echo # End of MDEV-10583 sql_mode=ORACLE: SQL%ROWCOUNT
+--echo #
+
+--echo #
+--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
+
+--echo #
+--echo # Missing table
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE;
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Missing column
+--echo #
+
+CREATE TABLE t1 (b INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE;
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
+--error ER_BAD_FIELD_ERROR
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # One %TYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE;
+BEGIN
+ a:= 123;
+ SELECT a;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--echo #
+--echo # Two %TYPE variables, with a truncation warning on assignment
+--echo #
+
+CREATE TABLE t1 (a TINYINT, b INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE;
+ b t1.b%TYPE;
+BEGIN
+ a:= 200;
+ b:= 200;
+ SELECT a, b;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %TYPE variables for fields with various attributes
+--echo #
+
+CREATE TABLE t1 (
+ id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ a TINYINT NOT NULL,
+ b INT NOT NULL,
+ ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ UNIQUE(a)
+);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ id t1.id%TYPE;
+ a t1.a%TYPE;
+ b t1.b%TYPE;
+ ts t1.ts%TYPE;
+BEGIN
+ SELECT id, a, b, ts;
+ CREATE TABLE t2 AS SELECT id, a, b, ts;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %TYPE + virtual columns
+--echo #
+
+#
+# TODO: Changing 'a + 10' to 'a mod 10' make it fail, because
+# it's Item::print() returns 'a % 10' which makes grammar conflict
+# with cursor attributes
+
+CREATE TABLE t1 (
+ a INT NOT NULL,
+ b VARCHAR(32),
+ c INT AS (a + 10) VIRTUAL,
+ d VARCHAR(5) AS (left(b,5)) PERSISTENT
+);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ c t1.c%TYPE;
+ d t1.d%TYPE;
+BEGIN
+ SELECT c, d;
+ CREATE TABLE t2 AS SELECT c, d;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %TYPE + the ZEROFILL attribute
+--echo #
+
+CREATE TABLE t1 (
+ dz DECIMAL(10,3) ZEROFILL
+);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ dzr t1.dz%TYPE := 10;
+ dzt DECIMAL(10,3) ZEROFILL := 10;
+BEGIN
+ SELECT dzr, dzt;
+ CREATE TABLE t2 AS SELECT dzr,dzt;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Temporary tables shadow real tables for %TYPE purposes
+--echo #
+CREATE TABLE t1 (a VARCHAR(10));
+INSERT INTO t1 VALUES ('t1');
+CREATE TEMPORARY TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10);
+SELECT * FROM t1;
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE:=11;
+BEGIN
+ CREATE TABLE t2 AS SELECT a;
+END;
+$$
+DELIMITER ;$$
+--echo #
+--echo # Should use INT(11) as %TYPE, as in the temporary table
+--echo #
+CALL p1();
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t2;
+SELECT * FROM t1;
+DROP TEMPORARY TABLE t1;
+SELECT * FROM t1;
+--echo #
+--echo # Should use VARCHAR(10) as %TYPE, as in the real table
+--echo #
+CALL p1();
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # t1.a%TYPE searches for "t1" in the current database
+--echo #
+
+CREATE TABLE t1 (a VARCHAR(10));
+CREATE DATABASE test1;
+CREATE TABLE test1.t1 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE:=11;
+BEGIN
+ CREATE TABLE test.t2 AS SELECT a;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # This interprets t1.a%TYPE as VARCHAR(10), as in test.t1.a
+--echo #
+
+USE test;
+CALL test.p1();
+SHOW CREATE TABLE test.t2;
+DROP TABLE test.t2;
+
+--echo #
+--echo # This interprets t1.a%TYPE as INT, as in test1.t1.a
+--echo #
+
+USE test1;
+CALL test.p1();
+SHOW CREATE TABLE test.t2;
+DROP TABLE test.t2;
+
+--echo #
+--echo # Error if there is no an active database
+--echo #
+
+DROP DATABASE test1;
+--error ER_NO_DB_ERROR
+CALL test.p1();
+
+USE test;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # A reference to a table in a non-existing database
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a test1.t1.a%TYPE;
+BEGIN
+ CREATE TABLE t1 AS SELECT a;
+END;
+$$
+DELIMITER ;$$
+--error ER_NO_SUCH_TABLE
+CALL p1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A reference to a table in a different database
+--echo #
+CREATE TABLE t1(a INT);
+CREATE DATABASE test1;
+CREATE TABLE test1.t1 (a VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a t1.a%TYPE;
+ b test1.t1.a%TYPE;
+BEGIN
+ CREATE TABLE t2 AS SELECT a,b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+SHOW CREATE TABLE t2;
+DROP PROCEDURE p1;
+DROP TABLE t2;
+DROP DATABASE test1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Using a table before it appears in a %TYPE declaration + multiple %TYPE declarations
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+INSERT INTO t1 (a,b) VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ INSERT INTO t1 (a,b) VALUES (11, 'b11');
+ SELECT * FROM t1;
+ DECLARE
+ va t1.a%TYPE:= 30;
+ vb t1.b%TYPE:= 'b30';
+ BEGIN
+ INSERT INTO t1 (a,b) VALUES (12,'b12');
+ SELECT * FROM t1;
+ INSERT INTO t1 (a,b) VALUES (va, vb);
+ SELECT * FROM t1;
+ END;
+ DECLARE
+ va t1.a%TYPE:= 40;
+ vb t1.b%TYPE:= 'b40';
+ BEGIN
+ INSERT INTO t1 (a,b) VALUES (va,vb);
+ SELECT * FROM t1;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # %TYPE variables + TABLE vs VIEW
+--echo #
+
+CREATE TABLE t1 (
+ bit6 BIT(6),
+ bit7 BIT(7),
+ bit8 BIT(8),
+ i1 TINYINT,
+ i2 SMALLINT,
+ i3 MEDIUMINT,
+ i4 INT,
+ i8 BIGINT,
+ ff FLOAT,
+ fd DOUBLE,
+ cc CHAR(10),
+ cv VARCHAR(10),
+ cvu VARCHAR(10) CHARACTER SET utf8,
+ t1 TINYTEXT,
+ t2 TEXT,
+ t3 MEDIUMTEXT,
+ t4 LONGTEXT,
+ enum1 ENUM('a','b','c'),
+ set1 SET('a','b','c'),
+ blob1 TINYBLOB,
+ blob2 BLOB,
+ blob3 MEDIUMBLOB,
+ blob4 LONGBLOB,
+ yy YEAR,
+ dd DATE,
+ tm0 TIME,
+ tm3 TIME(3),
+ tm6 TIME(6),
+ dt0 DATETIME,
+ dt3 DATETIME(3),
+ dt6 DATETIME(6),
+ ts0 TIMESTAMP,
+ ts3 TIMESTAMP(3),
+ ts6 TIMESTAMP(6),
+ dc100 DECIMAL(10,0),
+ dc103 DECIMAL(10,3),
+ dc209 DECIMAL(20,9)
+);
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1(command enum('create','select'))
+AS
+ bit6 t1.bit6%TYPE := 0x30;
+ bit7 t1.bit7%TYPE := 0x41;
+ bit8 t1.bit8%TYPE := 0x7E;
+ i1 t1.i1%TYPE := 11;
+ i2 t1.i2%TYPE := 12;
+ i3 t1.i3%TYPE := 13;
+ i4 t1.i4%TYPE := 14;
+ i8 t1.i8%TYPE := 18;
+ ff t1.ff%TYPE := 21;
+ fd t1.fd%TYPE := 22;
+ cc t1.cc%TYPE := 'char';
+ cv t1.cv%TYPE := 'varchar';
+ cvu t1.cvu%TYPE := 'varcharu8';
+ t1 t1.t1%TYPE := 'text1';
+ t2 t1.t2%TYPE := 'text2';
+ t3 t1.t3%TYPE := 'text3';
+ t4 t1.t4%TYPE := 'text4';
+ enum1 t1.enum1%TYPE := 'b';
+ set1 t1.set1%TYPE := 'a,c';
+ blob1 t1.blob1%TYPE := 'blob1';
+ blob2 t1.blob2%TYPE := 'blob2';
+ blob3 t1.blob3%TYPE := 'blob3';
+ blob4 t1.blob4%TYPE := 'blob4';
+ yy t1.yy%TYPE := 2001;
+ dd t1.dd%TYPE := '2001-01-01';
+ tm0 t1.tm0%TYPE := '00:00:01';
+ tm3 t1.tm3%TYPE := '00:00:03.333';
+ tm6 t1.tm6%TYPE := '00:00:06.666666';
+ dt0 t1.dt0%TYPE := '2001-01-01 00:00:01';
+ dt3 t1.dt3%TYPE := '2001-01-03 00:00:01.333';
+ dt6 t1.dt6%TYPE := '2001-01-06 00:00:01.666666';
+ ts0 t1.ts0%TYPE := '2002-01-01 00:00:01';
+ ts3 t1.ts3%TYPE := '2002-01-03 00:00:01.333';
+ ts6 t1.ts6%TYPE := '2002-01-06 00:00:01.666666';
+ dc100 t1.dc100%TYPE := 10;
+ dc103 t1.dc103%TYPE := 10.123;
+ dc209 t1.dc209%TYPE := 10.123456789;
+BEGIN
+ CASE
+ WHEN command='create' THEN
+ CREATE TABLE t2 AS SELECT
+ bit6, bit7, bit8,
+ i1,i2,i3,i4,i8,
+ ff,fd, dc100, dc103, dc209,
+ cc,cv,cvu,
+ t1,t2,t3,t4,
+ enum1, set1,
+ blob1, blob2, blob3, blob4,
+ dd, yy,
+ tm0, tm3, tm6,
+ dt0, dt3, dt6,
+ ts0, ts3, ts6;
+ WHEN command='select' THEN
+ SELECT
+ bit6, bit7, bit8,
+ i1,i2,i3,i4,i8,
+ ff,fd, dc100, dc103, dc209,
+ cc,cv,cvu,
+ t1,t2,t3,t4,
+ enum1, set1,
+ blob1, blob2, blob3, blob4,
+ dd, yy,
+ tm0, tm3, tm6,
+ dt0, dt3, dt6,
+ ts0, ts3, ts6;
+ END CASE;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # TABLE
+--echo #
+CALL p1('create');
+SHOW CREATE TABLE t2;
+--vertical_results
+SELECT * FROM t2;
+--horizontal_results
+DROP TABLE t2;
+
+--disable_ps_protocol
+--enable_metadata
+--vertical_results
+CALL p1('select');
+--horizontal_results
+--disable_metadata
+--enable_ps_protocol
+
+--echo #
+--echo # VIEW
+--echo #
+ALTER TABLE t1 RENAME t0;
+CREATE VIEW t1 AS SELECT * FROM t0;
+
+CALL p1('create');
+SHOW CREATE TABLE t2;
+--vertical_results
+SELECT * FROM t2;
+--horizontal_results
+DROP TABLE t2;
+
+--disable_ps_protocol
+--enable_metadata
+--vertical_results
+CALL p1('select');
+--horizontal_results
+--disable_metadata
+--enable_ps_protocol
+
+DROP VIEW t1;
+DROP TABLE t0;
+
+DROP PROCEDURE p1;
+
+--echo #
+--echo # VIEW with subqueries
+--echo #
+CREATE TABLE t1 (a INT,b INT);
+INSERT INTO t1 VALUES (10,1),(20,2),(30,3),(40,4);
+SELECT AVG(a) FROM t1;
+CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1);
+SELECT * FROM v1;
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a v1.a%TYPE := 10;
+ b v1.b%TYPE := 1;
+BEGIN
+ SELECT a,b;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN INT
+AS
+ a v1.a%TYPE := 10;
+ b v1.b%TYPE := 1;
+BEGIN
+ RETURN a+b;
+END;
+$$
+DELIMITER ;$$
+SELECT f1();
+DROP FUNCTION f1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %TYPE variables + INFORMATION_SCHEMA
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ tables_table_name INFORMATION_SCHEMA.TABLES.TABLE_NAME%TYPE;
+ tables_table_rows INFORMATION_SCHEMA.TABLES.TABLE_ROWS%TYPE;
+ processlist_info INFORMATION_SCHEMA.PROCESSLIST.INFO%TYPE;
+ processlist_info_binary INFORMATION_SCHEMA.PROCESSLIST.INFO_BINARY%TYPE;
+BEGIN
+ CREATE TABLE t1 AS SELECT
+ tables_table_name,
+ tables_table_rows,
+ processlist_info,
+ processlist_info_binary;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # %TYPE + Table structure change
+--echo # Data type for both a0 and a1 is chosen in the very beginning
+--echo #
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a0 t1.a%TYPE;
+BEGIN
+ ALTER TABLE t1 MODIFY a VARCHAR(10); -- This does not affect a1
+ DECLARE
+ a1 t1.a%TYPE;
+ BEGIN
+ CREATE TABLE t2 AS SELECT a0, a1;
+ SHOW CREATE TABLE t2;
+ DROP TABLE t2;
+ END;
+END
+$$
+DELIMITER ;$$
+CREATE TABLE t1 (a INT);
+CALL p1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # %TYPE in parameters
+--echo #
+CREATE TABLE t1 (a VARCHAR(10));
+CREATE DATABASE test1;
+CREATE TABLE test1.t1 (b SMALLINT);
+DELIMITER $$;
+CREATE PROCEDURE p1(a t1.a%TYPE, b test1.t1.b%TYPE)
+AS
+BEGIN
+ CREATE TABLE t2 AS SELECT a, b;
+END;
+$$
+DELIMITER ;$$
+CALL p1('test', 123);
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t2;
+DROP PROCEDURE p1;
+DROP TABLE test1.t1;
+DROP DATABASE test1;
+DROP TABLE t1;
+
+--echo #
+--echo # %TYPE in a stored function variables and arguments
+--echo #
+
+CREATE TABLE t1 (a INT);
+SET sql_mode=ORACLE;
+DELIMITER $$;
+CREATE FUNCTION f1 (prm t1.a%TYPE) RETURN INT
+AS
+ a t1.a%TYPE:= prm;
+BEGIN
+ RETURN a;
+END;
+$$
+DELIMITER ;$$
+SELECT f1(20);
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # %TYPE in function RETURN clause is not supported yet
+--echo #
+DELIMITER $$;
+--error ER_UNKNOWN_DATA_TYPE
+CREATE FUNCTION f1 RETURN t1.a%TYPE
+AS
+BEGIN
+ RETURN 0;
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
+
+
+--echo #
+--echo # MDEV-12089 sql_mode=ORACLE: Understand optional routine name after the END keyword
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END f1;
+$$
+DELIMITER ;$$
+DROP FUNCTION f1;
+
+DELIMITER $$;
+CREATE FUNCTION test.f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END test.f1;
+$$
+DELIMITER ;$$
+DROP FUNCTION f1;
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE FUNCTION test.f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END test2.f1;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE FUNCTION test.f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END test.f2;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE FUNCTION f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END test.f2;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE FUNCTION f1 RETURN INT AS
+BEGIN
+ RETURN 10;
+END test2.f1;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+BEGIN
+ NULL;
+END p1;
+$$
+DELIMITER ;$$
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+CREATE PROCEDURE test.p1 AS
+BEGIN
+ NULL;
+END test.p1;
+$$
+DELIMITER ;$$
+DROP PROCEDURE p1;
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PROCEDURE test.p1 AS
+BEGIN
+ NULL;
+END test2.p1;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PROCEDURE test.p1 AS
+BEGIN
+ NULL;
+END test.p2;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PROCEDURE p1 AS
+BEGIN
+ NULL;
+END test.p2;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_END_IDENTIFIER_DOES_NOT_MATCH
+CREATE PROCEDURE p1 AS
+BEGIN
+ NULL;
+END test2.p1;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # MDEV-12107 sql_mode=ORACLE: Inside routines the CALL keywoard is optional
+--echo #
+DELIMITER /;
+CREATE OR REPLACE PROCEDURE p1(a INT) AS
+BEGIN
+ SELECT 'This is p1' AS "comment";
+END;
+/
+CREATE OR REPLACE PROCEDURE p2 AS
+BEGIN
+ SELECT 'This is p2' AS "comment";
+END;
+/
+BEGIN
+ p1(10);
+ p2;
+ test.p1(10);
+ test.p2;
+END;
+/
+CREATE PROCEDURE p3 AS
+BEGIN
+ p1(10);
+ p2;
+ test.p1(10);
+ test.p2;
+END
+/
+DELIMITER ;/
+CALL p3;
+DROP PROCEDURE p3;
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-12854 Synchronize CREATE..SELECT data type and result set metadata data type for INT functions
+--echo #
+
+--enable_metadata
+--disable_ps_protocol
+SELECT SQL%ROWCOUNT;
+--enable_ps_protocol
+--disable_metadata
+
+--echo #
+--echo # MDEV-13686 EXCEPTION reserved keyword in SQL_MODE=oracle but not in Oracle itself
+--echo #
+CREATE TABLE t1 (c1 int);
+CREATE VIEW v1 AS SELECT c1 exception FROM t1;
+SELECT exception FROM v1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+
+
+--echo #
+--echo # MDEV-14139 Anchored data types for variables
+--echo #
+
+DELIMITER $$;
+--error ER_SP_UNDECLARED_VAR
+BEGIN NOT ATOMIC
+ DECLARE a a%TYPE;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+DECLARE
+ int11 INT;
+ dec103 DECIMAL(10,3);
+ flt0 FLOAT;
+ dbl0 DOUBLE;
+ enum0 ENUM('a','b');
+ bit3 BIT(3);
+
+ varchar10 VARCHAR(10);
+ text1 TEXT;
+ tinytext1 TINYTEXT;
+ mediumtext1 MEDIUMTEXT;
+ longtext1 LONGTEXT;
+
+ time3 TIME(3);
+ datetime4 DATETIME(4);
+ timestamp5 TIMESTAMP(5);
+ date0 DATE;
+
+ a_int11 int11%TYPE;
+ a_dec103 dec103%TYPE;
+ a_flt0 flt0%TYPE;
+ a_dbl0 dbl0%TYPE;
+ a_bit3 bit3%TYPE;
+ a_enum0 enum0%TYPE;
+ a_varchar10 varchar10%TYPE;
+ a_text1 text1%TYPE;
+ a_tinytext1 tinytext1%TYPE;
+ a_mediumtext1 mediumtext1%TYPE;
+ a_longtext1 longtext1%TYPE;
+ a_time3 time3%TYPE;
+ a_datetime4 datetime4%TYPE;
+ a_timestamp5 timestamp5%TYPE;
+ a_date0 date0%TYPE;
+
+ aa_int11 a_int11%TYPE;
+ aa_dec103 a_dec103%TYPE;
+ aa_flt0 a_flt0%TYPE;
+ aa_dbl0 a_dbl0%TYPE;
+ aa_bit3 a_bit3%TYPE;
+ aa_enum0 a_enum0%TYPE;
+ aa_varchar10 a_varchar10%TYPE;
+ aa_text1 a_text1%TYPE;
+ aa_tinytext1 a_tinytext1%TYPE;
+ aa_mediumtext1 a_mediumtext1%TYPE;
+ aa_longtext1 a_longtext1%TYPE;
+ aa_time3 a_time3%TYPE;
+ aa_datetime4 a_datetime4%TYPE;
+ aa_timestamp5 a_timestamp5%TYPE;
+ aa_date0 a_date0%TYPE;
+BEGIN
+ CREATE TABLE t1 AS
+ SELECT a_int11,a_dec103,a_flt0,a_dbl0,a_bit3,
+ a_enum0,a_varchar10,
+ a_text1,a_tinytext1,a_mediumtext1,a_longtext1,
+ a_time3,a_datetime4,a_timestamp5,a_date0;
+ SHOW CREATE TABLE t1;
+ DROP TABLE t1;
+
+ CREATE TABLE t1 AS
+ SELECT aa_int11,aa_dec103,aa_flt0,aa_dbl0,aa_bit3,
+ aa_enum0,aa_varchar10,
+ aa_text1,aa_tinytext1,aa_mediumtext1,aa_longtext1,
+ aa_time3,aa_datetime4,aa_timestamp5,aa_date0;
+ SHOW CREATE TABLE t1;
+ DROP TABLE t1;
+
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # MDEV-11160 "Incorrect column name" when "CREATE TABLE t1 AS SELECT spvar"
+--echo #
+
+
+CREATE TABLE t1 (x INT);
+INSERT INTO t1 VALUES (10);
+CREATE VIEW v1 AS SELECT x+1 AS a,x+1 AS b FROM t1;
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+ a INT := 1;
+ b INT := 2;
+BEGIN
+ CREATE TABLE t2 AS SELECT a,b FROM v1;
+ SHOW CREATE TABLE t2;
+ SELECT * FROM t2;
+ DROP TABLE t2;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP VIEW v1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-14228 MariaDB crashes with function
+--echo #
+
+CREATE TABLE t1 (c VARCHAR(16), KEY(c));
+INSERT INTO t1 VALUES ('foo');
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR(16)
+IS
+ v VARCHAR2(16);
+BEGIN
+ FOR v IN (SELECT DISTINCT c FROM t1)
+ LOOP
+ IF (v = 'bar') THEN
+ SELECT 1 INTO @a;
+ END IF;
+ END LOOP;
+ RETURN 'qux';
+END $$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT f1();
+DROP FUNCTION f1;
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR(16)
+IS
+ v t1%ROWTYPE;
+BEGIN
+ IF v = 'bar' THEN
+ NULL;
+ END IF;
+ RETURN 'qux';
+END $$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT f1();
+DROP FUNCTION f1;
+
+DELIMITER $$;
+CREATE FUNCTION f1() RETURN VARCHAR(16)
+IS
+ v ROW(a INT);
+BEGIN
+ IF v = 'bar' THEN
+ NULL;
+ END IF;
+ RETURN 'qux';
+END $$
+DELIMITER ;$$
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+SELECT f1();
+DROP FUNCTION f1;
+
+DROP TABLE t1;
+
+
+DELIMITER $$;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+DECLARE
+ v ROW(a INT);
+BEGIN
+ SELECT v IN ('a','b');
+END $$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+DECLARE
+ v ROW(a INT);
+BEGIN
+ SELECT 'a' IN (v,'b');
+END $$
+DELIMITER ;$$
+
+DELIMITER $$;
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+DECLARE
+ v ROW(a INT);
+BEGIN
+ SELECT 'a' IN ('b',v);
+END $$
+DELIMITER ;$$
+
+--echo #
+--echo # MDEV-17253 Oracle compatibility: The REVERSE key word for FOR loop behaves incorrectly
+--echo #
+
+DELIMITER $$;
+DECLARE
+ totalprice DECIMAL(12,2):=NULL;
+ loop_start INTEGER := 1;
+BEGIN
+ FOR idx IN REVERSE loop_start..10 LOOP
+ SELECT idx;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ loop_start INTEGER := 1;
+BEGIN
+ FOR idx IN REVERSE 3..loop_start LOOP
+ SELECT idx;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ loop_start INTEGER := 1;
+BEGIN
+ FOR idx IN REVERSE loop_start..3 LOOP
+ SELECT idx;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # MDEV-28588 SIGSEGV in __memmove_avx_unaligned_erms, strmake_root
+--echo #
+
+SET sql_mode=ORACLE;
+BEGIN END;
+
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW BEGIN END;
+--vertical_results
+SELECT ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA='test' AND TRIGGER_NAME='tr';
+--horizontal_results
+DROP TRIGGER tr;
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-19637 Crash on an SP variable assignment to a wrong subselect
+--echo #
+
+DELIMITER $$;
+--error ER_CANT_USE_OPTION_HERE
+DECLARE
+ a INT;
+BEGIN
+ SET a=(SELECT 1 FROM DUAL UNION SELECT HIGH_PRIORITY 2 FROM DUAL);
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/statement-expr.test b/mysql-test/suite/compat/oracle/t/statement-expr.test
new file mode 100644
index 00000000..1caab623
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/statement-expr.test
@@ -0,0 +1,86 @@
+# Testing expressions of different kinds in various non-SELECT statements
+
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+#
+# Subselects in non-SELECT statements
+#
+
+CREATE TABLE t1 (id INT, id1 INT);
+INSERT INTO t1 VALUES (1,7);
+INSERT INTO t1 VALUES (1,8);
+SELECT ROW(1,7) IN (SELECT id, id1 FROM t1 WHERE id1= 8);
+EXECUTE IMMEDIATE 'SELECT ROW(1, 7) IN (SELECT id, id1 FROM t1 WHERE id1= 8)';
+DROP TABLE t1;
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE 'SELECT ?' USING (1 IN (SELECT * FROM t1));
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE 'SELECT ?' USING (SELECT * FROM t1);
+
+
+CREATE TABLE t1 (id INT);
+INSERT INTO t1 VALUES (10);
+DELIMITER $$;
+CREATE PROCEDURE p1(a INT) AS BEGIN NULL; END;
+$$
+DELIMITER ;$$
+CALL p1((1) IN (SELECT * FROM t1));
+CALL p1(EXISTS (SELECT * FROM t1));
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+--error ER_PARSE_ERROR
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO=(1 IN (SELECT * FROM t1));
+--error ER_PARSE_ERROR
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO=EXISTS (SELECT * FROM t1);
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+BEGIN NOT ATOMIC
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ RESIGNAL SET MYSQL_ERRNO=(1 IN (SELECT * FROM t1));
+ SIGNAL SQLSTATE '01000';
+END;
+$$
+--error ER_PARSE_ERROR
+BEGIN NOT ATOMIC
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ RESIGNAL SET MYSQL_ERRNO=EXISTS (SELECT * FROM t1);
+ SIGNAL SQLSTATE '01000';
+END;
+$$
+DELIMITER ;$$
+
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+PREPARE stmt FROM (1 IN (SELECT * FROM t1));
+--error ER_SUBQUERIES_NOT_SUPPORTED
+PREPARE stmt FROM EXISTS (SELECT * FROM t1);
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE (1 IN (SELECT * FROM t1));
+--error ER_SUBQUERIES_NOT_SUPPORTED
+EXECUTE IMMEDIATE EXISTS (SELECT * FROM t1);
+
+--error ER_PARSE_ERROR
+GET DIAGNOSTICS CONDITION (1 IN (SELECT * FROM t1)) @errno=MYSQL_ERRNO;
+--error ER_PARSE_ERROR
+GET DIAGNOSTICS CONDITION EXISTS (SELECT * FROM t1) @errno=MYSQL_ERRNO;
+
+--error ER_SUBQUERIES_NOT_SUPPORTED
+PURGE BINARY LOGS BEFORE (1 IN (SELECT * FROM t1));
+--error ER_SUBQUERIES_NOT_SUPPORTED
+PURGE BINARY LOGS BEFORE EXISTS (SELECT * FROM t1);
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+DO 1 IN (SELECT * FROM t1);
+DO EXISTS (SELECT * FROM t1);
+DROP TABLE t1;
+
+
diff --git a/mysql-test/suite/compat/oracle/t/table_value_constr.test b/mysql-test/suite/compat/oracle/t/table_value_constr.test
new file mode 100644
index 00000000..ca3c40bb
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/table_value_constr.test
@@ -0,0 +1,1287 @@
+SET sql_mode=ORACLE;
+
+create table t1 (a int, b int);
+
+insert into t1 values (1,2),(4,6),(9,7),
+ (1,1),(2,5),(7,8);
+
+--echo # just VALUES
+
+values (1,2);
+
+values (1,2), (3,4), (5.6,0);
+
+values ('abc', 'def');
+
+--echo # UNION that uses VALUES structure(s)
+
+select 1,2
+union
+values (1,2);
+
+values (1,2)
+union
+select 1,2;
+
+select 1,2
+union
+values (1,2),(3,4),(5,6),(7,8);
+
+select 3,7
+union
+values (1,2),(3,4),(5,6);
+
+select 3,7,4
+union
+values (1,2,5),(4,5,6);
+
+select 1,2
+union
+values (1,7),(3,6.5);
+
+select 1,2
+union
+values (1,2.0),(3,6);
+
+select 1.8,2
+union
+values (1,2),(3,6);
+
+values (1,2.4),(3,6)
+union
+select 2.8,9;
+
+values (1,2),(3,4),(5,6),(7,8)
+union
+select 5,6;
+
+select 'ab','cdf'
+union
+values ('al','zl'),('we','q');
+
+values ('ab', 'cdf')
+union
+select 'ab','cdf';
+
+values (1,2)
+union
+values (1,2),(5,6);
+
+values (1,2)
+union
+values (3,4),(5,6);
+
+values (1,2)
+union
+values (1,2)
+union values (4,5);
+
+--echo # UNION ALL that uses VALUES structure
+
+values (1,2),(3,4)
+union all
+select 5,6;
+
+values (1,2),(3,4)
+union all
+select 1,2;
+
+select 5,6
+union all
+values (1,2),(3,4);
+
+select 1,2
+union all
+values (1,2),(3,4);
+
+values (1,2)
+union all
+values (1,2),(5,6);
+
+values (1,2)
+union all
+values (3,4),(5,6);
+
+values (1,2)
+union all
+values (1,2)
+union all
+values (4,5);
+
+values (1,2)
+union all
+values (1,2)
+union values (1,2);
+
+values (1,2)
+union
+values (1,2)
+union all
+values (1,2);
+
+--echo # EXCEPT that uses VALUES structure(s)
+
+select 1,2
+except
+values (3,4),(5,6);
+
+select 1,2
+except
+values (1,2),(3,4);
+
+values (1,2),(3,4)
+except
+select 5,6;
+
+values (1,2),(3,4)
+except
+select 1,2;
+
+values (1,2),(3,4)
+except
+values (5,6);
+
+values (1,2),(3,4)
+except
+values (1,2);
+
+--echo # INTERSECT that uses VALUES structure(s)
+
+select 1,2
+intersect
+values (3,4),(5,6);
+
+select 1,2
+intersect
+values (1,2),(3,4);
+
+values (1,2),(3,4)
+intersect
+select 5,6;
+
+values (1,2),(3,4)
+intersect
+select 1,2;
+
+values (1,2),(3,4)
+intersect
+values (5,6);
+
+values (1,2),(3,4)
+intersect
+values (1,2);
+
+--echo # combination of different structures that uses VALUES structures : UNION + EXCEPT
+
+values (1,2),(3,4)
+except
+select 1,2
+union values (1,2);
+
+values (1,2),(3,4)
+except
+values (1,2)
+union
+values (1,2);
+
+values (1,2),(3,4)
+except
+values (1,2)
+union
+values (3,4);
+
+values (1,2),(3,4)
+union
+values (1,2)
+except
+values (1,2);
+
+--echo # combination of different structures that uses VALUES structures : UNION ALL + EXCEPT
+
+values (1,2),(3,4)
+except
+select 1,2
+union all
+values (1,2);
+
+values (1,2),(3,4)
+except
+values (1,2)
+union all
+values (1,2);
+
+values (1,2),(3,4)
+except
+values (1,2)
+union all
+values (3,4);
+
+values (1,2),(3,4)
+union all
+values (1,2)
+except
+values (1,2);
+
+--echo # combination of different structures that uses VALUES structures : UNION + INTERSECT
+
+values (1,2),(3,4)
+intersect
+select 1,2
+union
+values (1,2);
+
+values (1,2),(3,4)
+intersect
+values (1,2)
+union
+values (1,2);
+
+values (1,2),(3,4)
+intersect
+values (1,2)
+union
+values (3,4);
+
+values (1,2),(3,4)
+union
+values (1,2)
+intersect
+values (1,2);
+
+--echo # combination of different structures that uses VALUES structures : UNION ALL + INTERSECT
+
+values (1,2),(3,4)
+intersect
+select 1,2
+union all
+values (1,2);
+
+values (1,2),(3,4)
+intersect
+values (1,2)
+union all
+values (1,2);
+
+values (1,2),(3,4)
+intersect
+values (1,2)
+union all
+values (3,4);
+
+values (1,2),(3,4)
+union all
+values (1,2)
+intersect
+values (1,2);
+
+--echo # combination of different structures that uses VALUES structures : UNION + UNION ALL
+
+values (1,2),(3,4)
+union all
+select 1,2
+union
+values (1,2);
+
+values (1,2),(3,4)
+union all
+values (1,2)
+union
+values (1,2);
+
+values (1,2),(3,4)
+union all
+values (1,2)
+union
+values (3,4);
+
+values (1,2),(3,4)
+union
+values (1,2)
+union all
+values (1,2);
+
+values (1,2)
+union
+values (1,2)
+union all
+values (1,2);
+
+--echo # CTE that uses VALUES structure(s) : non-recursive CTE
+
+with t2 as
+(
+ values (1,2),(3,4)
+)
+select * from t2;
+
+with t2 as
+(
+ select 1,2
+ union
+ values (1,2)
+)
+select * from t2;
+
+with t2 as
+(
+ select 1,2
+ union
+ values (1,2),(3,4)
+)
+select * from t2;
+
+with t2 as
+(
+ values (1,2)
+ union
+ select 1,2
+)
+select * from t2;
+
+with t2 as
+(
+ values (1,2),(3,4)
+ union
+ select 1,2
+)
+select * from t2;
+
+with t2 as
+(
+ values (5,6)
+ union
+ values (1,2),(3,4)
+)
+select * from t2;
+
+with t2 as
+(
+ values (1,2)
+ union
+ values (1,2),(3,4)
+)
+select * from t2;
+
+with t2 as
+(
+ select 1,2
+ union all
+ values (1,2),(3,4)
+)
+select * from t2;
+
+with t2 as
+(
+ values (1,2),(3,4)
+ union all
+ select 1,2
+)
+select * from t2;
+
+with t2 as
+(
+ values (1,2)
+ union all
+ values (1,2),(3,4)
+)
+select * from t2;
+
+--echo # recursive CTE that uses VALUES structure(s) : singe VALUES structure as anchor
+
+with recursive t2(a,b) as
+(
+ values(1,1)
+ union
+ select t1.a, t1.b
+ from t1,t2
+ where t1.a=t2.a
+)
+select * from t2;
+
+with recursive t2(a,b) as
+(
+ values(1,1)
+ union
+ select t1.a+1, t1.b
+ from t1,t2
+ where t1.a=t2.a
+)
+select * from t2;
+
+--echo # recursive CTE that uses VALUES structure(s) : several VALUES structures as anchors
+
+with recursive t2(a,b) as
+(
+ values(1,1)
+ union
+ values (3,4)
+ union
+ select t2.a+1, t1.b
+ from t1,t2
+ where t1.a=t2.a
+)
+select * from t2;
+
+--echo # recursive CTE that uses VALUES structure(s) : that uses UNION ALL
+
+with recursive t2(a,b,st) as
+(
+ values(1,1,1)
+ union all
+ select t2.a, t1.b, t2.st+1
+ from t1,t2
+ where t1.a=t2.a and st<3
+)
+select * from t2;
+
+--echo # recursive CTE that uses VALUES structure(s) : computation of factorial (first 10 elements)
+
+with recursive fact(n,f) as
+(
+ values(1,1)
+ union
+ select n+1,f*n from fact where n < 10
+)
+select * from fact;
+
+--echo # Derived table that uses VALUES structure(s) : singe VALUES structure
+
+select * from (values (1,2),(3,4)) as t2;
+
+--echo # Derived table that uses VALUES structure(s) : UNION with VALUES structure(s)
+
+select * from (select 1,2 union values (1,2)) as t2;
+
+select * from (select 1,2 union values (1,2),(3,4)) as t2;
+
+select * from (values (1,2) union select 1,2) as t2;
+
+select * from (values (1,2),(3,4) union select 1,2) as t2;
+
+select * from (values (5,6) union values (1,2),(3,4)) as t2;
+
+select * from (values (1,2) union values (1,2),(3,4)) as t2;
+
+--echo # Derived table that uses VALUES structure(s) : UNION ALL with VALUES structure(s)
+
+select * from (select 1,2 union all values (1,2),(3,4)) as t2;
+
+select * from (values (1,2),(3,4) union all select 1,2) as t2;
+
+select * from (values (1,2) union all values (1,2),(3,4)) as t2;
+
+--echo # CREATE VIEW that uses VALUES structure(s) : singe VALUES structure
+
+let $drop_view= drop view v1;
+let $select_view= select * from v1;
+
+create view v1 as values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+--echo # CREATE VIEW that uses VALUES structure(s) : UNION with VALUES structure(s)
+
+create view v1 as
+ select 1,2
+ union
+ values (1,2);
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ select 1,2
+ union
+ values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ values (1,2)
+ union
+ select 1,2;
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ values (1,2),(3,4)
+ union
+ select 1,2;
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ values (5,6)
+ union
+ values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+--echo # CREATE VIEW that uses VALUES structure(s) : UNION ALL with VALUES structure(s)
+
+create view v1 as
+ values (1,2)
+ union
+ values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ select 1,2
+ union all
+ values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ values (1,2),(3,4)
+ union all
+ select 1,2;
+
+eval $select_view;
+eval $drop_view;
+
+create view v1 as
+ values (1,2)
+ union all
+ values (1,2),(3,4);
+
+eval $select_view;
+eval $drop_view;
+
+--echo # IN-subquery with VALUES structure(s) : simple case
+let $query=
+select * from t1
+where a in (values (1));
+let $subst_query=
+select * from t1
+where a in (select * from (values (1)) as tvc_0);
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # IN-subquery with VALUES structure(s) : UNION with VALUES on the first place
+let $query=
+select * from t1
+where a in (values (1) union select 2);
+let $subst_query=
+select * from t1
+where a in (select * from (values (1)) as tvc_0 union
+ select 2);
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # IN-subquery with VALUES structure(s) : UNION with VALUES on the second place
+let $query=
+select * from t1
+where a in (select 2 union values (1));
+let $subst_query=
+select * from t1
+where a in (select 2 union
+ select * from (values (1)) tvc_0);
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # IN-subquery with VALUES structure(s) : UNION ALL
+let $query=
+select * from t1
+where a in (values (1) union all select b from t1);
+let $subst_query=
+select * from t1
+where a in (select * from (values (1)) as tvc_0 union all
+ select b from t1);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # NOT IN subquery with VALUES structure(s) : simple case
+let $query=
+select * from t1
+where a not in (values (1),(2));
+let $subst_query=
+select * from t1
+where a not in (select * from (values (1),(2)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # NOT IN subquery with VALUES structure(s) : UNION with VALUES on the first place
+let $query=
+select * from t1
+where a not in (values (1) union select 2);
+let $subst_query=
+select * from t1
+where a not in (select * from (values (1)) as tvc_0 union
+ select 2);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # NOT IN subquery with VALUES structure(s) : UNION with VALUES on the second place
+let $query=
+select * from t1
+where a not in (select 2 union values (1));
+let $subst_query=
+select * from t1
+where a not in (select 2 union
+ select * from (values (1)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ANY-subquery with VALUES structure(s) : simple case
+let $query=
+select * from t1
+where a = any (values (1),(2));
+let $subst_query=
+select * from t1
+where a = any (select * from (values (1),(2)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ANY-subquery with VALUES structure(s) : UNION with VALUES on the first place
+let $query=
+select * from t1
+where a = any (values (1) union select 2);
+let $subst_query=
+select * from t1
+where a = any (select * from (values (1)) as tvc_0 union
+ select 2);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ANY-subquery with VALUES structure(s) : UNION with VALUES on the second place
+let $query=
+select * from t1
+where a = any (select 2 union values (1));
+let $subst_query=
+select * from t1
+where a = any (select 2 union
+ select * from (values (1)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ALL-subquery with VALUES structure(s) : simple case
+let $query=
+select * from t1
+where a = all (values (1));
+let $subst_query=
+select * from t1
+where a = all (select * from (values (1)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ALL-subquery with VALUES structure(s) : UNION with VALUES on the first place
+let $query=
+select * from t1
+where a = all (values (1) union select 1);
+let $subst_query=
+select * from t1
+where a = all (select * from (values (1)) as tvc_0 union
+ select 1);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # ALL-subquery with VALUES structure(s) : UNION with VALUES on the second place
+let $query=
+select * from t1
+where a = any (select 1 union values (1));
+let $subst_query=
+select * from t1
+where a = any (select 1 union
+ select * from (values (1)) as tvc_0);
+
+eval $query;
+eval $subst_query;
+eval explain extended $query;
+eval explain extended $subst_query;
+
+--echo # prepare statement that uses VALUES structure(s): single VALUES structure
+
+prepare stmt1 from '
+values (1,2);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+--echo # prepare statement that uses VALUES structure(s): UNION with VALUES structure(s)
+
+prepare stmt1 from '
+ select 1,2
+ union
+ values (1,2),(3,4);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ values (1,2),(3,4)
+ union
+ select 1,2;
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ select 1,2
+ union
+ values (3,4)
+ union
+ values (1,2);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ values (5,6)
+ union
+ values (1,2),(3,4);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+--echo # prepare statement that uses VALUES structure(s): UNION ALL with VALUES structure(s)
+
+prepare stmt1 from '
+ select 1,2
+ union
+ values (1,2),(3,4);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ values (1,2),(3,4)
+ union all
+ select 1,2;
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ select 1,2
+ union all
+ values (3,4)
+ union all
+ values (1,2);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from '
+ values (1,2)
+ union all
+ values (1,2),(3,4);
+';
+
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+--echo # explain query that uses VALUES structure(s): single VALUES structure
+
+explain
+values (1,2);
+
+explain format=json
+values (1,2);
+
+--echo # explain query that uses VALUES structure(s): UNION with VALUES structure(s)
+
+explain
+select 1,2
+union
+values (1,2),(3,4);
+
+explain
+values (1,2),(3,4)
+union
+select 1,2;
+
+explain
+values (5,6)
+union
+values (1,2),(3,4);
+
+explain format=json
+select 1,2
+union
+values (1,2),(3,4);
+
+explain format=json
+values (1,2),(3,4)
+union
+select 1,2;
+
+explain format=json
+values (5,6)
+union
+values (1,2),(3,4);
+
+explain
+select 1,2
+union
+values (3,4)
+union
+values (1,2);
+
+explain format=json
+select 1,2
+union
+values (3,4)
+union
+values (1,2);
+
+--echo # explain query that uses VALUES structure(s): UNION ALL with VALUES structure(s)
+
+explain
+select 1,2
+union
+values (1,2),(3,4);
+
+explain
+values (1,2),(3,4)
+union all
+select 1,2;
+
+explain
+values (1,2)
+union all
+values (1,2),(3,4);
+
+explain format=json
+values (1,2),(3,4)
+union all
+select 1,2;
+
+explain format=json
+select 1,2
+union
+values (1,2),(3,4);
+
+explain format=json
+values (1,2)
+union all
+values (1,2),(3,4);
+
+explain
+select 1,2
+union all
+values (3,4)
+union all
+values (1,2);
+
+explain format=json
+select 1,2
+union all
+values (3,4)
+union all
+values (1,2);
+
+--echo # analyze query that uses VALUES structure(s): single VALUES structure
+
+analyze
+values (1,2);
+
+--source include/analyze-format.inc
+analyze format=json
+values (1,2);
+
+--echo # analyze query that uses VALUES structure(s): UNION with VALUES structure(s)
+
+analyze
+select 1,2
+union
+values (1,2),(3,4);
+
+analyze
+values (1,2),(3,4)
+union
+select 1,2;
+
+analyze
+values (5,6)
+union
+values (1,2),(3,4);
+
+--source include/analyze-format.inc
+analyze format=json
+select 1,2
+union
+values (1,2),(3,4);
+
+--source include/analyze-format.inc
+analyze format=json
+values (1,2),(3,4)
+union
+select 1,2;
+
+--source include/analyze-format.inc
+analyze format=json
+values (5,6)
+union
+values (1,2),(3,4);
+
+analyze
+select 1,2
+union
+values (3,4)
+union
+values (1,2);
+
+--source include/analyze-format.inc
+analyze format=json
+select 1,2
+union
+values (3,4)
+union
+values (1,2);
+
+--echo # analyze query that uses VALUES structure(s): UNION ALL with VALUES structure(s)
+
+analyze
+select 1,2
+union
+values (1,2),(3,4);
+
+analyze
+values (1,2),(3,4)
+union all
+select 1,2;
+
+analyze
+values (1,2)
+union all
+values (1,2),(3,4);
+
+--source include/analyze-format.inc
+analyze format=json
+values (1,2),(3,4)
+union all
+select 1,2;
+
+--source include/analyze-format.inc
+analyze format=json
+select 1,2
+union
+values (1,2),(3,4);
+
+--source include/analyze-format.inc
+analyze format=json
+values (1,2)
+union all
+values (1,2),(3,4);
+
+analyze
+select 1,2
+union all
+values (3,4)
+union all
+values (1,2);
+
+--source include/analyze-format.inc
+analyze format=json
+select 1,2
+union all
+values (3,4)
+union all
+values (1,2);
+
+--echo # different number of values in TVC
+--error ER_WRONG_NUMBER_OF_VALUES_IN_TVC
+values (1,2),(3,4,5);
+
+--echo # illegal parameter data types in TVC
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+values (1,point(1,1)),(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+values (1,point(1,1)+1);
+
+--echo # field reference in TVC
+--error ER_FIELD_REFERENCE_IN_TVC
+select * from (values (1), (b), (2)) as new_tvc;
+--error ER_FIELD_REFERENCE_IN_TVC
+select * from (values (1), (t1.b), (2)) as new_tvc;
+
+drop table t1;
+
+--echo #
+--echo # MDEV-15940: cursor over TVC
+--echo #
+
+DELIMITER |;
+
+DECLARE
+ v INT;
+ CURSOR cur IS VALUES(7);
+BEGIN
+ OPEN cur;
+ FETCH cur INTO v;
+ SELECT v;
+END;
+|
+
+DECLARE
+ v INT DEFAULT 0;
+BEGIN
+ FOR a IN (VALUES (7)) LOOP
+ SET v = v + 1;
+ END LOOP;
+ SELECT v;
+END;
+|
+
+DELIMITER ;|
+
+--echo #
+--echo # MDEV-16038: empty row in TVC
+--echo #
+
+--error ER_EMPTY_ROW_IN_TVC
+with t as (values (),()) select 1 from t;
+
+--echo #
+--echo # MDEV-17017: TVC in derived table
+--echo #
+
+create table t1 (a int);
+insert into t1 values (9), (3), (2);
+
+let $q1=
+select * from (values (7), (5), (8), (1), (3), (8), (1)) t;
+eval $q1;
+eval explain $q1;
+
+let $q2=
+select * from (values (1,11), (7,77), (3,31), (4,42)) t;
+eval $q2;
+eval explain $q2;
+
+let $q3=
+select * from (values (7), (5), (8), (1) union values (3), (8), (1)) t;
+eval $q3;
+eval explain $q3;
+
+let $q4=
+select * from (values (7), (5), (8), (1) union select * from t1) t;
+eval $q4;
+eval explain $q4;
+
+drop table t1;
+
+--echo #
+--echo # MDEV-16930: expression in the first row of TVC specifying derived table
+--echo #
+
+SELECT 1 + 1, 2, 'abc';
+SELECT * FROM (SELECT 1 + 1, 2, 'abc') t;
+WITH cte AS (SELECT 1 + 1, 2, 'abc') SELECT * FROM cte;
+SELECT 1 + 1, 2, 'abc' UNION SELECT 3+4, 3, 'abc';
+CREATE VIEW v1 AS SELECT 1 + 1, 2, 'abc';
+SELECT * FROM v1;
+DROP VIEW v1;
+
+VALUES(1 + 1,2,'abc');
+SELECT * FROM (VALUES(1 + 1,2,'abc')) t;
+
+--echo #
+--echo # MDEV-17894: tvc with ORDER BY ... LIMIT
+--echo #
+
+let $q=
+values (5), (7), (1), (3), (4) limit 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1 limit 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5,90), (7,20), (1,70), (3,50), (4,10) order by 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1);
+eval $q;
+eval explain extended $q;
+
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2;
+eval $q;
+eval explain extended $q;
+
+
+let $q=
+select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3);
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3;
+eval $q;
+eval explain extended $q;
+
+let $q=
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+eval $q;
+eval explain extended $q;
+
+let $q=
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union all
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3
+ order by 1 limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+--error ER_BAD_FIELD_ERROR
+values (5,90), (7,20), (1,70), (3,50), (4,10) order by 3;
+
+create view v1 as values (5), (7), (1), (3), (4) order by 1 limit 2;
+show create view v1;
+select * from v1;
+drop view v1;
+
+create view v1 as
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+show create view v1;
+select * from v1;
+drop view v1;
+
+--error ER_BAD_FIELD_ERROR
+create view v1 as values (5,90), (7,20), (1,70), (3,50), (4,10) order by 3;
+
+--error ER_BAD_FIELD_ERROR
+create view v1 as
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union
+( values (5), (7), (1), (3), (4) order by 2 limit 2 );
diff --git a/mysql-test/suite/compat/oracle/t/trigger.test b/mysql-test/suite/compat/oracle/t/trigger.test
new file mode 100644
index 00000000..45affb2d
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/trigger.test
@@ -0,0 +1,106 @@
+set sql_mode=ORACLE;
+
+--error ER_PARSE_ERROR
+:NEW.a := 1;
+--error ER_PARSE_ERROR
+:OLD.a := 1;
+--error ER_PARSE_ERROR
+:OLa.a := 1;
+
+--error ER_PARSE_ERROR
+SELECT :NEW.a;
+--error ER_PARSE_ERROR
+SELECT :OLD.a;
+--error ER_PARSE_ERROR
+SELECT :OLa.a;
+
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:= 10;
+INSERT INTO t1 VALUES ();
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW :NEW.a:= 10;
+INSERT INTO t1 VALUES ();
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+
+CREATE TABLE t1 (a INT);
+DELIMITER /;
+CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW
+BEGIN
+ IF :NEW.a IS NULL
+ THEN
+ :NEW.a:= 10;
+ END IF;
+END;
+/
+DELIMITER ;/
+INSERT INTO t1 VALUES (NULL);
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+DELIMITER /;
+CREATE TRIGGER tr1 BEFORE UPDATE ON t1 FOR EACH ROW
+BEGIN
+ IF :OLD.a IS NULL
+ THEN
+ :NEW.a:= 10;
+ END IF;
+END;
+/
+DELIMITER ;/
+INSERT INTO t1 VALUES (NULL);
+UPDATE t1 SET a=NULL;
+SELECT * FROM t1;
+DROP TRIGGER tr1;
+DROP TABLE t1;
+
+
+
+CREATE TABLE t1 (a INT, b INT, c INT);
+DELIMITER /;
+CREATE TRIGGER tr1 BEFORE INSERT ON t1
+FOR EACH ROW
+DECLARE
+ cnt INT := 0;
+BEGIN
+ IF :NEW.a IS NULL THEN cnt:=cnt+1; END IF;
+ IF :NEW.b IS NULL THEN cnt:=cnt+1; END IF;
+ IF :NEW.c IS NULL THEN :NEW.c:=cnt; END IF;
+END;
+/
+DELIMITER ;/
+INSERT INTO t1 VALUES ();
+INSERT INTO t1 VALUES (1, NULL, NULL);
+INSERT INTO t1 VALUES (NULL, 1, NULL);
+INSERT INTO t1 VALUES (1, 1, NULL);
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
+--echo #
+
+CREATE TABLE t1 (a INT, b INT, total INT);
+DELIMITER $$;
+CREATE TRIGGER tr1 BEFORE INSERT ON t1
+FOR EACH ROW
+DECLARE
+ va t1.a%TYPE:= :NEW.a;
+ vb t1.b%TYPE:= :NEW.b;
+BEGIN
+ :NEW.total:= va + vb;
+END;
+$$
+DELIMITER ;$$
+INSERT INTO t1 (a,b) VALUES (10, 20);
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/truncate.test b/mysql-test/suite/compat/oracle/t/truncate.test
new file mode 100644
index 00000000..ac540047
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/truncate.test
@@ -0,0 +1,16 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10588 sql_mode=ORACLE: TRUNCATE TABLE t1 [ {DROP|REUSE} STORAGE ]
+--echo #
+
+CREATE TABLE t1 (a INT);
+TRUNCATE TABLE t1 REUSE STORAGE;
+TRUNCATE TABLE t1 DROP STORAGE;
+DROP TABLE t1;
+
+# REUSE is actually a reserved word in Oracle.
+# But we don't reserve it for MDEV-10588
+
+CREATE TABLE reuse (reuse INT);
+DROP TABLE reuse;
diff --git a/mysql-test/suite/compat/oracle/t/type_blob.test b/mysql-test/suite/compat/oracle/t/type_blob.test
new file mode 100644
index 00000000..989581ee
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_blob.test
@@ -0,0 +1,17 @@
+SET sql_mode=ORACLE;
+CREATE TABLE t1 (a BLOB);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-20263 sql_mode=ORACLE: BLOB(65535) should not translate to LONGBLOB
+--echo #
+
+CREATE TABLE t1 (
+ c1 BLOB(100),
+ c2 BLOB(65535),
+ c3 BLOB(16777215),
+ c4 BLOB(16777216)
+);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/type_clob.test b/mysql-test/suite/compat/oracle/t/type_clob.test
new file mode 100644
index 00000000..275e368b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_clob.test
@@ -0,0 +1,10 @@
+SET sql_mode=ORACLE;
+
+# CLOB is not a reserved word even in sql_mode=ORACLE
+CREATE TABLE clob (clob INT);
+SHOW CREATE TABLE clob;
+DROP TABLE clob;
+
+CREATE TABLE t1 (a CLOB);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/type_date.test b/mysql-test/suite/compat/oracle/t/type_date.test
new file mode 100644
index 00000000..f6886d15
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_date.test
@@ -0,0 +1,101 @@
+SET sql_mode=ORACLE;
+CREATE TABLE t1 (a DATE);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode
+--echo #
+
+SET sql_mode=DEFAULT;
+--error ER_UNKNOWN_DATA_TYPE
+CREATE TABLE t1 (a unknown.DATE);
+
+
+SET sql_mode=DEFAULT;
+CREATE TABLE t1 (
+ def_date DATE,
+ mdb_date mariadb_schema.DATE,
+ ora_date oracle_schema.DATE,
+ max_date maxdb_schema.DATE
+);
+SHOW CREATE TABLE t1;
+SET sql_mode=ORACLE;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+SET sql_mode=ORACLE;
+CREATE TABLE t1 (
+ def_date DATE,
+ mdb_date mariadb_schema.DATE,
+ ora_date oracle_schema.DATE,
+ max_date maxdb_schema.DATE
+);
+SHOW CREATE TABLE t1;
+SET sql_mode=DEFAULT;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # ALTER..MODIFY and ALTER..CHANGE understand qualifiers
+--echo #
+
+SET sql_mode=DEFAULT;
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2001-01-01');
+SET sql_mode=ORACLE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+ALTER TABLE t1 MODIFY a DATE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+ALTER TABLE t1 MODIFY a mariadb_schema.DATE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+ALTER TABLE t1 MODIFY a oracle_schema.DATE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+ALTER TABLE t1 CHANGE a b mariadb_schema.DATE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+ALTER TABLE t1 CHANGE b a oracle_schema.DATE;
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+
+DROP TABLE t1;
+
+--echo #
+--echo # Qualified syntax is not supported yet in SP
+--echo # See MDEV-23353 Qualified data types in SP
+--echo #
+
+SET sql_mode=ORACLE;
+DELIMITER $$;
+--error ER_UNKNOWN_DATA_TYPE
+CREATE FUNCTION f1() RETURN mariadb_schema.DATE AS
+BEGIN
+ RETURN CURRENT_DATE;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1(a mariadb_schema.DATE) AS
+BEGIN
+ NULL;
+END;
+$$
+--error ER_PARSE_ERROR
+CREATE PROCEDURE p1() AS
+ a mariadb_schema.DATE;
+BEGIN
+ NULL;
+END;
+$$
+DELIMITER ;$$
diff --git a/mysql-test/suite/compat/oracle/t/type_number.test b/mysql-test/suite/compat/oracle/t/type_number.test
new file mode 100644
index 00000000..d8ea04ab
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_number.test
@@ -0,0 +1,9 @@
+SET sql_mode=ORACLE;
+
+CREATE TABLE t1 (a NUMBER);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a NUMBER(10,2));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/type_raw.test b/mysql-test/suite/compat/oracle/t/type_raw.test
new file mode 100644
index 00000000..3b54f5a3
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_raw.test
@@ -0,0 +1,10 @@
+SET sql_mode=ORACLE;
+
+# RAW is not a reserved word even in sql_mode=ORACLE
+CREATE TABLE raw (raw INT);
+SHOW CREATE TABLE raw;
+DROP TABLE raw;
+
+CREATE TABLE t1 (a RAW(10));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/type_varchar.test b/mysql-test/suite/compat/oracle/t/type_varchar.test
new file mode 100644
index 00000000..05691bfb
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_varchar.test
@@ -0,0 +1,9 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-11275 sql_mode=ORACLE: CAST(..AS VARCHAR(N))
+--echo #
+
+SELECT CAST(123 AS VARCHAR(10)) FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT CAST(123 AS VARCHAR) FROM DUAL;
diff --git a/mysql-test/suite/compat/oracle/t/type_varchar2.test b/mysql-test/suite/compat/oracle/t/type_varchar2.test
new file mode 100644
index 00000000..bd024371
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/type_varchar2.test
@@ -0,0 +1,19 @@
+SET sql_mode=ORACLE;
+
+# VARCHAR2 is not a reserved word even in sql_mode=ORACLE
+CREATE TABLE varchar2 (varchar2 INT);
+SHOW CREATE TABLE varchar2;
+DROP TABLE varchar2;
+
+CREATE TABLE t1 (a VARCHAR2(10));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-11275 sql_mode=ORACLE: CAST(..AS VARCHAR(N))
+--echo #
+
+SELECT CAST(123 AS VARCHAR2(10)) FROM DUAL;
+--error ER_PARSE_ERROR
+SELECT CAST(123 AS VARCHAR2) FROM DUAL;
diff --git a/mysql-test/suite/compat/oracle/t/update_innodb.test b/mysql-test/suite/compat/oracle/t/update_innodb.test
new file mode 100644
index 00000000..79660920
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/update_innodb.test
@@ -0,0 +1,31 @@
+--source include/have_innodb.inc
+
+--echo #
+--echo # MDEV-19535 sql_mode=ORACLE: 'SELECT INTO @var FOR UPDATE' does not lock the table
+--echo #
+
+SET sql_mode='ORACLE';
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) engine=innodb;
+INSERT INTO t1 VALUES (1);
+START TRANSACTION;
+--enable_prepare_warnings
+SELECT a AS a_con1 FROM t1 INTO @a FOR UPDATE;
+
+--connect(con2,localhost,root,,)
+SET sql_mode='ORACLE';
+START TRANSACTION;
+--send SELECT a AS a_con2 FROM t1 INTO @a FOR UPDATE;
+
+--connection default
+UPDATE t1 SET a=a+100;
+COMMIT;
+
+--connection con2
+--reap
+SELECT a AS con2 FROM t1;
+COMMIT;
+
+--disable_prepare_warnings
+
+--connection default
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/variables.test b/mysql-test/suite/compat/oracle/t/variables.test
new file mode 100644
index 00000000..4705cac1
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/variables.test
@@ -0,0 +1,38 @@
+SET sql_mode=oracle;
+
+--echo #
+--echo # MDEV-10411 Providing compatibility for basic PL/SQL constructs
+--echo # Part 6: Assignment operator
+--echo #
+
+max_sort_length:=1030;
+SELECT @@max_sort_length;
+max_sort_length:=DEFAULT;
+
+--echo #
+--echo # Testing that SP variables shadow global variables in assignments
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1
+AS
+BEGIN
+ max_sort_length:=1030;
+ DECLARE
+ max_sort_length INT DEFAULT 1031;
+ BEGIN
+ SELECT @@max_sort_length, max_sort_length;
+ max_sort_length:=1032;
+ SELECT @@max_sort_length, max_sort_length;
+ END;
+ SELECT @@max_sort_length;
+ max_sort_length:= DEFAULT;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+
+--echo #
+--echo # End of MDEV-10411 Providing compatibility for basic PL/SQL constructs (part 6)
+--echo #
diff --git a/mysql-test/suite/compat/oracle/t/vcol.test b/mysql-test/suite/compat/oracle/t/vcol.test
new file mode 100644
index 00000000..1317006f
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/vcol.test
@@ -0,0 +1,16 @@
+--echo #
+--echo # MDEV-13500 sql_mode=ORACLE: can't create a virtual column with function MOD
+--echo #
+
+SET sql_mode=ORACLE;
+CREATE TABLE t1 (c1 INTEGER, c2 INTEGER AS (c1 MOD 10) VIRTUAL);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 (c1) VALUES (999);
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+CREATE VIEW v1 AS SELECT a MOD 10 FROM t1;
+SHOW CREATE VIEW v1;
+DROP VIEW v1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/versioning.test b/mysql-test/suite/compat/oracle/t/versioning.test
new file mode 100644
index 00000000..abcca8c5
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/versioning.test
@@ -0,0 +1,23 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-15975 PL/SQL parser does not understand historical queries
+--echo #
+
+CREATE TABLE t1 (a INT) WITH SYSTEM VERSIONING;
+INSERT INTO t1 VALUES (10);
+DELETE FROM t1;
+INSERT INTO t1 VALUES (20);
+SELECT * FROM t1 FOR SYSTEM_TIME ALL;
+SELECT * FROM t1 FOR SYSTEM_TIME AS OF (NOW()+INTERVAL 10 YEAR);
+DROP TABLE t1;
+
+--echo #
+--echo # MDEV-17959 Assertion `opt_bootstrap || mysql_parse_status || thd->lex->select_stack_top == 0' failed in parse_sql upon DELETE HISTORY under ORACLE mode
+--echo #
+
+SET SQL_MODE= ORACLE;
+CREATE TABLE t1 (a INT);
+--error ER_VERS_NOT_VERSIONED
+DELETE HISTORY FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/win.test b/mysql-test/suite/compat/oracle/t/win.test
new file mode 100644
index 00000000..c6f0b647
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/win.test
@@ -0,0 +1,22 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-13384: "window" seems like a reserved column name but it's not listed as one
+--echo #
+--echo # Currently we allow window as an identifier, except for table aliases.
+--echo #
+
+CREATE TABLE door (id INT, window VARCHAR(10));
+
+--error ER_PARSE_ERROR
+SELECT id
+FROM door as window;
+
+SELECT id, window
+FROM door;
+
+--error ER_PARSE_ERROR
+SELECT id, window
+FROM door as window;
+
+DROP TABLE door;