summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-08-18 23:36:42 +0400
committerAlexander Barkov <bar@mariadb.org>2018-02-25 21:08:19 +0400
commit583eb96c2492adb87e88a014b24eb0724fb00257 (patch)
tree501cb4e5e3855400e79df8911ac43ef1f89300b3
parent83ea839fb15dd5ed616d2b3152ccc5472ee5e5e6 (diff)
downloadmariadb-git-583eb96c2492adb87e88a014b24eb0724fb00257.tar.gz
MDEV-11952 Oracle-style packages: stage#5mariadb-10.3.5bb-10.3-compatibility
- CREATE PACKAGE [BODY] statements are now entirely written to mysql.proc with type='PACKAGE' and type='PACKAGE BODY'. - CREATE PACKAGE BODY now supports IF NOT EXISTS - DROP PACKAGE BODY now supports IF EXISTS - CREATE OR REPLACE PACKAGE [BODY] is now supported - CREATE PACKAGE [BODY] now support the DEFINER clause: CREATE DEFINER user@host PACKAGE pkg ... END; CREATE DEFINER user@host PACKAGE BODY pkg ... END; - CREATE PACKAGE [BODY] now supports SQL SECURITY and COMMENT clauses, e.g.: CREATE PACKAGE p1 SQL SECURITY INVOKER COMMENT "comment" AS ... END; - Package routines are now created from the package CREATE PACKAGE BODY statement and don't produce individual records in mysql.proc. - CREATE PACKAGE BODY now supports package-wide variables. Package variables can be read and set inside package routines. Package variables are stored in a separate sp_rcontext, which is cached in THD on the first packate routine call. - CREATE PACKAGE BODY now supports the initialization section. - All public routines (i.e. declared in CREATE PACKAGE) must have implementations in CREATE PACKAGE BODY - Only public package routines are available outside of the package - {CREATE|DROP} PACKAGE [BODY] now respects CREATE ROUTINE and ALTER ROUTINE privileges - "GRANT EXECUTE ON PACKAGE BODY pkg" is now supported - SHOW CREATE PACKAGE [BODY] is now supported - SHOW PACKAGE [BODY] STATUS is now supported - CREATE and DROP for PACKAGE [BODY] now works for non-current databases - mysqldump now supports packages - "SHOW {PROCEDURE|FUNCTION) CODE pkg.routine" now works for package routines - "SHOW PACKAGE BODY CODE pkg" now works (the package initialization section) - A new package body level MDL was added - Recursive calls for package procedures are now possible - Routine forward declarations in CREATE PACKATE BODY are now supported. - Package body variables now work as SP OUT parameters - Package body variables now work as SELECT INTO targets - Package body variables now support ROW, %ROWTYPE, %TYPE
-rw-r--r--client/mysqldump.c21
-rw-r--r--mysql-test/r/information_schema.result2
-rw-r--r--mysql-test/r/information_schema_routines.result10
-rw-r--r--mysql-test/r/mysqld--help.result2
-rw-r--r--mysql-test/r/show_check.result4
-rw-r--r--mysql-test/r/skip_grants.result2
-rw-r--r--mysql-test/r/system_mysql_db.result4
-rw-r--r--mysql-test/r/system_mysql_db_fix40123.result4
-rw-r--r--mysql-test/r/system_mysql_db_fix50030.result4
-rw-r--r--mysql-test/r/system_mysql_db_fix50117.result4
-rw-r--r--mysql-test/suite/compat/oracle/r/binlog_stm_sp_package.result268
-rw-r--r--mysql-test/suite/compat/oracle/r/rpl_sp_package.result183
-rw-r--r--mysql-test/suite/compat/oracle/r/rpl_sp_package_variables.result38
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-code.result245
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-db.result43
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-package.result96
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-trigger.result44
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-view.result42
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-innodb.result75
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-mdl.result80
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-mysqldump.result261
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package-security.result322
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-package.result2871
-rw-r--r--mysql-test/suite/compat/oracle/r/sp.result7
-rw-r--r--mysql-test/suite/compat/oracle/t/binlog_stm_sp_package.test158
-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/sp-cache-invalidate.inc11
-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-innodb.test62
-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.test93
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package-security.test331
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-package.test2626
-rw-r--r--mysql-test/suite/compat/oracle/t/sp.test5
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_is.result4
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_is_embedded.result4
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_mysql.result8
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result8
-rw-r--r--mysql-test/suite/funcs_1/r/is_routines.result6
-rw-r--r--mysql-test/suite/funcs_1/r/is_routines_embedded.result6
-rw-r--r--mysql-test/suite/roles/acl_statistics.result4
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_server_embedded.result4
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result4
-rw-r--r--mysql-test/suite/versioning/r/partition.result14
-rw-r--r--plugin/metadata_lock_info/metadata_lock_info.cc3
-rw-r--r--scripts/mysql_system_tables.sql4
-rw-r--r--scripts/mysql_system_tables_fix.sql10
-rw-r--r--sql/item.cc59
-rw-r--r--sql/item.h52
-rw-r--r--sql/item_create.cc17
-rw-r--r--sql/item_func.cc11
-rw-r--r--sql/item_func.h6
-rw-r--r--sql/item_xmlfunc.cc7
-rw-r--r--sql/lex.h2
-rw-r--r--sql/mdl.cc2
-rw-r--r--sql/mdl.h1
-rw-r--r--sql/mysqld.cc11
-rw-r--r--sql/share/errmsg-utf8.txt4
-rw-r--r--sql/sp.cc667
-rw-r--r--sql/sp.h244
-rw-r--r--sql/sp_head.cc361
-rw-r--r--sql/sp_head.h129
-rw-r--r--sql/sp_pcontext.h3
-rw-r--r--sql/sp_rcontext.cc27
-rw-r--r--sql/sp_rcontext.h3
-rw-r--r--sql/sql_acl.cc106
-rw-r--r--sql/sql_base.cc33
-rw-r--r--sql/sql_class.cc24
-rw-r--r--sql/sql_class.h48
-rw-r--r--sql/sql_cmd.h15
-rw-r--r--sql/sql_lex.cc319
-rw-r--r--sql/sql_lex.h47
-rw-r--r--sql/sql_parse.cc33
-rw-r--r--sql/sql_prepare.cc21
-rw-r--r--sql/sql_show.cc28
-rw-r--r--sql/sql_show.h2
-rw-r--r--sql/sql_string.h4
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_yacc.yy50
-rw-r--r--sql/sql_yacc_ora.yy411
86 files changed, 11065 insertions, 279 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c
index 2c55fc381b1..9ea19855f0b 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -2482,7 +2482,14 @@ static void print_blob_as_hex(FILE *output_file, const char *str, ulong len)
static uint dump_routines_for_db(char *db)
{
char query_buff[QUERY_LENGTH];
- const char *routine_type[]= {"FUNCTION", "PROCEDURE"};
+ const char *routine_type[]= {"FUNCTION",
+ "PROCEDURE",
+ "PACKAGE",
+ "PACKAGE BODY"};
+ const char *create_caption_xml[]= {"Create Function",
+ "Create Procedure",
+ "Create Package",
+ "Create Package Body"};
char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3];
char *routine_name;
int i;
@@ -2520,8 +2527,8 @@ static uint dump_routines_for_db(char *db)
if (opt_xml)
fputs("\t<routines>\n", sql_file);
- /* 0, retrieve and dump functions, 1, procedures */
- for (i= 0; i <= 1; i++)
+ /* 0, retrieve and dump functions, 1, procedures, etc. */
+ for (i= 0; i < 4; i++)
{
my_snprintf(query_buff, sizeof(query_buff),
"SHOW %s STATUS WHERE Db = '%s'",
@@ -2571,12 +2578,8 @@ static uint dump_routines_for_db(char *db)
{
if (opt_xml)
{
- if (i) /* Procedures. */
- print_xml_row(sql_file, "routine", routine_res, &row,
- "Create Procedure");
- else /* Functions. */
- print_xml_row(sql_file, "routine", routine_res, &row,
- "Create Function");
+ print_xml_row(sql_file, "routine", routine_res, &row,
+ create_caption_xml[i]);
continue;
}
if (opt_drop)
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index d9ed5e7e891..5fcda217d01 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -652,7 +652,7 @@ select * from t115;
table_name column_name column_type
proc db char(64)
proc name char(64)
-proc type enum('FUNCTION','PROCEDURE')
+proc type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
proc specific_name char(64)
proc language enum('SQL')
proc sql_data_access enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')
diff --git a/mysql-test/r/information_schema_routines.result b/mysql-test/r/information_schema_routines.result
index 0d83f3a2bda..eaa69adf740 100644
--- a/mysql-test/r/information_schema_routines.result
+++ b/mysql-test/r/information_schema_routines.result
@@ -9,7 +9,7 @@ ROUTINES CREATE TEMPORARY TABLE `ROUTINES` (
`ROUTINE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`ROUTINE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`ROUTINE_NAME` varchar(64) NOT NULL DEFAULT '',
- `ROUTINE_TYPE` varchar(9) NOT NULL DEFAULT '',
+ `ROUTINE_TYPE` varchar(13) NOT NULL DEFAULT '',
`DATA_TYPE` varchar(64) NOT NULL DEFAULT '',
`CHARACTER_MAXIMUM_LENGTH` int(21) DEFAULT NULL,
`CHARACTER_OCTET_LENGTH` int(21) DEFAULT NULL,
@@ -137,14 +137,14 @@ ORDINAL_POSITION 5
COLUMN_DEFAULT ''
IS_NULLABLE NO
DATA_TYPE varchar
-CHARACTER_MAXIMUM_LENGTH 9
-CHARACTER_OCTET_LENGTH 27
+CHARACTER_MAXIMUM_LENGTH 13
+CHARACTER_OCTET_LENGTH 39
NUMERIC_PRECISION NULL
NUMERIC_SCALE NULL
DATETIME_PRECISION NULL
CHARACTER_SET_NAME utf8
COLLATION_NAME utf8_general_ci
-COLUMN_TYPE varchar(9)
+COLUMN_TYPE varchar(13)
COLUMN_KEY
EXTRA
PRIVILEGES #
@@ -729,7 +729,7 @@ SPECIFIC_NAME varchar(64) NO
ROUTINE_CATALOG varchar(512) NO
ROUTINE_SCHEMA varchar(64) NO
ROUTINE_NAME varchar(64) NO
-ROUTINE_TYPE varchar(9) NO
+ROUTINE_TYPE varchar(13) NO
DATA_TYPE varchar(64) NO
CHARACTER_MAXIMUM_LENGTH int(21) YES NULL
CHARACTER_OCTET_LENGTH int(21) YES NULL
diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
index add82225739..ef6a49df08b 100644
--- a/mysql-test/r/mysqld--help.result
+++ b/mysql-test/r/mysqld--help.result
@@ -1552,7 +1552,7 @@ performance-schema-max-rwlock-instances -1
performance-schema-max-socket-classes 10
performance-schema-max-socket-instances -1
performance-schema-max-stage-classes 160
-performance-schema-max-statement-classes 191
+performance-schema-max-statement-classes 200
performance-schema-max-table-handles -1
performance-schema-max-table-instances -1
performance-schema-max-thread-classes 50
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index b86d72acb84..5083f1e615b 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -1131,7 +1131,7 @@ def information_schema ROUTINES ROUTINES SPECIFIC_NAME SPECIFIC_NAME 253 192 2 N
def information_schema ROUTINES ROUTINES ROUTINE_CATALOG ROUTINE_CATALOG 253 1536 3 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_SCHEMA ROUTINE_SCHEMA 253 192 4 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_NAME ROUTINE_NAME 253 192 2 N 1 0 33
-def information_schema ROUTINES ROUTINES ROUTINE_TYPE ROUTINE_TYPE 253 27 9 N 1 0 33
+def information_schema ROUTINES ROUTINES ROUTINE_TYPE ROUTINE_TYPE 253 39 9 N 1 0 33
def information_schema ROUTINES ROUTINES DTD_IDENTIFIER DTD_IDENTIFIER 252 589815 0 Y 16 0 33
def information_schema ROUTINES ROUTINES ROUTINE_BODY ROUTINE_BODY 253 24 3 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_DEFINITION ROUTINE_DEFINITION 252 589815 8 Y 16 0 33
@@ -1186,7 +1186,7 @@ def information_schema ROUTINES ROUTINES SPECIFIC_NAME SPECIFIC_NAME 253 192 2 N
def information_schema ROUTINES ROUTINES ROUTINE_CATALOG ROUTINE_CATALOG 253 1536 3 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_SCHEMA ROUTINE_SCHEMA 253 192 4 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_NAME ROUTINE_NAME 253 192 2 N 1 0 33
-def information_schema ROUTINES ROUTINES ROUTINE_TYPE ROUTINE_TYPE 253 27 8 N 1 0 33
+def information_schema ROUTINES ROUTINES ROUTINE_TYPE ROUTINE_TYPE 253 39 8 N 1 0 33
def information_schema ROUTINES ROUTINES DTD_IDENTIFIER DTD_IDENTIFIER 252 589815 7 Y 16 0 33
def information_schema ROUTINES ROUTINES ROUTINE_BODY ROUTINE_BODY 253 24 3 N 1 0 33
def information_schema ROUTINES ROUTINES ROUTINE_DEFINITION ROUTINE_DEFINITION 252 589815 8 Y 16 0 33
diff --git a/mysql-test/r/skip_grants.result b/mysql-test/r/skip_grants.result
index 1c055ef7385..de263074b61 100644
--- a/mysql-test/r/skip_grants.result
+++ b/mysql-test/r/skip_grants.result
@@ -88,6 +88,8 @@ Acl_column_grants 0
Acl_database_grants 0
Acl_function_grants 0
Acl_procedure_grants 0
+Acl_package_spec_grants 0
+Acl_package_body_grants 0
Acl_proxy_users 0
Acl_role_grants 0
Acl_roles 0
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index 1f8946d3de9..2abcfb92ffa 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -179,7 +179,7 @@ procs_priv CREATE TABLE `procs_priv` (
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(80) COLLATE utf8_bin NOT NULL DEFAULT '',
`Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '',
- `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
+ `Routine_type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') COLLATE utf8_bin NOT NULL,
`Grantor` char(141) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
@@ -205,7 +205,7 @@ Table Create Table
proc CREATE TABLE `proc` (
`db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`name` char(64) NOT NULL DEFAULT '',
- `type` enum('FUNCTION','PROCEDURE') NOT NULL,
+ `type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL,
`specific_name` char(64) NOT NULL DEFAULT '',
`language` enum('SQL') NOT NULL DEFAULT 'SQL',
`sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL',
diff --git a/mysql-test/r/system_mysql_db_fix40123.result b/mysql-test/r/system_mysql_db_fix40123.result
index 1f8946d3de9..2abcfb92ffa 100644
--- a/mysql-test/r/system_mysql_db_fix40123.result
+++ b/mysql-test/r/system_mysql_db_fix40123.result
@@ -179,7 +179,7 @@ procs_priv CREATE TABLE `procs_priv` (
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(80) COLLATE utf8_bin NOT NULL DEFAULT '',
`Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '',
- `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
+ `Routine_type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') COLLATE utf8_bin NOT NULL,
`Grantor` char(141) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
@@ -205,7 +205,7 @@ Table Create Table
proc CREATE TABLE `proc` (
`db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`name` char(64) NOT NULL DEFAULT '',
- `type` enum('FUNCTION','PROCEDURE') NOT NULL,
+ `type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL,
`specific_name` char(64) NOT NULL DEFAULT '',
`language` enum('SQL') NOT NULL DEFAULT 'SQL',
`sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL',
diff --git a/mysql-test/r/system_mysql_db_fix50030.result b/mysql-test/r/system_mysql_db_fix50030.result
index 7e64f004495..81b6da4c16d 100644
--- a/mysql-test/r/system_mysql_db_fix50030.result
+++ b/mysql-test/r/system_mysql_db_fix50030.result
@@ -179,7 +179,7 @@ procs_priv CREATE TABLE `procs_priv` (
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(80) COLLATE utf8_bin NOT NULL DEFAULT '',
`Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '',
- `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
+ `Routine_type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') COLLATE utf8_bin NOT NULL,
`Grantor` char(141) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
@@ -205,7 +205,7 @@ Table Create Table
proc CREATE TABLE `proc` (
`db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`name` char(64) NOT NULL DEFAULT '',
- `type` enum('FUNCTION','PROCEDURE') NOT NULL,
+ `type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL,
`specific_name` char(64) NOT NULL DEFAULT '',
`language` enum('SQL') NOT NULL DEFAULT 'SQL',
`sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL',
diff --git a/mysql-test/r/system_mysql_db_fix50117.result b/mysql-test/r/system_mysql_db_fix50117.result
index 1f8946d3de9..2abcfb92ffa 100644
--- a/mysql-test/r/system_mysql_db_fix50117.result
+++ b/mysql-test/r/system_mysql_db_fix50117.result
@@ -179,7 +179,7 @@ procs_priv CREATE TABLE `procs_priv` (
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(80) COLLATE utf8_bin NOT NULL DEFAULT '',
`Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '',
- `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
+ `Routine_type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') COLLATE utf8_bin NOT NULL,
`Grantor` char(141) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
@@ -205,7 +205,7 @@ Table Create Table
proc CREATE TABLE `proc` (
`db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`name` char(64) NOT NULL DEFAULT '',
- `type` enum('FUNCTION','PROCEDURE') NOT NULL,
+ `type` enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL,
`specific_name` char(64) NOT NULL DEFAULT '',
`language` enum('SQL') NOT NULL DEFAULT 'SQL',
`sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL',
diff --git a/mysql-test/suite/compat/oracle/r/binlog_stm_sp_package.result b/mysql-test/suite/compat/oracle/r/binlog_stm_sp_package.result
new file mode 100644
index 00000000000..8c1ee05686c
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/binlog_stm_sp_package.result
@@ -0,0 +1,268 @@
+SET sql_mode=ORACLE;
+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;
+$$
+Warnings:
+Note 1304 PACKAGE p1 already exists
+CREATE PACKAGE BODY p1 AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END;
+END;
+$$
+DROP PACKAGE BODY p1;
+DROP PACKAGE p1;
+DROP PACKAGE IF EXISTS p1;
+Warnings:
+Note 1305 PACKAGE test.p1 does not exist
+#
+# Creating a package with a COMMENT clause
+#
+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;
+$$
+DROP PACKAGE p1;
+#
+# Creating a package with a different DEFINER
+#
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+PROCEDURE p1;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+DROP PACKAGE p1;
+#
+# Creating a package with a different DEFINER, with SQL SECURITY INVOKER
+#
+CREATE DEFINER=xxx@localhost PACKAGE p1 SQL SECURITY INVOKER AS
+PROCEDURE p1;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+DROP PACKAGE p1;
+#
+# Creating a new package in a remote database
+#
+CREATE DATABASE test2;
+CREATE PACKAGE test2.test2 COMMENT 'package-test2-comment' AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END
+$$
+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;
+$$
+DROP PACKAGE BODY test2.test2;
+DROP PACKAGE test2.test2;
+DROP DATABASE test2;
+#
+# MDEV-13139 Package-wide variables in CREATE PACKAGE
+#
+CREATE TABLE t1 (a INT);
+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;
+$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+a
+10
+11
+# sp-cache-invalidate
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+a
+10
+11
+10
+11
+DROP PACKAGE p1;
+DROP TABLE t1;
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE "p1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE IF NOT EXISTS "p1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE BODY "p1" AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE BODY p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE IF EXISTS p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE "p1" COMMENT 'package-p1-comment'
+ AS
+PROCEDURE p1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE BODY "p1" COMMENT 'package-body-p1-comment'
+ AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="xxx"@"localhost" PACKAGE "p1" AS
+PROCEDURE p1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="xxx"@"localhost" PACKAGE BODY "p1" AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="xxx"@"localhost" PACKAGE "p1" SQL SECURITY INVOKER
+ AS
+PROCEDURE p1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="xxx"@"localhost" PACKAGE BODY "p1" SQL SECURITY INVOKER
+ AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # CREATE DATABASE test2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE "test2"."test2" COMMENT 'package-test2-comment'
+ AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" 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
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE BODY test2.test2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE test2.test2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # DROP DATABASE test2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT)
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE "p1" AS
+PROCEDURE p1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PACKAGE BODY "p1" AS
+a INT:=0;
+PROCEDURE p1 AS
+BEGIN
+INSERT INTO t1 VALUES (a);
+a:=a+1;
+END;
+BEGIN
+a:=10;
+END
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('a',10))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('a',11))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" FUNCTION "dummy"() RETURN int(11)
+AS
+BEGIN
+RETURN 1;
+END
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP FUNCTION dummy
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('a',10))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('a',11))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP PACKAGE p1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP TABLE "t1" /* generated by server */
diff --git a/mysql-test/suite/compat/oracle/r/rpl_sp_package.result b/mysql-test/suite/compat/oracle/r/rpl_sp_package.result
new file mode 100644
index 00000000000..2f10ec8ccd9
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/rpl_sp_package.result
@@ -0,0 +1,183 @@
+include/master-slave.inc
+[connection master]
+connection master;
+SET sql_mode=ORACLE;
+CREATE PACKAGE pack AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY pack AS
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END;
+PROCEDURE p1 AS
+BEGIN
+SELECT f1();
+END;
+END pack;
+$$
+connection slave;
+connection slave;
+SELECT * FROM mysql.proc WHERE db='test' AND name='pack';
+db test
+name pack
+type PACKAGE
+specific_name pack
+language SQL
+sql_data_access CONTAINS_SQL
+is_deterministic NO
+security_type DEFINER
+param_list
+returns
+body AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END
+definer root@localhost
+created #
+modified #
+sql_mode PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+db_collation latin1_swedish_ci
+body_utf8
+aggregate NONE
+db test
+name pack
+type PACKAGE BODY
+specific_name pack
+language SQL
+sql_data_access CONTAINS_SQL
+is_deterministic NO
+security_type DEFINER
+param_list
+returns
+body AS
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END;
+PROCEDURE p1 AS
+BEGIN
+SELECT f1();
+END;
+END
+definer root@localhost
+created #
+modified #
+sql_mode PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+db_collation latin1_swedish_ci
+body_utf8
+aggregate NONE
+SELECT * FROM mysql.proc WHERE db='test' AND name LIKE 'pack.%';
+SET @@sql_mode=ORACLE;
+SELECT pack.f1();
+pack.f1()
+10
+CALL pack.p1();
+f1()
+10
+SET @@sql_mode=DEFAULT;
+connection master;
+DROP PACKAGE pack;
+connection slave;
+connection slave;
+SELECT COUNT(*) FROM mysql.proc WHERE db='test' AND name='pack';
+COUNT(*)
+0
+#
+# Creating a package with a COMMENT
+#
+connection master;
+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;
+$$
+SELECT definer, name, security_type, type, `comment` FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type comment
+root@localhost p1 DEFINER PACKAGE package-p1-comment
+root@localhost p1 DEFINER PACKAGE BODY package-body-p1-comment
+connection slave;
+SELECT definer, name, security_type, type, `comment` FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type comment
+root@localhost p1 DEFINER PACKAGE package-p1-comment
+root@localhost p1 DEFINER PACKAGE BODY package-body-p1-comment
+connection master;
+DROP PACKAGE p1;
+connection slave;
+#
+# Creating a package with a different DEFINER
+#
+connection master;
+CREATE DEFINER=xxx@localhost PACKAGE p1 AS
+PROCEDURE p1;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 DEFINER PACKAGE
+xxx@localhost p1 DEFINER PACKAGE BODY
+connection slave;
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 DEFINER PACKAGE
+xxx@localhost p1 DEFINER PACKAGE BODY
+connection master;
+DROP PACKAGE p1;
+connection slave;
+#
+# Creating a package with a different DEFINER + SQL SECURITY INVOKER
+#
+connection master;
+CREATE DEFINER=xxx@localhost PACKAGE p1 SQL SECURITY INVOKER AS
+PROCEDURE p1;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+CREATE DEFINER=xxx@localhost PACKAGE BODY p1 SQL SECURITY INVOKER AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+END;
+$$
+Warnings:
+Note 1449 The user specified as a definer ('xxx'@'localhost') does not exist
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 INVOKER PACKAGE
+xxx@localhost p1 INVOKER PACKAGE BODY
+connection slave;
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 INVOKER PACKAGE
+xxx@localhost p1 INVOKER PACKAGE BODY
+connection master;
+DROP PACKAGE p1;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/compat/oracle/r/rpl_sp_package_variables.result b/mysql-test/suite/compat/oracle/r/rpl_sp_package_variables.result
new file mode 100644
index 00000000000..764686e4118
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/rpl_sp_package_variables.result
@@ -0,0 +1,38 @@
+include/master-slave.inc
+[connection master]
+connection master;
+SET sql_mode=ORACLE;
+#
+# MDEV-13139 Package-wide variables in CREATE PACKAGE
+#
+connection master;
+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;
+$$
+CALL p1.p1();
+CALL p1.p1();
+SELECT * FROM t1;
+a
+10
+10
+connection slave;
+SELECT * FROM t1;
+a
+10
+10
+connection master;
+DROP PACKAGE p1;
+DROP TABLE t1;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-code.result b/mysql-test/suite/compat/oracle/r/sp-package-code.result
new file mode 100644
index 00000000000..0dea72dba89
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-code.result
@@ -0,0 +1,245 @@
+SET sql_mode=ORACLE;
+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;
+$$
+SHOW PROCEDURE CODE pkg1.p1;
+Pos Instruction
+0 set b@0 20
+1 set b@0 PACKAGE_BODY.a@0
+2 set b@0 PACKAGE_BODY.a@0 + 1
+3 set PACKAGE_BODY.a@0 b@0
+4 set PACKAGE_BODY.a@0 b@0 + 1
+5 set PACKAGE_BODY.a@0 PACKAGE_BODY.a@0 + 1
+6 stmt 31 "SET @a:=@a+2"
+7 stmt 0 "SELECT f1() FROM DUAL"
+SHOW FUNCTION CODE pkg1.f1;
+Pos Instruction
+0 freturn int PACKAGE_BODY.a@0
+SHOW PACKAGE BODY CODE pkg1;
+Pos Instruction
+0 set a@0 10
+1 set a@0 a@0 + 1
+2 set b@1 NULL
+3 set b@1 a@0
+4 set b@1 a@0 + 1
+5 set a@0 b@1
+6 set a@0 b@1 + 1
+7 jump 11
+CALL pkg1.p2show;
+Pos Instruction
+0 freturn blob 'This is f2public'
+Pos Instruction
+0 freturn blob 'This is f2private'
+Pos Instruction
+0 stmt 0 "SELECT 'This is p2public'"
+Pos Instruction
+0 stmt 0 "SELECT 'This is p2private'"
+Pos Instruction
+0 stmt 110 "SHOW FUNCTION CODE f2public"
+1 stmt 110 "SHOW FUNCTION CODE f2private"
+2 stmt 109 "SHOW PROCEDURE CODE p2public"
+3 stmt 109 "SHOW PROCEDURE CODE p2private"
+4 stmt 109 "SHOW PROCEDURE CODE p2show"
+DROP PACKAGE pkg1;
+CREATE TABLE t1 (a INT);
+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;
+$$
+SHOW PROCEDURE CODE pkg1.p1;
+Pos Instruction
+0 set b@0 20
+1 set b@0 PACKAGE_BODY.a@0
+2 set b@0 PACKAGE_BODY.a@0 + 1
+3 set b@0 b@0 + 1
+4 set PACKAGE_BODY.a@0 b@0
+5 set PACKAGE_BODY.a@0 b@0 + 1
+6 set PACKAGE_BODY.a@0 PACKAGE_BODY.a@0 + 1
+SHOW PACKAGE BODY CODE pkg1;
+Pos Instruction
+0 set a@0 10
+1 set a@0 a@0 + 1
+2 set b@1 NULL
+3 set b@1 a@0
+4 set b@1 a@0 + 1
+5 set a@0 b@1
+6 set a@0 b@1 + 1
+7 jump 11
+DROP PACKAGE pkg1;
+DROP TABLE t1;
+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;
+$$
+SHOW PROCEDURE CODE pkg1.p1;
+Pos Instruction
+0 set b@0 (20,'x20')
+1 set b@0 PACKAGE_BODY.a@0
+2 set PACKAGE_BODY.a@0 b@0
+3 set b.a@0[0] PACKAGE_BODY.a.a@0[0] + 1
+4 set PACKAGE_BODY.a.a@0[0] b.a@0[0] + 1
+5 set PACKAGE_BODY.a.a@0[0] PACKAGE_BODY.a.a@0[0] + 1
+SHOW PACKAGE BODY CODE pkg1;
+Pos Instruction
+0 set a@0 (10,'x10')
+1 set a.a@0[0] a.a@0[0] + 1
+2 set b@1 (30,'x30')
+3 set b@1 a@0
+4 set b.a@1[0] a.a@0[0] + 1
+5 set a@0 b@1
+6 set a.a@0[0] b.a@1[0] + 1
+7 jump 11
+DROP PACKAGE pkg1;
+CREATE TABLE t1 (a INT, b TEXT);
+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;
+$$
+SHOW PROCEDURE CODE pkg1.p1;
+Pos Instruction
+0 set b@0 (20,'x20')
+1 set b@0 PACKAGE_BODY.a@0
+2 set PACKAGE_BODY.a@0 b@0
+3 set b.a@0["a"] PACKAGE_BODY.a.a@0["a"] + 1
+4 set PACKAGE_BODY.a.a@0["a"] b.a@0["a"] + 1
+5 set PACKAGE_BODY.a.a@0["a"] PACKAGE_BODY.a.a@0["a"] + 1
+SHOW PACKAGE BODY CODE pkg1;
+Pos Instruction
+0 set a@0 (10,'x10')
+1 set a.a@0["a"] a.a@0["a"] + 1
+2 set b@1 (30,'x30')
+3 set b@1 a@0
+4 set b.a@1["a"] a.a@0["a"] + 1
+5 set a@0 b@1
+6 set a.a@0["a"] b.a@1["a"] + 1
+7 jump 11
+DROP PACKAGE pkg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-db.result b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-db.result
new file mode 100644
index 00000000000..95f45c25314
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-db.result
@@ -0,0 +1,43 @@
+#
+# MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+#
+SET @object_type='db';
+#
+# Start of sp-package-concurrent-dml.inc
+#
+SET sql_mode=ORACLE;
+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;
+$$
+connect con2,localhost,root;
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+connection default;
+CALL pkg1.p1;
+connection con2;
+CREATE DATABASE test1;
+CREATE FUNCTION test1.f1() RETURNS INT RETURN 10;
+DROP DATABASE test1;
+DO RELEASE_LOCK('mdev15070');
+disconnect con2;
+connection default;
+msg
+This is p1
+msg
+This is p2
+DROP PACKAGE IF EXISTS pkg1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-package.result b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-package.result
new file mode 100644
index 00000000000..eb7d38a8f67
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-package.result
@@ -0,0 +1,96 @@
+#
+# MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+#
+SET @object_type='package_replace_pkg1';
+#
+# Start of sp-package-concurrent-dml.inc
+#
+SET sql_mode=ORACLE;
+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;
+$$
+connect con2,localhost,root;
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+connection default;
+CALL pkg1.p1;
+connection con2;
+SET sql_mode=ORACLE;
+CREATE OR REPLACE PACKAGE pkg1 AS
+PROCEDURE p1;
+END;
+$$
+DROP PACKAGE pkg1;
+DO RELEASE_LOCK('mdev15070');
+disconnect con2;
+connection default;
+msg
+This is p1
+msg
+This is p2
+DROP PACKAGE IF EXISTS pkg1;
+Warnings:
+Note 1305 PACKAGE test.pkg1 does not exist
+SET @object_type='package_body_replace_pkg1';
+#
+# Start of sp-package-concurrent-dml.inc
+#
+SET sql_mode=ORACLE;
+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;
+$$
+connect con2,localhost,root;
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+connection default;
+CALL pkg1.p1;
+connection con2;
+SET sql_mode=ORACLE;
+CREATE OR REPLACE PACKAGE BODY pkg1 AS
+PROCEDURE p1 AS
+BEGIN
+SELECT 'This is p1 version 2' AS msg;
+END;
+END;
+$$
+DROP PACKAGE pkg1;
+DO RELEASE_LOCK('mdev15070');
+disconnect con2;
+connection default;
+msg
+This is p1
+msg
+This is p2
+DROP PACKAGE IF EXISTS pkg1;
+Warnings:
+Note 1305 PACKAGE test.pkg1 does not exist
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-trigger.result b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-trigger.result
new file mode 100644
index 00000000000..8181714f59c
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-trigger.result
@@ -0,0 +1,44 @@
+#
+# MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+#
+SET @object_type='trigger';
+#
+# Start of sp-package-concurrent-dml.inc
+#
+SET sql_mode=ORACLE;
+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;
+$$
+connect con2,localhost,root;
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+connection default;
+CALL pkg1.p1;
+connection con2;
+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;
+DO RELEASE_LOCK('mdev15070');
+disconnect con2;
+connection default;
+msg
+This is p1
+msg
+This is p2
+DROP PACKAGE IF EXISTS pkg1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-view.result b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-view.result
new file mode 100644
index 00000000000..b0ceec608a6
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-concurrent-dml-view.result
@@ -0,0 +1,42 @@
+#
+# MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+#
+SET @object_type='view';
+#
+# Start of sp-package-concurrent-dml.inc
+#
+SET sql_mode=ORACLE;
+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;
+$$
+connect con2,localhost,root;
+connection con2;
+DO GET_LOCK('mdev15070', 120);
+connection default;
+CALL pkg1.p1;
+connection con2;
+CREATE VIEW v1 AS SELECT 1 AS c;
+DROP VIEW v1;
+DO RELEASE_LOCK('mdev15070');
+disconnect con2;
+connection default;
+msg
+This is p1
+msg
+This is p2
+DROP PACKAGE IF EXISTS pkg1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-innodb.result b/mysql-test/suite/compat/oracle/r/sp-package-innodb.result
new file mode 100644
index 00000000000..50eb2dc6cd0
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-innodb.result
@@ -0,0 +1,75 @@
+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';
+ENGINE
+InnoDB
+INSERT INTO t1 VALUES (10,'none');
+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;
+$$
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+a routine
+10 none
+11 pkg1 initialization
+12 p1
+DELETE FROM t1;
+# sp-cache-invalidate
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+a routine
+NULL pkg1 initialization
+NULL p1
+ROLLBACK;
+SELECT * FROM t1 ORDER BY a;
+a routine
+DELETE FROM t1;
+# sp-cache-invalidate
+INSERT INTO t1 VALUES (20,'none');
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+a routine
+20 none
+21 pkg1 initialization
+22 p1
+COMMIT;
+SELECT * FROM t1 ORDER BY a;
+a routine
+20 none
+21 pkg1 initialization
+22 p1
+DELETE FROM t1;
+# sp-cache-invalidate
+INSERT INTO t1 VALUES (20,'none');
+START TRANSACTION;
+CALL pkg1.p1;
+SELECT * FROM t1 ORDER BY a;
+a routine
+20 none
+21 pkg1 initialization
+22 p1
+ROLLBACK;
+SELECT * FROM t1 ORDER BY a;
+a routine
+20 none
+DELETE FROM t1;
+DROP PACKAGE pkg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-mdl.result b/mysql-test/suite/compat/oracle/r/sp-package-mdl.result
new file mode 100644
index 00000000000..18cc834461c
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-mdl.result
@@ -0,0 +1,80 @@
+SET sql_mode=ORACLE;
+DO GET_LOCK('lock',300);
+connect conn1,localhost,root,,;
+SET sql_mode=ORACLE;
+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;
+$$
+SELECT pkg1.f1();
+connection default;
+connect conn2,localhost,root,,;
+SET sql_mode=ORACLE;
+DROP PACKAGE pkg1;
+connection default;
+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;
+CONN 0
+INFO 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
+STATE Filling schema table
+LOCK_MODE MDL_SHARED_NO_WRITE
+LOCK_TYPE User lock
+TABLE_NAME
+CONN 1
+INFO DO GET_LOCK('lock',300)
+STATE User lock
+LOCK_MODE MDL_SHARED
+LOCK_TYPE Stored package body metadata lock
+TABLE_NAME pkg1
+CONN 1
+INFO DO GET_LOCK('lock',300)
+STATE User lock
+LOCK_MODE MDL_SHARED
+LOCK_TYPE Stored function metadata lock
+TABLE_NAME pkg1.f1
+CONN 1
+INFO DO GET_LOCK('lock',300)
+STATE User lock
+LOCK_MODE MDL_SHARED
+LOCK_TYPE Stored procedure metadata lock
+TABLE_NAME pkg1.p1
+CONN 2
+INFO DROP PACKAGE pkg1
+STATE Waiting for stored package body metadata lock
+LOCK_MODE MDL_INTENTION_EXCLUSIVE
+LOCK_TYPE Global read lock
+TABLE_NAME
+CONN 2
+INFO DROP PACKAGE pkg1
+STATE Waiting for stored package body metadata lock
+LOCK_MODE MDL_INTENTION_EXCLUSIVE
+LOCK_TYPE Schema metadata lock
+TABLE_NAME
+DO RELEASE_LOCK('lock');
+connection conn1;
+pkg1.f1()
+1
+disconnect conn1;
+connection conn2;
+disconnect conn2;
+connection default;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-mysqldump.result b/mysql-test/suite/compat/oracle/r/sp-package-mysqldump.result
new file mode 100644
index 00000000000..1a8e01a992f
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-mysqldump.result
@@ -0,0 +1,261 @@
+SET sql_mode=ORACLE;
+CREATE PROCEDURE p1 AS
+BEGIN
+SELECT pkg1.f1(); -- a standalone routine calls a package routine
+END;
+$$
+CREATE PACKAGE pkg1 AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END;
+$$
+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;
+$$
+CALL p1;
+pkg1.f1()
+10
+CALL pkg1.p1;
+pkg1.f1()
+10
+SELECT pkg1.f1();
+pkg1.f1()
+10
+CREATE PACKAGE pkg2 AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END;
+$$
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+/*!50003 DROP PROCEDURE IF EXISTS `p1` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT' */ ;
+DELIMITER ;;
+CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
+AS
+BEGIN
+SELECT pkg1.f1(); -- a standalone routine calls a package routine
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PACKAGE IF EXISTS `pkg1` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT' */ ;
+DELIMITER ;;
+CREATE DEFINER="root"@"localhost" PACKAGE "pkg1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PACKAGE IF EXISTS `pkg2` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT' */ ;
+DELIMITER ;;
+CREATE DEFINER="root"@"localhost" PACKAGE "pkg2" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 DROP PACKAGE BODY IF EXISTS `pkg1` */;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT' */ ;
+DELIMITER ;;
+CREATE DEFINER="root"@"localhost" 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 ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+<?xml version="1.0"?>
+<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<database name="test">
+ <routines>
+ <routine Procedure="p1" sql_mode="PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT" character_set_client="latin1" collation_connection="latin1_swedish_ci" Database_Collation="latin1_swedish_ci">
+<![CDATA[
+CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
+AS
+BEGIN
+SELECT pkg1.f1(); -- a standalone routine calls a package routine
+END
+]]>
+ </routine>
+ <routine Package="pkg1" sql_mode="PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT" character_set_client="latin1" collation_connection="latin1_swedish_ci" Database_Collation="latin1_swedish_ci">
+<![CDATA[
+CREATE DEFINER="root"@"localhost" PACKAGE "pkg1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END
+]]>
+ </routine>
+ <routine Package="pkg2" sql_mode="PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT" character_set_client="latin1" collation_connection="latin1_swedish_ci" Database_Collation="latin1_swedish_ci">
+<![CDATA[
+CREATE DEFINER="root"@"localhost" PACKAGE "pkg2" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END
+]]>
+ </routine>
+ <routine Package_body="pkg1" sql_mode="PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT" character_set_client="latin1" collation_connection="latin1_swedish_ci" Database_Collation="latin1_swedish_ci">
+<![CDATA[
+CREATE DEFINER="root"@"localhost" 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
+]]>
+ </routine>
+ </routines>
+</database>
+</mysqldump>
+DROP PACKAGE pkg1;
+DROP PACKAGE pkg2;
+DROP PROCEDURE p1;
+SHOW PACKAGE STATUS;
+Db test
+Name pkg1
+Type PACKAGE
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+Db test
+Name pkg2
+Type PACKAGE
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW PACKAGE BODY STATUS;
+Db test
+Name pkg1
+Type PACKAGE BODY
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW CREATE PACKAGE pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="root"@"localhost" PACKAGE "pkg1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE pkg2;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg2 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="root"@"localhost" PACKAGE "pkg2" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE BODY pkg1;
+Package body sql_mode Create Package Body character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="root"@"localhost" PACKAGE BODY "pkg1" AS
+PROCEDURE p1 AS
+BEGIN
+CALL test.p1;
+END;
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END;
+END latin1 latin1_swedish_ci latin1_swedish_ci
+CALL p1;
+pkg1.f1()
+10
+CALL pkg1.p1;
+pkg1.f1()
+10
+SELECT pkg1.f1();
+pkg1.f1()
+10
+DROP PACKAGE pkg1;
+DROP PACKAGE pkg2;
+DROP PROCEDURE p1;
+# removing the dump file
diff --git a/mysql-test/suite/compat/oracle/r/sp-package-security.result b/mysql-test/suite/compat/oracle/r/sp-package-security.result
new file mode 100644
index 00000000000..6cb580e0c46
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package-security.result
@@ -0,0 +1,322 @@
+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;
+CURRENT_USER
+u1@localhost
+SET sql_mode=ORACLE;
+#
+# User u1 cannot drop PROCEDURE, PACKAGE, PACKAGE BODY by default
+#
+DROP PROCEDURE p1;
+ERROR 42000: alter routine command denied to user 'u1'@'localhost' for routine 'db1.p1'
+DROP PACKAGE pkg1;
+ERROR 42000: alter routine command denied to user 'u1'@'localhost' for routine 'db1.pkg1'
+DROP PACKAGE BODY pkg1;
+ERROR 42000: alter routine command denied to user 'u1'@'localhost' for routine 'db1.pkg1'
+#
+# User u1 cannot create PROCEDURE, PACKAGE, PACKAGE BODY by default
+#
+CREATE PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+$$
+ERROR 42000: Access denied for user 'u1'@'localhost' to database 'db1'
+CREATE PACKAGE pkg1 AS
+PROCEDURE p1;
+END;
+$$
+ERROR 42000: Access denied for user 'u1'@'localhost' to database 'db1'
+CREATE PACKAGE BODY pkg1 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+ERROR 42000: PACKAGE db1.pkg1 does not exist
+#
+# Now create a PACKAGE by root
+#
+connection default;
+USE db1;
+CREATE PROCEDURE p1root AS
+BEGIN
+SELECT 1;
+END;
+$$
+CREATE PACKAGE pkg1 AS
+PROCEDURE p1;
+FUNCTION f1 RETURN TEXT;
+END;
+$$
+SHOW CREATE PACKAGE pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="root"@"localhost" PACKAGE "pkg1" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN TEXT;
+END latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# u1 cannot SHOW yet:
+# - the standalone procedure earlier created by root
+# - the package specifications earlier create by root
+#
+connection conn1;
+SHOW CREATE PROCEDURE p1root;
+ERROR 42000: PROCEDURE p1root does not exist
+SHOW CREATE PACKAGE pkg1;
+ERROR 42000: PACKAGE pkg1 does not exist
+#
+# User u1 still cannot create a PACKAGE BODY
+#
+connection conn1;
+CREATE PACKAGE BODY pkg1 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+FUNCTION f1 RETURN TEXT AS BEGIN RETURN 'This is f1'; END;
+END;
+$$
+ERROR 42000: Access denied for user 'u1'@'localhost' to database 'db1'
+#
+# Now grant EXECUTE:
+# - on the standalone procedure earlier created by root
+# - on the package specification earlier created by root
+#
+connection default;
+GRANT EXECUTE ON PROCEDURE db1.p1root TO u1@localhost;
+GRANT EXECUTE ON PACKAGE db1.pkg1 TO u1@localhost;
+#
+# Now u1 can do SHOW for:
+# - the standalone procedure earlier created by root
+# - the package specification earlier created by root
+#
+disconnect conn1;
+connect conn1,localhost,u1,,db1;
+SET sql_mode=ORACLE;
+SHOW CREATE PROCEDURE db1.p1root;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+p1root PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE db1.pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# Now revoke EXECUTE and grant CREATE ROUTINE instead
+#
+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;
+#
+# Reconnect u1 to make new grants have effect
+#
+disconnect conn1;
+connect conn1,localhost,u1,,db1;
+SET sql_mode=ORACLE;
+#
+# Now u1 can SHOW:
+# - standalone routines earlier created by root
+# - package specifications earlier created by root
+#
+SHOW CREATE PROCEDURE p1root;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+p1root PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# Now u1 can CREATE, DROP and EXECUTE its own standalone procedures
+#
+CREATE PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+$$
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE "db1"."p1" TO 'u1'@'localhost'
+CALL p1;
+DROP PROCEDURE p1;
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+#
+# Now u1 can also CREATE, DROP its own package specifications
+#
+CREATE PACKAGE pkg2 AS
+PROCEDURE p1;
+FUNCTION f1 RETURN TEXT;
+END;
+$$
+SHOW CREATE PACKAGE pkg2;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg2 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="u1"@"localhost" PACKAGE "pkg2" AS
+PROCEDURE p1;
+FUNCTION f1 RETURN TEXT;
+END latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+GRANT EXECUTE, ALTER ROUTINE ON PACKAGE "db1"."pkg2" TO 'u1'@'localhost'
+DROP PACKAGE pkg2;
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+#
+# Now u1 can also CREATE, DROP package bodies and EXECUTE package body routines
+#
+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;
+$$
+SHOW CREATE PACKAGE pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE BODY pkg1;
+Package body sql_mode Create Package Body character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT CREATE DEFINER="u1"@"localhost" 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 latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+GRANT EXECUTE, ALTER ROUTINE ON PACKAGE BODY "db1"."pkg1" TO 'u1'@'localhost'
+CALL pkg1.p1;
+comment
+This is pkg1.p1
+SELECT pkg1.f1();
+pkg1.f1()
+This is pkg1.f1
+DROP PACKAGE BODY pkg1;
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+#
+# Now create a PACKAGE BODY by root.
+# u1 does not have EXECUTE access by default.
+#
+connection default;
+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;
+$$
+connection conn1;
+SHOW CREATE PACKAGE pkg1;
+Package sql_mode Create Package character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+SHOW CREATE PACKAGE BODY pkg1;
+Package body sql_mode Create Package Body character_set_client collation_connection Database Collation
+pkg1 PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT NULL latin1 latin1_swedish_ci latin1_swedish_ci
+CALL pkg1.p1;
+ERROR 42000: execute command denied to user 'u1'@'localhost' for routine 'db1.pkg1'
+SELECT pkg1.f1();
+ERROR 42000: execute command denied to user 'u1'@'localhost' for routine 'db1.pkg1'
+#
+# Now grant EXECUTE to u1 on the PACKAGE BODY created by root
+#
+connection default;
+GRANT EXECUTE ON PACKAGE BODY db1.pkg1 TO u1@localhost;
+disconnect conn1;
+connect conn1,localhost,u1,,db1;
+SELECT CURRENT_USER;
+CURRENT_USER
+u1@localhost
+SET sql_mode=ORACLE;
+SHOW GRANTS;
+Grants for u1@localhost
+GRANT USAGE ON *.* TO 'u1'@'localhost'
+GRANT SELECT, CREATE ROUTINE ON "db1".* TO 'u1'@'localhost'
+GRANT EXECUTE ON PACKAGE BODY "db1"."pkg1" TO 'u1'@'localhost'
+CALL pkg1.p1;
+comment
+This is pkg1.p1
+SELECT pkg1.f1();
+pkg1.f1()
+This is pkg1.f1
+connection default;
+DROP PACKAGE BODY pkg1;
+#
+# u1 still cannot DROP the package specification earlier created by root.
+#
+connection conn1;
+DROP PACKAGE pkg1;
+ERROR 42000: alter routine command denied to user 'u1'@'localhost' for routine 'db1.pkg1'
+#
+# Grant ALTER ROUTINE to u1
+#
+connection default;
+GRANT ALTER ROUTINE ON db1.* TO u1@localhost;
+#
+# Now u1 can DROP:
+# - the standalone procedure earlier created by root
+# - the package specification earlier created by root
+#
+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;
+#
+# Creator=root, definer=xxx
+#
+CREATE USER xxx@localhost;
+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;
+$$
+CALL p1.p1;
+ERROR 42000: execute command denied to user 'xxx'@'localhost' for routine 'test.p1'
+GRANT EXECUTE ON PACKAGE BODY test.p1 TO xxx@localhost;
+CALL p1.p1;
+SESSION_USER() CURRENT_USER() msg
+root@localhost xxx@localhost package body p1
+SESSION_USER() CURRENT_USER() msg
+root@localhost xxx@localhost p1.p1
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+#
+# Creator=root, definer=xxx, SQL SECURITY INVOKER
+#
+CREATE USER xxx@localhost;
+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;
+$$
+CALL p1.p1;
+SESSION_USER() CURRENT_USER() msg
+root@localhost root@localhost package body p1
+SESSION_USER() CURRENT_USER() msg
+root@localhost root@localhost p1.p1
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
diff --git a/mysql-test/suite/compat/oracle/r/sp-package.result b/mysql-test/suite/compat/oracle/r/sp-package.result
new file mode 100644
index 00000000000..4c8fac26d2d
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-package.result
@@ -0,0 +1,2871 @@
+SET sql_mode=ORACLE;
+#
+# Creating a body of a non-existing package
+#
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+ERROR 42000: PACKAGE test.test2 does not exist
+#
+# Dropping a non-existing package
+#
+DROP PACKAGE test2;
+ERROR 42000: PACKAGE test.test2 does not exist
+DROP PACKAGE IF EXISTS test2;
+Warnings:
+Note 1305 PACKAGE test.test2 does not exist
+DROP PACKAGE BODY test2;
+ERROR 42000: PACKAGE BODY test.test2 does not exist
+#
+# Bad combinations of OR REPLACE and IF EXISTS
+#
+CREATE OR REPLACE PACKAGE IF NOT EXISTS pkg AS
+PROCEDURE p1;
+END;
+$$
+ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
+CREATE OR REPLACE PACKAGE BODY IF NOT EXISTS pkg AS
+PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+ERROR HY000: Incorrect usage of OR REPLACE and IF NOT EXISTS
+#
+# PACKAGE and PS
+#
+PREPARE stmt FROM 'CREATE PACKAGE test2 AS FUNCTION f1 RETURN INT; END test2';
+ERROR HY000: This command is not supported in the prepared statement protocol yet
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+PREPARE stmt FROM 'CREATE PACKAGE BODY test2 AS'
+ ' FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;'
+ 'END test2';
+ERROR HY000: This command is not supported in the prepared statement protocol yet
+DROP PACKAGE test2;
+#
+# Package and READ ONLY transactions
+#
+SET SESSION TRANSACTION READ ONLY;
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END
+$$
+ERROR 25006: Cannot execute statement in a READ ONLY transaction
+SET SESSION TRANSACTION READ WRITE;
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION f2 RETURN INT;
+END;
+$$
+SET SESSION TRANSACTION READ ONLY
+$$
+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;
+$$
+ERROR 25006: Cannot execute statement in a READ ONLY transaction
+SET SESSION TRANSACTION READ WRITE;
+DROP PACKAGE test2;
+SET SESSION TRANSACTION READ ONLY;
+DROP PACKAGE test2;
+ERROR 25006: Cannot execute statement in a READ ONLY transaction
+DROP PACKAGE BODY test2;
+ERROR 25006: Cannot execute statement in a READ ONLY transaction
+SET SESSION TRANSACTION READ WRITE;
+#
+# Syntax error inside a CREATE PACKAGE, inside a routine definition
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION f2 RETURN INT;
+FUNCTION f3;
+FUNCTION f4 RETURN INT;
+END
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ';
+FUNCTION f4 RETURN INT;
+END' at line 4
+#
+# Syntax error inside a CREATE PACKAGE, outside of a routine definition
+#
+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
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AS BEGIN RETURN 10; END;
+FUNCTION f4 RETURN INT;
+END' at line 4
+#
+# Syntax error inside a CREATE PACKAGE BODY, inside a routine definition
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION f2 RETURN INT;
+END;
+$$
+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
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SA BEGIN RETURN 10; END; -- Notice "SA" vs "AS"
+END' at line 3
+DROP PACKAGE test2;
+#
+# Syntax error inside a CREATE PACKAGE BODY, outside a routine definition
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION f2 RETURN INT;
+END;
+$$
+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
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SOME SYNTAX ERROR;
+FUNCTION f2 RETURN INT AS BEGIN RETURN 10; END;
+END' at line 3
+DROP PACKAGE test2;
+#
+# Syntax error inside a CREATE PACKAGE BODY executable section
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+BEGIN
+SOME SYNTAX ERROR;
+END
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SYNTAX ERROR;
+END' at line 4
+DROP PACKAGE test2;
+#
+# CREATE PROCEDURE inside a package PROCEDURE is not allowed
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS
+BEGIN
+CREATE PROCEDURE p1 AS BEGIN NULL; END;
+END;
+END;
+$$
+ERROR 2F003: Can't create a PROCEDURE from within another stored routine
+DROP PACKAGE test2;
+#
+# CREATE PACKAGE inside a package PROCEDURE is not allowed
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS
+BEGIN
+CREATE PACKAGE p1 AS PROCEDURE p1; END;
+END;
+END;
+$$
+ERROR 2F003: Can't create a PACKAGE from within another stored routine
+DROP PACKAGE test2;
+#
+# CREATE PROCEDURE inside a package executable section is not allowed
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+CREATE PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+ERROR 2F003: Can't create a PROCEDURE from within another stored routine
+DROP PACKAGE test2;
+#
+# CREATE FUNCTION inside a package executable section is not allowed
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+CREATE FUNCTION f1 RETURN INT AS BEGIN RETURN 0; END;
+END;
+$$
+ERROR 2F003: Can't create a FUNCTION from within another stored routine
+DROP PACKAGE test2;
+#
+# CREATE PACKAGE inside a package executable section is not allowed
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+BEGIN
+CREATE PACKAGE p1 AS PROCEDURE p1; END;
+END;
+$$
+ERROR 2F003: Can't create a PACKAGE from within another stored routine
+DROP PACKAGE test2;
+#
+# Broken CREATE PACKAGE at CREATE PACKAGE BODY time
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT
+AS BEGIN
+RETURN f2();
+END;
+END;
+$$
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+DROP PACKAGE test2;
+#
+# Broken CREATE PACKAGE at a package function call time
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT
+AS BEGIN
+RETURN f2();
+END;
+END;
+$$
+SELECT test2.f1();
+ERROR 42000: FUNCTION test.f2 does not exist
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+# sp-cache-invalidate
+SELECT test2.f1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+SELECT test2.f1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+SELECT test2.f1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+DROP PACKAGE test2;
+#
+# Broken CREATE PACKAGE at a package procedure call time
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1
+AS BEGIN
+CALL p2;
+END;
+END;
+$$
+CALL test2.f1();
+ERROR 42000: PROCEDURE test2.f1 does not exist
+UPDATE mysql.proc SET `body`='garbage'
+ WHERE db='test' AND name='test2' AND type='PACKAGE';
+# sp-cache-invalidate
+CALL test2.p1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+CALL test2.p1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+CALL test2.p1();
+ERROR HY000: Failed to load routine test.test2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6)
+DROP PACKAGE test2;
+#
+# Bad routine names
+#
+CREATE PACKAGE p1 AS
+PROCEDURE pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1;
+END;
+$$
+ERROR 42000: Identifier name 'pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1' is too long
+CREATE PACKAGE p1 AS
+FUNCTION fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1
+RETURN INT;
+END;
+$$
+ERROR 42000: Identifier name 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1' is too long
+CREATE PACKAGE p1 AS
+PROCEDURE "p1 ";
+END;
+$$
+ERROR 42000: Incorrect routine name 'p1 '
+CREATE PACKAGE p1 AS
+FUNCTION "f1 " RETURN INT;
+END;
+$$
+ERROR 42000: Incorrect routine name 'f1 '
+CREATE PACKAGE p1 AS
+PROCEDURE "p1.p1";
+END;
+$$
+ERROR 42000: Incorrect routine name 'p1.p1'
+CREATE PACKAGE p1 AS
+FUNCTION "f1.f1" RETURN INT;
+END;
+$$
+ERROR 42000: Incorrect routine name 'f1.f1'
+#
+# Duplicate PROCEDURE in CREATE PACKAGE
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+PROCEDURE p1;
+END;
+$$
+ERROR 42000: PROCEDURE test2.p1 already exists
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+PROCEDURE P1;
+END;
+$$
+ERROR 42000: PROCEDURE test2.P1 already exists
+#
+# Duplicate FUNCTION in CREATE PACKAGE
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION f1 RETURN INT;
+END;
+$$
+ERROR 42000: FUNCTION test2.f1 already exists
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+FUNCTION F1 RETURN INT;
+END;
+$$
+ERROR 42000: FUNCTION test2.F1 already exists
+#
+# Duplicate PROCEDURE in CREATE PACKAGE BODY
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+ERROR 42000: PROCEDURE test2.p1 already exists
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+PROCEDURE P1 AS BEGIN NULL; END;
+END;
+$$
+ERROR 42000: PROCEDURE test2.P1 already exists
+DROP PACKAGE test2;
+#
+# Duplicate FUNCTION in CREATE PACKAGE BODY
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+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 42000: FUNCTION test2.f1 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 42000: FUNCTION test2.F1 already exists
+DROP PACKAGE test2;
+#
+# Routines declared in CREATE PACKAGE missing in CREATE PACKAGE BODY
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p2 AS BEGIN NULL; END;
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.p1' is declared in the package specification but is not defined in the package body
+DROP PACKAGE test2;
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+FUNCTION f2 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.f1' is declared in the package specification but is not defined in the package body
+DROP PACKAGE test2;
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+FUNCTION p1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.p1' is declared in the package specification but is not defined in the package body
+DROP PACKAGE test2;
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1(a INT) AS BEGIN NULL; END; -- Notice different prototype
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.p1' is declared in the package specification but is not defined in the package body
+DROP PACKAGE test2;
+#
+# Forward declarations in CREATE PACKAGE BODY with missing implementations
+#
+CREATE PACKAGE test2 AS
+PROCEDURE p1;
+END;
+$$
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS BEGIN NULL; END;
+PROCEDURE p2;
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.p2' has a forward declaration but is not defined
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1 AS BEGIN NULL; END;
+END;
+$$
+ERROR HY000: Subroutine 'test.test2.f1' has a forward declaration but is not defined
+DROP PACKAGE test2;
+#
+# Creating a new package
+#
+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
+$$
+Warnings:
+Note 1585 This function 'concat' has the same name as a native function
+SELECT * FROM mysql.proc WHERE db='test' AND name='test2';
+db test
+name test2
+type PACKAGE
+specific_name test2
+language SQL
+sql_data_access CONTAINS_SQL
+is_deterministic NO
+security_type DEFINER
+param_list
+returns
+body AS
+FUNCTION f1 RETURN INT DETERMINISTIC;
+FUNCTION f2(a INT) RETURN INT;
+FUNCTION concat RETURN INT;
+PROCEDURE p1;
+PROCEDURE p2(a INT);
+END
+definer root@localhost
+created #
+modified #
+sql_mode PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+comment package-test2-comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+db_collation latin1_swedish_ci
+body_utf8
+aggregate NONE
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME='test2';
+SPECIFIC_NAME test2
+ROUTINE_CATALOG def
+ROUTINE_SCHEMA test
+ROUTINE_NAME test2
+ROUTINE_TYPE PACKAGE
+DATA_TYPE
+CHARACTER_MAXIMUM_LENGTH NULL
+CHARACTER_OCTET_LENGTH NULL
+NUMERIC_PRECISION NULL
+NUMERIC_SCALE NULL
+DATETIME_PRECISION NULL
+CHARACTER_SET_NAME NULL
+COLLATION_NAME NULL
+DTD_IDENTIFIER NULL
+ROUTINE_BODY SQL
+ROUTINE_DEFINITION
+EXTERNAL_NAME NULL
+EXTERNAL_LANGUAGE NULL
+PARAMETER_STYLE SQL
+IS_DETERMINISTIC NO
+SQL_DATA_ACCESS CONTAINS SQL
+SQL_PATH NULL
+SECURITY_TYPE DEFINER
+CREATED #
+LAST_ALTERED #
+SQL_MODE PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+ROUTINE_COMMENT package-test2-comment
+DEFINER root@localhost
+CHARACTER_SET_CLIENT latin1
+COLLATION_CONNECTION latin1_swedish_ci
+DATABASE_COLLATION latin1_swedish_ci
+CREATE PACKAGE IF NOT EXISTS test2 AS
+FUNCTION f1 RETURN INT;
+END test2
+$$
+Warnings:
+Note 1304 PACKAGE test2 already exists
+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;
+$$
+Warnings:
+Note 1585 This function 'concat' has the same name as a native function
+Note 1585 This function 'concat' has the same name as a native function
+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;
+$$
+Warnings:
+Note 1585 This function 'concat' has the same name as a native function
+Note 1585 This function 'concat' has the same name as a native function
+Note 1304 PACKAGE BODY test2 already exists
+SELECT test2.f1();
+test2.f1()
+10
+SELECT test2.f2(1);
+test2.f2(1)
+11
+CALL test2.p1();
+f2(0)
+10
+CALL test2.p2(1);
+f2(a)
+11
+SELECT * FROM mysql.proc WHERE db='test' AND name LIKE 'test2.%';
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME='test2';
+SPECIFIC_NAME test2
+ROUTINE_CATALOG def
+ROUTINE_SCHEMA test
+ROUTINE_NAME test2
+ROUTINE_TYPE PACKAGE
+DATA_TYPE
+CHARACTER_MAXIMUM_LENGTH NULL
+CHARACTER_OCTET_LENGTH NULL
+NUMERIC_PRECISION NULL
+NUMERIC_SCALE NULL
+DATETIME_PRECISION NULL
+CHARACTER_SET_NAME NULL
+COLLATION_NAME NULL
+DTD_IDENTIFIER NULL
+ROUTINE_BODY SQL
+ROUTINE_DEFINITION
+EXTERNAL_NAME NULL
+EXTERNAL_LANGUAGE NULL
+PARAMETER_STYLE SQL
+IS_DETERMINISTIC NO
+SQL_DATA_ACCESS CONTAINS SQL
+SQL_PATH NULL
+SECURITY_TYPE DEFINER
+CREATED #
+LAST_ALTERED #
+SQL_MODE PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+ROUTINE_COMMENT package-test2-comment
+DEFINER root@localhost
+CHARACTER_SET_CLIENT latin1
+COLLATION_CONNECTION latin1_swedish_ci
+DATABASE_COLLATION latin1_swedish_ci
+SPECIFIC_NAME test2
+ROUTINE_CATALOG def
+ROUTINE_SCHEMA test
+ROUTINE_NAME test2
+ROUTINE_TYPE PACKAGE BODY
+DATA_TYPE
+CHARACTER_MAXIMUM_LENGTH NULL
+CHARACTER_OCTET_LENGTH NULL
+NUMERIC_PRECISION NULL
+NUMERIC_SCALE NULL
+DATETIME_PRECISION NULL
+CHARACTER_SET_NAME NULL
+COLLATION_NAME NULL
+DTD_IDENTIFIER NULL
+ROUTINE_BODY SQL
+ROUTINE_DEFINITION
+EXTERNAL_NAME NULL
+EXTERNAL_LANGUAGE NULL
+PARAMETER_STYLE SQL
+IS_DETERMINISTIC NO
+SQL_DATA_ACCESS CONTAINS SQL
+SQL_PATH NULL
+SECURITY_TYPE DEFINER
+CREATED #
+LAST_ALTERED #
+SQL_MODE PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+ROUTINE_COMMENT package-body-test2-comment
+DEFINER root@localhost
+CHARACTER_SET_CLIENT latin1
+COLLATION_CONNECTION latin1_swedish_ci
+DATABASE_COLLATION latin1_swedish_ci
+SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test' AND ROUTINE_NAME LIKE 'test2.%';
+SHOW PACKAGE STATUS;
+Db test
+Name test2
+Type PACKAGE
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment package-test2-comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW PACKAGE BODY STATUS;
+Db test
+Name test2
+Type PACKAGE BODY
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment package-body-test2-comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW CREATE PACKAGE test2;
+Package test2
+sql_mode PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+Create Package CREATE DEFINER="root"@"localhost" 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
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW CREATE PACKAGE BODY test2;
+Package body test2
+sql_mode PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT
+Create Package Body CREATE DEFINER="root"@"localhost" 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
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+DROP PACKAGE BODY test2;
+SELECT test2.f1();
+ERROR 42000: FUNCTION test.test2.f1 does not exist
+SELECT test2.f2();
+ERROR 42000: FUNCTION test.test2.f2 does not exist
+CALL test2.p1();
+ERROR 42000: PROCEDURE test.test2.p1 does not exist
+DROP PACKAGE BODY IF EXISTS test2;
+Warnings:
+Note 1305 PACKAGE BODY test.test2 does not exist
+DROP PACKAGE BODY test2;
+ERROR 42000: PACKAGE BODY test.test2 does not exist
+DROP PACKAGE test2;
+#
+# Creating a new package in a remote database
+#
+CREATE DATABASE test2;
+CREATE PACKAGE test2.test2 COMMENT 'package-test2-comment' AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END
+$$
+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;
+$$
+SHOW PACKAGE STATUS;
+Db test2
+Name test2
+Type PACKAGE
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment package-test2-comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+SHOW PACKAGE BODY STATUS;
+Db test2
+Name test2
+Type PACKAGE BODY
+Definer root@localhost
+Modified 0000-00-00 00:00:00
+Created 0000-00-00 00:00:00
+Security_type DEFINER
+Comment package-body-test2-comment
+character_set_client latin1
+collation_connection latin1_swedish_ci
+Database Collation latin1_swedish_ci
+USE test2;
+SELECT test2.f1();
+test2.f1()
+10
+CALL test2.p1();
+f1()
+10
+USE test;
+DROP PACKAGE BODY test2.test2;
+DROP PACKAGE test2.test2;
+DROP DATABASE test2;
+#
+# Only public routines are available outside
+#
+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;
+$$
+SELECT test2.f1();
+test2.f1()
+This is test2.f1
+CALL test2.p1();
+This is test2.p1
+This is test2.p1
+SELECT test2.f2();
+ERROR 42000: FUNCTION test2.f2 does not exist
+CALL test2.p2();
+ERROR 42000: PROCEDURE test2.p2 does not exist
+DROP PACKAGE test2;
+#
+# PACKAGE BODY with forward declarations
+#
+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;
+$$
+SELECT test2.f1();
+test2.f1()
+This is f2private
+CALL test2.p1();
+This is p2private
+This is p2private
+DROP PACKAGE test2;
+#
+# Calling private routines with forward declarations,
+# using qualified notation, e.g. "CALL pkg.proc"
+#
+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;
+$$
+SELECT test2.f1();
+test2.f1()
+This is f2private
+CALL test2.p1();
+msg
+This is p2private
+DROP PACKAGE test2;
+#
+# Calling private routines, using qualified notation, e.g. "pkg.proc"
+#
+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;
+$$
+SELECT test2.f1();
+test2.f1()
+This is f2private
+CALL test2.p1();
+msg
+This is p2private
+DROP PACKAGE test2;
+#
+# Calling private routines from the package initialization section,
+# using qualified notation, e.g. "pkg.proc"
+#
+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;
+$$
+CALL test2.p1();
+test2.f2private()
+This is f2private
+msg
+This is p2private
+msg
+This is p1
+DROP PACKAGE test2;
+#
+# Testing OR REPLACE
+#
+CREATE OR REPLACE PACKAGE pkg AS
+FUNCTION f0 RETURN INT;
+END;
+$$
+CREATE OR REPLACE PACKAGE pkg AS
+FUNCTION f1 RETURN INT;
+END;
+$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+name type body
+pkg PACKAGE AS
+FUNCTION f1 RETURN INT;
+END
+CREATE OR REPLACE PACKAGE BODY pkg AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+END;
+$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+name type body
+pkg PACKAGE AS
+FUNCTION f1 RETURN INT;
+END
+pkg PACKAGE BODY AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 10; END;
+END
+SELECT pkg.f1();
+pkg.f1()
+10
+CREATE OR REPLACE PACKAGE BODY pkg AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 20; END;
+END;
+$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+name type body
+pkg PACKAGE AS
+FUNCTION f1 RETURN INT;
+END
+pkg PACKAGE BODY AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 20; END;
+END
+SELECT pkg.f1();
+pkg.f1()
+20
+CREATE OR REPLACE PACKAGE pkg AS
+FUNCTION f1 RETURN BIGINT;
+END;
+$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+name type body
+pkg PACKAGE AS
+FUNCTION f1 RETURN BIGINT;
+END
+SELECT pkg.f1();
+ERROR 42000: FUNCTION test.pkg.f1 does not exist
+CREATE OR REPLACE PACKAGE BODY pkg AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 30; END;
+END;
+$$
+SELECT name, type, `body` FROM mysql.proc WHERE name LIKE 'pkg%' ORDER BY type;
+name type body
+pkg PACKAGE AS
+FUNCTION f1 RETURN BIGINT;
+END
+pkg PACKAGE BODY AS
+FUNCTION f1 RETURN INT AS BEGIN RETURN 30; END;
+END
+SELECT pkg.f1();
+pkg.f1()
+30
+DROP PACKAGE pkg;
+#
+# Package routines accessing tables
+#
+CREATE TABLE t1 (a INT);
+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;
+$$
+CALL test2.p1(10);
+SELECT * FROM t1;
+a
+10
+DROP PACKAGE test2;
+DROP TABLE t1;
+#
+# CREATE PACKAGE: Optional package name after the "END" keyword
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END test2.test2
+$$
+ERROR HY000: END identifier 'test2.test2' does not match 'test.test2'
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END test3
+$$
+ERROR HY000: END identifier 'test3' does not match 'test2'
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END test2
+$$
+DROP PACKAGE test2;
+#
+# MDEV-12089 sql_mode=ORACLE: Understand optional routine name after the END keyword
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN INT;
+PROCEDURE p1;
+END test2;
+$$
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END f1.f1;
+END test2;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.f1;
+END test2' at line 5
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END f2;
+END test2;
+$$
+ERROR HY000: END identifier 'f2' does not match 'f1'
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END p1.p1;
+END test2;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.p1;
+END test2' at line 5
+CREATE PACKAGE BODY test2 AS
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END p2;
+END test2;
+$$
+ERROR HY000: END identifier 'p2' does not match 'p1'
+CREATE PACKAGE BODY test2 AS
+FUNCTION f1 RETURN INT AS
+BEGIN
+RETURN 10;
+END f1;
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END p1;
+END test2;
+$$
+DROP PACKAGE test2;
+#
+# Package and package routine name and end name are case insensitive
+#
+CREATE PACKAGE test2 AS
+FUNCTION f1 RETURN TEXT;
+PROCEDURE p1;
+END TEST2;
+$$
+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;
+$$
+SELECT TEST2.F1();
+TEST2.F1()
+This is f1
+SELECT test2.f1();
+test2.f1()
+This is f1
+CALL TEST2.p1();
+msg
+This is p1
+CALL test2.P1();
+msg
+This is p1
+DROP PACKAGE BODY TEST2;
+DROP PACKAGE TEST2;
+#
+# Testing various qualified/non-qualified db/package SP call chains
+#
+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;
+$$
+SET max_sp_recursion_depth=10;
+# pack.routine -> *
+CALL pack.p1('p2');
+@track
+test.pack.p1() test.pack.p2()
+CALL pack.p1('f2');
+@track
+test.pack.p1() test.pack.f2()
+CALL pack.p1('px');
+ERROR 42000: PROCEDURE test.px does not exist
+CALL pack.p1('fx');
+ERROR 42000: FUNCTION test.fx does not exist
+CALL pack.p1('pp2');
+@track
+test.pack.p1() test.pp2()
+CALL pack.p1('ff2');
+@track
+test.pack.p1() test.ff2()
+CALL pack.p1('pack.p2');
+@track
+test.pack.p1() test.pack.p2()
+CALL pack.p1('pack.f2');
+@track
+test.pack.p1() test.pack.f2()
+CALL pack.p1('pack.px');
+ERROR 42000: PROCEDURE pack.px does not exist
+CALL pack.p1('pack.fx');
+ERROR 42000: FUNCTION pack.fx does not exist
+CALL pack.p1('test.pp2');
+@track
+test.pack.p1() test.pp2()
+CALL pack.p1('test.ff2');
+@track
+test.pack.p1() test.ff2()
+DO pack.f1('p2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2()
+DO pack.f1('f2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2()
+DO pack.p1('px');
+ERROR 42000: FUNCTION pack.p1 does not exist
+DO pack.p1('fx');
+ERROR 42000: FUNCTION pack.p1 does not exist
+DO pack.f1('pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2()
+DO pack.f1('ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2()
+DO pack.f1('pack.p2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2()
+DO pack.f1('pack.f2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2()
+SELECT pack.f1('pack.px');
+ERROR 42000: PROCEDURE pack.px does not exist
+SELECT pack.f1('pack.fx');
+ERROR 42000: FUNCTION pack.fx does not exist
+DO pack.f1('test.pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2()
+DO pack.f1('test.ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2()
+#
+# Qualified_package_routine -> Non_qualified_package_routine
+#
+# pack.routine -> [pack.]routine -> pack.routine
+CALL pack.p1('p2 pack.p3');
+@track
+test.pack.p1() test.pack.p2() test.pack.p3()
+CALL pack.p1('p2 pack.f3');
+@track
+test.pack.p1() test.pack.p2() test.pack.f3()
+CALL pack.p1('f2 pack.p3');
+@track
+test.pack.p1() test.pack.f2() test.pack.p3()
+CALL pack.p1('f2 pack.f3');
+@track
+test.pack.p1() test.pack.f2() test.pack.f3()
+DO pack.f1('p2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.p3()
+DO pack.f1('p2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.f3()
+DO pack.f1('f2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.p3()
+DO pack.f1('f2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.f3()
+# pack.routine -> [pack.]routine -> [pack]routine
+CALL pack.p1('p2 p3');
+@track
+test.pack.p1() test.pack.p2() test.pack.p3()
+CALL pack.p1('p2 f3');
+@track
+test.pack.p1() test.pack.p2() test.pack.f3()
+CALL pack.p1('f2 p3');
+@track
+test.pack.p1() test.pack.f2() test.pack.p3()
+CALL pack.p1('f2 f3');
+@track
+test.pack.p1() test.pack.f2() test.pack.f3()
+DO pack.f1('p2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.p3()
+DO pack.f1('p2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.f3()
+DO pack.f1('f2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.p3()
+DO pack.f1('f2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.f3()
+# pack.routine -> [pack.]routine -> test.routine
+CALL pack.p1('p2 test.p3');
+@track
+test.pack.p1() test.pack.p2() test.p3()
+CALL pack.p1('p2 test.f3');
+@track
+test.pack.p1() test.pack.p2() test.f3()
+CALL pack.p1('f2 test.p3');
+@track
+test.pack.p1() test.pack.f2() test.p3()
+CALL pack.p1('f2 test.f3');
+@track
+test.pack.p1() test.pack.f2() test.f3()
+DO pack.f1('p2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.p3()
+DO pack.f1('p2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.f3()
+DO pack.f1('f2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.p3()
+DO pack.f1('f2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.f3()
+# pack.routine -> [pack.]routine -> [test.]routine
+CALL pack.p1('p2 pp2');
+@track
+test.pack.p1() test.pack.p2() test.pp2()
+CALL pack.p1('p2 ff2');
+@track
+test.pack.p1() test.pack.p2() test.ff2()
+CALL pack.p1('f2 pp2');
+@track
+test.pack.p1() test.pack.f2() test.pp2()
+CALL pack.p1('f2 ff2');
+@track
+test.pack.p1() test.pack.f2() test.ff2()
+DO pack.f1('p2 pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pp2()
+DO pack.f1('p2 ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.ff2()
+DO pack.f1('f2 pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pp2()
+DO pack.f1('f2 ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.ff2()
+#
+# Qualified_package_routine -> Non_qualified_database_routine
+#
+# pack.routine -> [test.]routine -> pack.routine
+CALL pack.p1('pp2 pack.p3');
+@track
+test.pack.p1() test.pp2() test.pack.p3()
+CALL pack.p1('pp2 pack.f3');
+@track
+test.pack.p1() test.pp2() test.pack.f3()
+CALL pack.p1('ff2 pack.p3');
+@track
+test.pack.p1() test.ff2() test.pack.p3()
+CALL pack.p1('ff2 pack.f3');
+@track
+test.pack.p1() test.ff2() test.pack.f3()
+DO pack.f1('pp2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.pack.p3()
+DO pack.f1('pp2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.pack.f3()
+DO pack.f1('ff2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.pack.p3()
+DO pack.f1('ff2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.pack.f3()
+# pack.routine -> [test.]routine -> test.routine
+CALL pack.p1('pp2 test.p3');
+@track
+test.pack.p1() test.pp2() test.p3()
+CALL pack.p1('pp2 test.f3');
+@track
+test.pack.p1() test.pp2() test.f3()
+CALL pack.p1('ff2 test.p3');
+@track
+test.pack.p1() test.ff2() test.p3()
+CALL pack.p1('ff2 test.f3');
+@track
+test.pack.p1() test.ff2() test.f3()
+DO pack.f1('pp2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.p3()
+DO pack.f1('pp2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.f3()
+DO pack.f1('ff2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.p3()
+DO pack.f1('ff2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.f3()
+# pack.routine -> [test.]routine -> [test.]routine
+CALL pack.p1('pp2 p3');
+@track
+test.pack.p1() test.pp2() test.p3()
+CALL pack.p1('pp2 f3');
+@track
+test.pack.p1() test.pp2() test.f3()
+CALL pack.p1('ff2 p3');
+@track
+test.pack.p1() test.ff2() test.p3()
+CALL pack.p1('ff2 f3');
+@track
+test.pack.p1() test.ff2() test.f3()
+DO pack.f1('pp2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.p3()
+DO pack.f1('pp2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.f3()
+DO pack.f1('ff2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.p3()
+DO pack.f1('ff2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.f3()
+#
+# Qualified_package_routine -> Qualified_package_routine
+#
+# pack.routine -> pack.routine -> pack.routine
+CALL pack.p1('pack.p2 pack.p3');
+@track
+test.pack.p1() test.pack.p2() test.pack.p3()
+CALL pack.p1('pack.p2 pack.f3');
+@track
+test.pack.p1() test.pack.p2() test.pack.f3()
+CALL pack.p1('pack.f2 pack.p3');
+@track
+test.pack.p1() test.pack.f2() test.pack.p3()
+CALL pack.p1('pack.f2 pack.f3');
+@track
+test.pack.p1() test.pack.f2() test.pack.f3()
+DO pack.f1('pack.p2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.p3()
+DO pack.f1('pack.p2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.f3()
+DO pack.f1('pack.f2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.p3()
+DO pack.f1('pack.f2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.f3()
+# pack.routine -> pack.routine -> [pack.]routine
+CALL pack.p1('pack.p2 p3');
+@track
+test.pack.p1() test.pack.p2() test.pack.p3()
+CALL pack.p1('pack.p2 f3');
+@track
+test.pack.p1() test.pack.p2() test.pack.f3()
+CALL pack.p1('pack.f2 p3');
+@track
+test.pack.p1() test.pack.f2() test.pack.p3()
+CALL pack.p1('pack.f2 f3');
+@track
+test.pack.p1() test.pack.f2() test.pack.f3()
+DO pack.f1('pack.p2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.p3()
+DO pack.f1('pack.p2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pack.f3()
+DO pack.f1('pack.f2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.p3()
+DO pack.f1('pack.f2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pack.f3()
+# pack.routine -> pack.routine -> test.routine
+CALL pack.p1('pack.p2 test.p3');
+@track
+test.pack.p1() test.pack.p2() test.p3()
+CALL pack.p1('pack.p2 test.f3');
+@track
+test.pack.p1() test.pack.p2() test.f3()
+CALL pack.p1('pack.f2 test.p3');
+@track
+test.pack.p1() test.pack.f2() test.p3()
+CALL pack.p1('pack.f2 test.f3');
+@track
+test.pack.p1() test.pack.f2() test.f3()
+DO pack.f1('pack.p2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.p3()
+DO pack.f1('pack.p2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.f3()
+DO pack.f1('pack.f2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.p3()
+DO pack.f1('pack.f2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.f3()
+# pack.routine -> pack.routine -> [test.]routine
+CALL pack.p1('pack.p2 pp2');
+@track
+test.pack.p1() test.pack.p2() test.pp2()
+CALL pack.p1('pack.p2 ff2');
+@track
+test.pack.p1() test.pack.p2() test.ff2()
+CALL pack.p1('pack.f2 pp2');
+@track
+test.pack.p1() test.pack.f2() test.pp2()
+CALL pack.p1('pack.f2 ff2');
+@track
+test.pack.p1() test.pack.f2() test.ff2()
+DO pack.f1('pack.p2 pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.pp2()
+DO pack.f1('pack.p2 ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.p2() test.ff2()
+DO pack.f1('pack.f2 pp2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.pp2()
+DO pack.f1('pack.f2 ff2');
+Warnings:
+Warning 1642 test.pack.f1() test.pack.f2() test.ff2()
+#
+# Qualified_package_routine -> Qualified_database_routine
+#
+pack.routine -> test.routine -> pack.routine
+CALL pack.p1('test.pp2 pack.p3');
+@track
+test.pack.p1() test.pp2() test.pack.p3()
+CALL pack.p1('test.pp2 pack.f3');
+@track
+test.pack.p1() test.pp2() test.pack.f3()
+CALL pack.p1('test.ff2 pack.p3');
+@track
+test.pack.p1() test.ff2() test.pack.p3()
+CALL pack.p1('test.ff2 pack.f3');
+@track
+test.pack.p1() test.ff2() test.pack.f3()
+DO pack.f1('test.pp2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.pack.p3()
+DO pack.f1('test.pp2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.pack.f3()
+DO pack.f1('test.ff2 pack.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.pack.p3()
+DO pack.f1('test.ff2 pack.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.pack.f3()
+pack.routine -> test.routine -> test.routine
+CALL pack.p1('test.pp2 test.p3');
+@track
+test.pack.p1() test.pp2() test.p3()
+CALL pack.p1('test.pp2 test.f3');
+@track
+test.pack.p1() test.pp2() test.f3()
+CALL pack.p1('test.ff2 test.p3');
+@track
+test.pack.p1() test.ff2() test.p3()
+CALL pack.p1('test.ff2 test.f3');
+@track
+test.pack.p1() test.ff2() test.f3()
+DO pack.f1('test.pp2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.p3()
+DO pack.f1('test.pp2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.f3()
+DO pack.f1('test.ff2 test.p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.p3()
+DO pack.f1('test.ff2 test.f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.f3()
+pack.routine -> test.routine -> [test.]routine
+CALL pack.p1('test.pp2 p3');
+@track
+test.pack.p1() test.pp2() test.p3()
+CALL pack.p1('test.pp2 f3');
+@track
+test.pack.p1() test.pp2() test.f3()
+CALL pack.p1('test.ff2 p3');
+@track
+test.pack.p1() test.ff2() test.p3()
+CALL pack.p1('test.ff2 f3');
+@track
+test.pack.p1() test.ff2() test.f3()
+DO pack.f1('test.pp2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.p3()
+DO pack.f1('test.pp2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.pp2() test.f3()
+DO pack.f1('test.ff2 p3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.p3()
+DO pack.f1('test.ff2 f3');
+Warnings:
+Warning 1642 test.pack.f1() test.ff2() test.f3()
+# Longer chains
+CALL pack.p1('p2 f2 p2 test.pp2 test.ff2 pack.p3');
+@track
+test.pack.p1() test.pack.p2() test.pack.f2() test.pack.p2() test.pp2() test.ff2() test.pack.p3()
+CALL pack.p1('p2 test.pp2 pack.p2 pack.f2 test.ff2 pack.p3');
+@track
+test.pack.p1() test.pack.p2() test.pp2() test.pack.p2() test.pack.f2() test.ff2() test.pack.p3()
+DROP PACKAGE pack;
+DROP FUNCTION f3;
+DROP PROCEDURE p3;
+DROP FUNCTION ff2;
+DROP PROCEDURE pp2;
+#
+# Calling a standalone function from a non-current database,
+# which calls a package routine from the same non-current database.
+#
+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;
+$$
+CALL p1;
+database()
+test
+CREATE DATABASE test2;
+USE test2;
+CALL test.p1;
+database()
+test
+DROP DATABASE test2;
+CALL test.p1;
+database()
+test
+USE test;
+DROP PACKAGE pkg1;
+DROP PROCEDURE p1;
+#
+# Creating a package with a different DEFINER
+#
+CREATE USER xxx@localhost;
+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;
+$$
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 DEFINER PACKAGE
+xxx@localhost p1 DEFINER PACKAGE BODY
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+#
+# Creating a package with a different DEFINER, with SQL SECURITY INVOKER
+#
+CREATE USER xxx@localhost;
+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;
+$$
+SELECT definer, name, security_type, type FROM mysql.proc WHERE name LIKE 'p1%' ORDER BY definer, name, type;
+definer name security_type type
+xxx@localhost p1 INVOKER PACKAGE
+xxx@localhost p1 INVOKER PACKAGE BODY
+DROP PACKAGE p1;
+DROP USER xxx@localhost;
+#
+# A package with an initialization section
+#
+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;
+$$
+CALL p1.p1();
+@a
+11
+CALL p1.p1();
+@a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+@a
+12
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+@a
+14
+DROP PACKAGE p1;
+#
+# A package with an initialization section calling
+# routines from the same package, and standalone routines.
+#
+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;
+$$
+CALL p1.p1();
+@msg
+[p1.init1][p1.init2][p1.init3][init20][p1.p1]
+CALL p1.p1();
+@msg
+[p1.init1][p1.init2][p1.init3][init20][p1.p1][p1.p1]
+SELECT p1.f1();
+p1.f1()
+[p1.init1][p1.init2][p1.init3][init20][p1.p1][p1.p1][p1.f1]
+SELECT p1.f1();
+p1.f1()
+[p1.init1][p1.init2][p1.init3][init20][p1.p1][p1.p1][p1.f1][p1.f1]
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+[p1.init1][p1.init2][p1.init3][init20][p1.f1]
+CALL p1.p1();
+@msg
+[p1.init1][p1.init2][p1.init3][init20][p1.f1][p1.p1]
+SELECT p1.f1();
+p1.f1()
+[p1.init1][p1.init2][p1.init3][init20][p1.f1][p1.p1][p1.f1]
+CALL p1.p1();
+@msg
+[p1.init1][p1.init2][p1.init3][init20][p1.f1][p1.p1][p1.f1][p1.p1]
+DROP PACKAGE p1;
+DROP PROCEDURE init20;
+#
+# EXECUTE IMMEDIATE in the package initialization section
+#
+SET @a=1000;
+CREATE TABLE t1 AS SELECT 10 AS a;
+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;
+$$
+CALL p1.p1();
+@a
+11
+CALL p1.p1();
+@a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# A package with an initialization section, loading table data into a user variable
+#
+SET @a=1000;
+CREATE TABLE t1 AS SELECT 10 AS a;
+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;
+$$
+CALL p1.p1();
+@a
+11
+CALL p1.p1();
+@a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# A package with an initialization section producing an error
+#
+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;
+$$
+CALL p1.p1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+SELECT p1.f1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+# sp-cache-invalidate
+SELECT p1.f1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+CALL p1.p1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+SELECT p1.f1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+CREATE TABLE t1 (a INT) AS SELECT 1;
+CALL p1.p1();
+msg
+This is p1
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+This is f1
+# sp-cache-invalidate
+CALL p1.p1();
+msg
+This is p1
+DROP TABLE t1;
+DROP PACKAGE p1;
+#
+# A package with SF-unsafe statements in the initialization section
+#
+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;
+$$
+CALL p1.p1();
+msg
+This is p1
+SELECT p1.f1();
+p1.f1()
+This is f1
+# sp-cache-invalidate
+SELECT p1.f1();
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger
+CALL p1.p1();
+msg
+This is p1
+SELECT p1.f1();
+p1.f1()
+This is f1
+DROP PACKAGE p1;
+#
+# MDEV-13139 Package-wide variables in CREATE PACKAGE
+#
+CREATE PACKAGE p1 AS
+PROCEDURE p1;
+FUNCTION f1 RETURN INT;
+END;
+$$
+CREATE PACKAGE BODY p1 AS
+a INT;
+a INT;
+PROCEDURE p1 AS
+BEGIN
+CREATE VIEW v1 AS SELECT a;
+END;
+END;
+$$
+ERROR 42000: Duplicate variable: a
+CREATE PACKAGE BODY p1 AS
+a INT;
+PROCEDURE p1 AS
+BEGIN
+NULL;
+END;
+b INT; -- Variables cannot go after routine definitions
+END;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'b INT; -- Variables cannot go after routine definitions
+END' at line 7
+CREATE PACKAGE BODY p1 AS
+a INT;
+PROCEDURE p1 AS
+BEGIN
+CREATE VIEW v1 AS SELECT a;
+END;
+END;
+$$
+ERROR HY000: View's SELECT contains a variable or parameter
+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;
+$$
+CALL p1.p1;
+a
+NULL
+CALL p1.p1;
+a
+101
+CALL p1.p1;
+a
+202
+SELECT p1.f1();
+p1.f1()
+303
+DROP PACKAGE p1;
+#
+# One package variable with a default value
+#
+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;
+$$
+CALL p1.p1();
+a
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+a
+14
+DROP PACKAGE p1;
+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;
+$$
+CALL p1.p1();
+a.a a.b
+11 bbbB
+CALL p1.p1();
+a.a a.b
+12 bbbBB
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+a.a a.b
+12 bbbB
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+a.a a.b
+14 bbbBB
+DROP PACKAGE p1;
+CREATE TABLE t1 (a INT);
+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;
+$$
+CALL p1.p1();
+a
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+a
+14
+DROP PACKAGE p1;
+DROP TABLE t1;
+CREATE TABLE t1 (a INT, b TEXT);
+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;
+$$
+CALL p1.p1();
+a.a a.b
+11 bbbB
+CALL p1.p1();
+a.a a.b
+12 bbbBB
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+a.a a.b
+12 bbbB
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+a.a a.b
+14 bbbBB
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# One package variable, set in the package initialization section
+#
+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;
+$$
+CALL p1.p1();
+a
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+CALL p1.p1();
+a
+14
+DROP PACKAGE p1;
+#
+# A package with an initialization section,
+# loading table data into a package variable
+#
+CREATE TABLE t1 AS SELECT 10 AS a;
+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;
+$$
+CALL p1.p1();
+a
+11
+CALL p1.p1();
+a
+12
+SELECT p1.f1();
+p1.f1()
+13
+SELECT p1.f1();
+p1.f1()
+14
+# sp-cache-invalidate
+SELECT p1.f1();
+p1.f1()
+11
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# Package variables and XPath
+#
+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;
+$$
+SELECT p1.f1();
+p1.f1()
+b1
+SELECT p1.f1();
+p1.f1()
+b2
+SELECT p1.f1();
+p1.f1()
+b3
+DROP PACKAGE p1;
+#
+# Package variables as OUT routine parameter
+#
+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;
+$$
+CALL p1.p1;
+a b
+10 11
+DROP PACKAGE p1;
+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;
+$$
+CALL p1.p1;
+a.a a.b b.a b.b
+101 bX 102 bBX
+DROP PACKAGE p1;
+CREATE TABLE t1 (a INT,b TEXT);
+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;
+$$
+CALL p1.p1;
+a.a a.b b.a b.b
+101 bX 102 bBX
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# Package variable fields as OUT routine parameters
+#
+CREATE TABLE t1 (a INT,b TEXT);
+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;
+$$
+CALL p1.p1;
+a.a a.b
+10 b
+a.a a.b
+11 bB
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# Package variables as SELECT INTO targets
+#
+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;
+$$
+CALL p1.p1;
+a b
+1 2
+DROP PACKAGE p1;
+CREATE TABLE t1 (a INT, b TEXT);
+INSERT INTO t1 VALUES (10,'b');
+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;
+$$
+CALL p1.p1;
+b.a b.b
+10 b
+a.a a.b
+10 b
+DROP PACKAGE p1;
+DROP TABLE t1;
+#
+# Package variable fields as SELECT INTO targets
+#
+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;
+$$
+CALL p1.p1;
+a.a a.b b.a b.b
+10 x1 20 x2
+DROP PACKAGE p1;
+#
+# Recursive package procedure calls
+# Makes sure that the non-top sp_head instances created by
+# sp_clone_and_link_routine() correctly reproduce the package context:
+# package variables, package routines.
+#
+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;
+$$
+SET max_sp_recursion_depth=5;
+CALL p1.p1(5);
+c pv1 f1()
+5 10 110
+c pv1 f1()
+4 11 111
+c pv1 f1()
+3 12 112
+c pv1 f1()
+2 13 113
+c pv1 f1()
+1 14 114
+c pv1 f1()
+0 15 115
+SET max_sp_recursion_depth=0;
+CALL p1.p1(0);
+c pv1 f1()
+0 15 115
+CALL p1.p1(1);
+c pv1 f1()
+1 15 115
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1.p1
+DROP PACKAGE p1;
+#
+# Non-reserved keywords as package body variable names
+#
+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;
+$$
+CALL p1.p1;
+ascii action
+11 21
+DROP PACKAGE p1;
+#
+# Package routines calling routines of another package
+#
+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;
+$$
+CALL p1.p1;
+msg
+This is p1.p1
+CALL p2.p1;
+msg
+This is p1.p1
+SELECT p1.f1(), p2.f1();
+p1.f1() p2.f1()
+This is p1.f1 This is p1.f1
+DROP PACKAGE p2;
+DROP PACKAGE p1;
+#
+# Package names with dot characters
+#
+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;
+$$
+CALL "p1.p1"."p1";
+msg
+This is p1
+SELECT "p1.p1"."f1"();
+"p1.p1"."f1"()
+This is f1
+DROP PACKAGE "p1.p1";
+#
+# MDEV-15070 Crash when doing a CREATE VIEW inside a package routine
+#
+SET sql_mode=ORACLE;
+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;
+$$
+CALL pkg1.p00;
+msg
+This is p01
+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;
+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;
+$$
+CALL pkg1.p00;
+msg
+This is p01
+DROP PACKAGE pkg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result
index f0a7d2808f1..7f042825385 100644
--- a/mysql-test/suite/compat/oracle/r/sp.result
+++ b/mysql-test/suite/compat/oracle/r/sp.result
@@ -166,6 +166,13 @@ IF a=10 THEN NULL; ELSE NULL; END IF;
END;
/
DROP PROCEDURE p1;
+# 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;
+function
+10
+DROP TABLE function;
# Testing that (some) keyword_sp are allowed in Oracle-style assignments
CREATE PROCEDURE p1 (action OUT INT) AS BEGIN action:=10; END;/
DROP PROCEDURE p1/
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 00000000000..577ff58d5ae
--- /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/rpl_sp_package.test b/mysql-test/suite/compat/oracle/t/rpl_sp_package.test
new file mode 100644
index 00000000000..40bb0b0d9cd
--- /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 00000000000..fca611243ad
--- /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/sp-cache-invalidate.inc b/mysql-test/suite/compat/oracle/t/sp-cache-invalidate.inc
new file mode 100644
index 00000000000..945af6e7129
--- /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-package-code.test b/mysql-test/suite/compat/oracle/t/sp-package-code.test
new file mode 100644
index 00000000000..9cca53964ec
--- /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 00000000000..0528e6cbfcd
--- /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 00000000000..0f1a0ef3975
--- /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 00000000000..09ba1d70142
--- /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 00000000000..d2c2041a353
--- /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 00000000000..8ee96d1ee6a
--- /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-innodb.test b/mysql-test/suite/compat/oracle/t/sp-package-innodb.test
new file mode 100644
index 00000000000..f4cd05b7112
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-innodb.test
@@ -0,0 +1,62 @@
+-- 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');
+
+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;
+
+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 00000000000..de4f7aaabad
--- /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 00000000000..8f50c1fc4b0
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-mysqldump.test
@@ -0,0 +1,93 @@
+--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 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 00000000000..e8c852ef452
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package-security.test
@@ -0,0 +1,331 @@
+--source include/not_embedded.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 00000000000..e61dd37467c
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-package.test
@@ -0,0 +1,2626 @@
+
+SET sql_mode=ORACLE;
+
+
+--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 #
+
+--error ER_UNSUPPORTED_PS
+PREPARE stmt FROM 'CREATE PACKAGE test2 AS FUNCTION f1 RETURN INT; END test2';
+
+DELIMITER $$;
+CREATE PACKAGE test2 AS
+ FUNCTION f1 RETURN INT;
+END;
+$$
+DELIMITER ;$$
+--error ER_UNSUPPORTED_PS
+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 ;$$
+
+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();
+--error ER_SP_PROC_TABLE_CORRUPT
+SELECT test2.f1();
+--error ER_SP_PROC_TABLE_CORRUPT
+SELECT test2.f1();
+
+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();
+--error ER_SP_PROC_TABLE_CORRUPT
+CALL test2.p1();
+--error ER_SP_PROC_TABLE_CORRUPT
+CALL test2.p1();
+
+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;
diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test
index 693569d184c..b88271ad3e2 100644
--- a/mysql-test/suite/compat/oracle/t/sp.test
+++ b/mysql-test/suite/compat/oracle/t/sp.test
@@ -170,6 +170,11 @@ 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 /;
diff --git a/mysql-test/suite/funcs_1/r/is_columns_is.result b/mysql-test/suite/funcs_1/r/is_columns_is.result
index bf6511e80bf..d76efef3e08 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_is.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_is.result
@@ -308,7 +308,7 @@ def information_schema ROUTINES ROUTINE_COMMENT 27 '' NO longtext 4294967295 429
def information_schema ROUTINES ROUTINE_DEFINITION 16 NULL YES longtext 4294967295 4294967295 NULL NULL NULL utf8 utf8_general_ci longtext select NEVER NULL
def information_schema ROUTINES ROUTINE_NAME 4 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NEVER NULL
def information_schema ROUTINES ROUTINE_SCHEMA 3 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NEVER NULL
-def information_schema ROUTINES ROUTINE_TYPE 5 '' NO varchar 9 27 NULL NULL NULL utf8 utf8_general_ci varchar(9) select NEVER NULL
+def information_schema ROUTINES ROUTINE_TYPE 5 '' NO varchar 13 39 NULL NULL NULL utf8 utf8_general_ci varchar(13) select NEVER NULL
def information_schema ROUTINES SECURITY_TYPE 23 '' NO varchar 7 21 NULL NULL NULL utf8 utf8_general_ci varchar(7) select NEVER NULL
def information_schema ROUTINES SPECIFIC_NAME 1 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NEVER NULL
def information_schema ROUTINES SQL_DATA_ACCESS 21 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NEVER NULL
@@ -820,7 +820,7 @@ NULL information_schema PROCESSLIST TID bigint NULL NULL NULL NULL bigint(4)
3.0000 information_schema ROUTINES ROUTINE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema ROUTINES ROUTINE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ROUTINES ROUTINE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
-3.0000 information_schema ROUTINES ROUTINE_TYPE varchar 9 27 utf8 utf8_general_ci varchar(9)
+3.0000 information_schema ROUTINES ROUTINE_TYPE varchar 13 39 utf8 utf8_general_ci varchar(13)
3.0000 information_schema ROUTINES DATA_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
NULL information_schema ROUTINES CHARACTER_MAXIMUM_LENGTH int NULL NULL NULL NULL int(21)
NULL information_schema ROUTINES CHARACTER_OCTET_LENGTH int NULL NULL NULL NULL int(21)
diff --git a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
index edf11d25f7e..b0d3c177580 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
@@ -308,7 +308,7 @@ def information_schema ROUTINES ROUTINE_COMMENT 27 '' NO longtext 4294967295 429
def information_schema ROUTINES ROUTINE_DEFINITION 16 NULL YES longtext 4294967295 4294967295 NULL NULL NULL utf8 utf8_general_ci longtext NEVER NULL
def information_schema ROUTINES ROUTINE_NAME 4 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) NEVER NULL
def information_schema ROUTINES ROUTINE_SCHEMA 3 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) NEVER NULL
-def information_schema ROUTINES ROUTINE_TYPE 5 '' NO varchar 9 27 NULL NULL NULL utf8 utf8_general_ci varchar(9) NEVER NULL
+def information_schema ROUTINES ROUTINE_TYPE 5 '' NO varchar 13 39 NULL NULL NULL utf8 utf8_general_ci varchar(13) NEVER NULL
def information_schema ROUTINES SECURITY_TYPE 23 '' NO varchar 7 21 NULL NULL NULL utf8 utf8_general_ci varchar(7) NEVER NULL
def information_schema ROUTINES SPECIFIC_NAME 1 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) NEVER NULL
def information_schema ROUTINES SQL_DATA_ACCESS 21 '' NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) NEVER NULL
@@ -820,7 +820,7 @@ NULL information_schema PROCESSLIST TID bigint NULL NULL NULL NULL bigint(4)
3.0000 information_schema ROUTINES ROUTINE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema ROUTINES ROUTINE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ROUTINES ROUTINE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
-3.0000 information_schema ROUTINES ROUTINE_TYPE varchar 9 27 utf8 utf8_general_ci varchar(9)
+3.0000 information_schema ROUTINES ROUTINE_TYPE varchar 13 39 utf8 utf8_general_ci varchar(13)
3.0000 information_schema ROUTINES DATA_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
NULL information_schema ROUTINES CHARACTER_MAXIMUM_LENGTH int NULL NULL NULL NULL int(21)
NULL information_schema ROUTINES CHARACTER_OCTET_LENGTH int NULL NULL NULL NULL int(21)
diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
index 74dff5a46ad..5d4e8fdf19d 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
@@ -154,13 +154,13 @@ def mysql proc security_type 8 'DEFINER' NO enum 7 21 NULL NULL NULL utf8 utf8_g
def mysql proc specific_name 4 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references NEVER NULL
def mysql proc sql_data_access 6 'CONTAINS_SQL' NO enum 17 51 NULL NULL NULL utf8 utf8_general_ci enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') select,insert,update,references NEVER NULL
def mysql proc sql_mode 15 '' NO set 539 1617 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') select,insert,update,references NEVER NULL
-def mysql proc type 3 NULL NO enum 9 27 NULL NULL NULL utf8 utf8_general_ci enum('FUNCTION','PROCEDURE') PRI select,insert,update,references NEVER NULL
+def mysql proc type 3 NULL NO enum 12 36 NULL NULL NULL utf8 utf8_general_ci enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') PRI select,insert,update,references NEVER NULL
def mysql procs_priv Db 2 '' NO char 64 192 NULL NULL NULL utf8 utf8_bin char(64) PRI select,insert,update,references NEVER NULL
def mysql procs_priv Grantor 6 '' NO char 141 423 NULL NULL NULL utf8 utf8_bin char(141) MUL select,insert,update,references NEVER NULL
def mysql procs_priv Host 1 '' NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references NEVER NULL
def mysql procs_priv Proc_priv 7 '' NO set 27 81 NULL NULL NULL utf8 utf8_general_ci set('Execute','Alter Routine','Grant') select,insert,update,references NEVER NULL
def mysql procs_priv Routine_name 4 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) PRI select,insert,update,references NEVER NULL
-def mysql procs_priv Routine_type 5 NULL NO enum 9 27 NULL NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE') PRI select,insert,update,references NEVER NULL
+def mysql procs_priv Routine_type 5 NULL NO enum 12 36 NULL NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') PRI select,insert,update,references NEVER NULL
def mysql procs_priv Timestamp 8 current_timestamp() NO timestamp NULL NULL NULL NULL 0 NULL NULL timestamp on update current_timestamp() select,insert,update,references NEVER NULL
def mysql procs_priv User 3 '' NO char 80 240 NULL NULL NULL utf8 utf8_bin char(80) PRI select,insert,update,references NEVER NULL
def mysql proxies_priv Grantor 6 '' NO char 141 423 NULL NULL NULL utf8 utf8_bin char(141) MUL select,insert,update,references NEVER NULL
@@ -479,7 +479,7 @@ NULL mysql innodb_table_stats sum_of_other_index_sizes bigint NULL NULL NULL NUL
3.0000 mysql plugin dl varchar 128 384 utf8 utf8_general_ci varchar(128)
3.0000 mysql proc db char 64 192 utf8 utf8_bin char(64)
3.0000 mysql proc name char 64 192 utf8 utf8_general_ci char(64)
-3.0000 mysql proc type enum 9 27 utf8 utf8_general_ci enum('FUNCTION','PROCEDURE')
+3.0000 mysql proc type enum 12 36 utf8 utf8_general_ci enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
3.0000 mysql proc specific_name char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql proc language enum 3 9 utf8 utf8_general_ci enum('SQL')
3.0000 mysql proc sql_data_access enum 17 51 utf8 utf8_general_ci enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')
@@ -502,7 +502,7 @@ NULL mysql proc modified timestamp NULL NULL NULL NULL timestamp
3.0000 mysql procs_priv Db char 64 192 utf8 utf8_bin char(64)
3.0000 mysql procs_priv User char 80 240 utf8 utf8_bin char(80)
3.0000 mysql procs_priv Routine_name char 64 192 utf8 utf8_general_ci char(64)
-3.0000 mysql procs_priv Routine_type enum 9 27 utf8 utf8_bin enum('FUNCTION','PROCEDURE')
+3.0000 mysql procs_priv Routine_type enum 12 36 utf8 utf8_bin enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
3.0000 mysql procs_priv Grantor char 141 423 utf8 utf8_bin char(141)
3.0000 mysql procs_priv Proc_priv set 27 81 utf8 utf8_general_ci set('Execute','Alter Routine','Grant')
NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp
diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
index 435a28728fe..9f17724b356 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
@@ -140,13 +140,13 @@ def mysql proc security_type 8 'DEFINER' NO enum 7 21 NULL NULL NULL utf8 utf8_g
def mysql proc specific_name 4 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) NEVER NULL
def mysql proc sql_data_access 6 'CONTAINS_SQL' NO enum 17 51 NULL NULL NULL utf8 utf8_general_ci enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NEVER NULL
def mysql proc sql_mode 15 '' NO set 539 1617 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NEVER NULL
-def mysql proc type 3 NULL NO enum 9 27 NULL NULL NULL utf8 utf8_general_ci enum('FUNCTION','PROCEDURE') PRI NEVER NULL
+def mysql proc type 3 NULL NO enum 12 36 NULL NULL NULL utf8 utf8_general_ci enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') PRI NEVER NULL
def mysql procs_priv Db 2 '' NO char 64 192 NULL NULL NULL utf8 utf8_bin char(64) PRI NEVER NULL
def mysql procs_priv Grantor 6 '' NO char 141 423 NULL NULL NULL utf8 utf8_bin char(141) MUL NEVER NULL
def mysql procs_priv Host 1 '' NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI NEVER NULL
def mysql procs_priv Proc_priv 7 '' NO set 27 81 NULL NULL NULL utf8 utf8_general_ci set('Execute','Alter Routine','Grant') NEVER NULL
def mysql procs_priv Routine_name 4 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) PRI NEVER NULL
-def mysql procs_priv Routine_type 5 NULL NO enum 9 27 NULL NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE') PRI NEVER NULL
+def mysql procs_priv Routine_type 5 NULL NO enum 12 36 NULL NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') PRI NEVER NULL
def mysql procs_priv Timestamp 8 current_timestamp() NO timestamp NULL NULL NULL NULL 0 NULL NULL timestamp on update current_timestamp() NEVER NULL
def mysql procs_priv User 3 '' NO char 80 240 NULL NULL NULL utf8 utf8_bin char(80) PRI NEVER NULL
def mysql proxies_priv Grantor 6 '' NO char 141 423 NULL NULL NULL utf8 utf8_bin char(141) MUL NEVER NULL
@@ -462,7 +462,7 @@ NULL mysql index_stats avg_frequency decimal NULL NULL NULL NULL decimal(12,4)
3.0000 mysql plugin dl varchar 128 384 utf8 utf8_general_ci varchar(128)
3.0000 mysql proc db char 64 192 utf8 utf8_bin char(64)
3.0000 mysql proc name char 64 192 utf8 utf8_general_ci char(64)
-3.0000 mysql proc type enum 9 27 utf8 utf8_general_ci enum('FUNCTION','PROCEDURE')
+3.0000 mysql proc type enum 12 36 utf8 utf8_general_ci enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
3.0000 mysql proc specific_name char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql proc language enum 3 9 utf8 utf8_general_ci enum('SQL')
3.0000 mysql proc sql_data_access enum 17 51 utf8 utf8_general_ci enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')
@@ -485,7 +485,7 @@ NULL mysql proc modified timestamp NULL NULL NULL NULL timestamp
3.0000 mysql procs_priv Db char 64 192 utf8 utf8_bin char(64)
3.0000 mysql procs_priv User char 80 240 utf8 utf8_bin char(80)
3.0000 mysql procs_priv Routine_name char 64 192 utf8 utf8_general_ci char(64)
-3.0000 mysql procs_priv Routine_type enum 9 27 utf8 utf8_bin enum('FUNCTION','PROCEDURE')
+3.0000 mysql procs_priv Routine_type enum 12 36 utf8 utf8_bin enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY')
3.0000 mysql procs_priv Grantor char 141 423 utf8 utf8_bin char(141)
3.0000 mysql procs_priv Proc_priv set 27 81 utf8 utf8_general_ci set('Execute','Alter Routine','Grant')
NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp
diff --git a/mysql-test/suite/funcs_1/r/is_routines.result b/mysql-test/suite/funcs_1/r/is_routines.result
index aadf89d89f5..5f8e965de84 100644
--- a/mysql-test/suite/funcs_1/r/is_routines.result
+++ b/mysql-test/suite/funcs_1/r/is_routines.result
@@ -33,7 +33,7 @@ SPECIFIC_NAME varchar(64) NO
ROUTINE_CATALOG varchar(512) NO
ROUTINE_SCHEMA varchar(64) NO
ROUTINE_NAME varchar(64) NO
-ROUTINE_TYPE varchar(9) NO
+ROUTINE_TYPE varchar(13) NO
DATA_TYPE varchar(64) NO
CHARACTER_MAXIMUM_LENGTH int(21) YES NULL
CHARACTER_OCTET_LENGTH int(21) YES NULL
@@ -67,7 +67,7 @@ ROUTINES CREATE TEMPORARY TABLE `ROUTINES` (
`ROUTINE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`ROUTINE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`ROUTINE_NAME` varchar(64) NOT NULL DEFAULT '',
- `ROUTINE_TYPE` varchar(9) NOT NULL DEFAULT '',
+ `ROUTINE_TYPE` varchar(13) NOT NULL DEFAULT '',
`DATA_TYPE` varchar(64) NOT NULL DEFAULT '',
`CHARACTER_MAXIMUM_LENGTH` int(21) DEFAULT NULL,
`CHARACTER_OCTET_LENGTH` int(21) DEFAULT NULL,
@@ -101,7 +101,7 @@ SPECIFIC_NAME varchar(64) NO
ROUTINE_CATALOG varchar(512) NO
ROUTINE_SCHEMA varchar(64) NO
ROUTINE_NAME varchar(64) NO
-ROUTINE_TYPE varchar(9) NO
+ROUTINE_TYPE varchar(13) NO
DATA_TYPE varchar(64) NO
CHARACTER_MAXIMUM_LENGTH int(21) YES NULL
CHARACTER_OCTET_LENGTH int(21) YES NULL
diff --git a/mysql-test/suite/funcs_1/r/is_routines_embedded.result b/mysql-test/suite/funcs_1/r/is_routines_embedded.result
index 8879efb21a5..a2acd83af49 100644
--- a/mysql-test/suite/funcs_1/r/is_routines_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_routines_embedded.result
@@ -33,7 +33,7 @@ SPECIFIC_NAME varchar(64) NO
ROUTINE_CATALOG varchar(512) NO
ROUTINE_SCHEMA varchar(64) NO
ROUTINE_NAME varchar(64) NO
-ROUTINE_TYPE varchar(9) NO
+ROUTINE_TYPE varchar(13) NO
DATA_TYPE varchar(64) NO
CHARACTER_MAXIMUM_LENGTH int(21) YES NULL
CHARACTER_OCTET_LENGTH int(21) YES NULL
@@ -67,7 +67,7 @@ ROUTINES CREATE TEMPORARY TABLE `ROUTINES` (
`ROUTINE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`ROUTINE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`ROUTINE_NAME` varchar(64) NOT NULL DEFAULT '',
- `ROUTINE_TYPE` varchar(9) NOT NULL DEFAULT '',
+ `ROUTINE_TYPE` varchar(13) NOT NULL DEFAULT '',
`DATA_TYPE` varchar(64) NOT NULL DEFAULT '',
`CHARACTER_MAXIMUM_LENGTH` int(21) DEFAULT NULL,
`CHARACTER_OCTET_LENGTH` int(21) DEFAULT NULL,
@@ -101,7 +101,7 @@ SPECIFIC_NAME varchar(64) NO
ROUTINE_CATALOG varchar(512) NO
ROUTINE_SCHEMA varchar(64) NO
ROUTINE_NAME varchar(64) NO
-ROUTINE_TYPE varchar(9) NO
+ROUTINE_TYPE varchar(13) NO
DATA_TYPE varchar(64) NO
CHARACTER_MAXIMUM_LENGTH int(21) YES NULL
CHARACTER_OCTET_LENGTH int(21) YES NULL
diff --git a/mysql-test/suite/roles/acl_statistics.result b/mysql-test/suite/roles/acl_statistics.result
index bf74cbf2e85..46e48b86936 100644
--- a/mysql-test/suite/roles/acl_statistics.result
+++ b/mysql-test/suite/roles/acl_statistics.result
@@ -4,6 +4,8 @@ Acl_column_grants 0
Acl_database_grants 2
Acl_function_grants 0
Acl_procedure_grants 0
+Acl_package_spec_grants 0
+Acl_package_body_grants 0
Acl_proxy_users 2
Acl_role_grants 0
Acl_roles 0
@@ -67,6 +69,8 @@ Acl_column_grants 2
Acl_database_grants 4
Acl_function_grants 3
Acl_procedure_grants 2
+Acl_package_spec_grants 0
+Acl_package_body_grants 0
Acl_proxy_users 3
Acl_role_grants 4
Acl_roles 2
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
index 09ac37198f0..c1dcc26e262 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
@@ -3024,9 +3024,9 @@ READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME PERFORMANCE_SCHEMA_MAX_STATEMENT_CLASSES
SESSION_VALUE NULL
-GLOBAL_VALUE 191
+GLOBAL_VALUE 200
GLOBAL_VALUE_ORIGIN COMPILE-TIME
-DEFAULT_VALUE 191
+DEFAULT_VALUE 200
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT Maximum number of statement instruments.
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
index 63f50e34ed3..e991e0c72b4 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
@@ -3234,9 +3234,9 @@ READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME PERFORMANCE_SCHEMA_MAX_STATEMENT_CLASSES
SESSION_VALUE NULL
-GLOBAL_VALUE 191
+GLOBAL_VALUE 200
GLOBAL_VALUE_ORIGIN COMPILE-TIME
-DEFAULT_VALUE 191
+DEFAULT_VALUE 200
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT Maximum number of statement instruments.
diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result
index decf22d4118..d44ded30218 100644
--- a/mysql-test/suite/versioning/r/partition.result
+++ b/mysql-test/suite/versioning/r/partition.result
@@ -90,7 +90,7 @@ ERROR HY000: Wrong partitions for `t1`: must have at least one HISTORY and exact
alter table t1 add partition (
partition p1 history);
Warnings:
-Warning 4113 Maybe missing parameters: no rotation condition for multiple HISTORY partitions.
+Warning 4115 Maybe missing parameters: no rotation condition for multiple HISTORY partitions.
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -244,11 +244,11 @@ x
6
insert into t1 values (7), (8);
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
### warn about full partition
delete from t1;
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
select * from t1 partition (p1) order by x;
x
4
@@ -303,7 +303,7 @@ x
delete from t1 where x < 3;
delete from t1;
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
select * from t1 partition (p0) order by x;
x
1
@@ -323,7 +323,7 @@ insert into t1 values (1);
update t1 set x= 2;
update t1 set x= 3;
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
select * from t1 partition (p0);
x
1
@@ -498,10 +498,10 @@ insert t1 values (1);
delete from t1;
insert t1 values (2);
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
delete from t1;
Warnings:
-Warning 4112 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
+Warning 4114 Versioned table `test`.`t1`: partition `p1` is full, add more HISTORY partitions
alter table t1 add partition (partition p0 history, partition p2 history);
select subpartition_name,table_rows from information_schema.partitions where table_schema='test' and table_name='t1';
subpartition_name table_rows
diff --git a/plugin/metadata_lock_info/metadata_lock_info.cc b/plugin/metadata_lock_info/metadata_lock_info.cc
index ef6fdd5cc5c..e32bbc55f3e 100644
--- a/plugin/metadata_lock_info/metadata_lock_info.cc
+++ b/plugin/metadata_lock_info/metadata_lock_info.cc
@@ -26,6 +26,7 @@ static const LEX_STRING metadata_lock_info_lock_name[] = {
{ C_STRING_WITH_LEN("Table metadata lock") },
{ C_STRING_WITH_LEN("Stored function metadata lock") },
{ C_STRING_WITH_LEN("Stored procedure metadata lock") },
+ { C_STRING_WITH_LEN("Stored package body metadata lock") },
{ C_STRING_WITH_LEN("Trigger metadata lock") },
{ C_STRING_WITH_LEN("Event metadata lock") },
{ C_STRING_WITH_LEN("Commit lock") },
@@ -53,7 +54,7 @@ static ST_FIELD_INFO i_s_metadata_lock_info_fields_info[] =
MY_I_S_MAYBE_NULL, "lock_mode", SKIP_OPEN_TABLE},
{"LOCK_DURATION", 30, MYSQL_TYPE_STRING, 0,
MY_I_S_MAYBE_NULL, "lock_duration", SKIP_OPEN_TABLE},
- {"LOCK_TYPE", 30, MYSQL_TYPE_STRING, 0,
+ {"LOCK_TYPE", 33, MYSQL_TYPE_STRING, 0,
MY_I_S_MAYBE_NULL, "lock_type", SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", 64, MYSQL_TYPE_STRING, 0,
MY_I_S_MAYBE_NULL, "table_schema", SKIP_OPEN_TABLE},
diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql
index 08f5eadaf7b..6f42e432d7a 100644
--- a/scripts/mysql_system_tables.sql
+++ b/scripts/mysql_system_tables.sql
@@ -82,9 +82,9 @@ CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsign
CREATE TABLE IF NOT EXISTS time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';
-CREATE TABLE IF NOT EXISTS proc (db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum( 'CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns longblob NOT NULL, body longblob NOT NULL, definer char(141) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'IGNORE_BAD_TABLE_OPTIONS', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', 'SIMULTANEOUS_ASSIGNMENT') DEFAULT '' NOT NULL, comment text collate utf8_bin NOT NULL, character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, aggregate enum('NONE', 'GROUP') DEFAULT 'NONE' NOT NULL, PRIMARY KEY (db,name,type)) engine=MyISAM character set utf8 comment='Stored Procedures';
+CREATE TABLE IF NOT EXISTS proc (db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum( 'CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns longblob NOT NULL, body longblob NOT NULL, definer char(141) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'IGNORE_BAD_TABLE_OPTIONS', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', 'SIMULTANEOUS_ASSIGNMENT') DEFAULT '' NOT NULL, comment text collate utf8_bin NOT NULL, character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, aggregate enum('NONE', 'GROUP') DEFAULT 'NONE' NOT NULL, PRIMARY KEY (db,name,type)) engine=MyISAM character set utf8 comment='Stored Procedures';
-CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(80) binary DEFAULT '' NOT NULL, Routine_name char(64) COLLATE utf8_general_ci DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE') NOT NULL, Grantor char(141) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';
+CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(80) binary DEFAULT '' NOT NULL, Routine_name char(64) COLLATE utf8_general_ci DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL, Grantor char(141) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';
-- Create general_log if CSV is enabled.
diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql
index 8e27dfbdc7e..51c7e544228 100644
--- a/scripts/mysql_system_tables_fix.sql
+++ b/scripts/mysql_system_tables_fix.sql
@@ -470,6 +470,16 @@ ALTER TABLE proc ADD character_set_client
ALTER TABLE proc MODIFY character_set_client
char(32) collate utf8_bin DEFAULT NULL;
+ALTER TABLE proc MODIFY type enum('FUNCTION',
+ 'PROCEDURE',
+ 'PACKAGE',
+ 'PACKAGE BODY') NOT NULL;
+
+ALTER TABLE procs_priv MODIFY Routine_type enum('FUNCTION',
+ 'PROCEDURE',
+ 'PACKAGE',
+ 'PACKAGE BODY') NOT NULL;
+
SELECT CASE WHEN COUNT(*) > 0 THEN
CONCAT ("WARNING: NULL values of the 'character_set_client' column ('mysql.proc' table) have been updated with a default value (", @@character_set_client, "). Please verify if necessary.")
ELSE NULL
diff --git a/sql/item.cc b/sql/item.cc
index d3345f69a40..9f0280b678c 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1789,13 +1789,16 @@ void Item_sp_variable::make_field(THD *thd, Send_field *field)
Item_splocal methods
*****************************************************************************/
-Item_splocal::Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name,
+Item_splocal::Item_splocal(THD *thd,
+ const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name,
uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q, uint len_in_q):
Item_sp_variable(thd, sp_var_name),
Rewritable_query_parameter(pos_in_q, len_in_q),
Type_handler_hybrid_field_type(handler),
+ m_rcontext_handler(rh),
m_var_idx(sp_var_idx)
{
maybe_null= TRUE;
@@ -1803,9 +1806,21 @@ Item_splocal::Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name,
}
+sp_rcontext *Item_splocal::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
+Item_field *Item_splocal::get_variable(sp_rcontext *ctx) const
+{
+ return get_rcontext(ctx)->get_variable(m_var_idx);
+}
+
+
bool Item_splocal::fix_fields(THD *thd, Item **ref)
{
- Item_field *item= thd->spcont->get_variable(m_var_idx);
+ Item *item= get_variable(thd->spcont);
set_handler(item->type_handler());
return fix_fields_from_item(thd, ref, item);
}
@@ -1816,7 +1831,7 @@ Item_splocal::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx);
+ return get_variable(m_thd->spcont);
}
const Item *
@@ -1824,7 +1839,7 @@ Item_splocal::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx);
+ return get_variable(m_thd->spcont);
}
@@ -1833,13 +1848,15 @@ Item_splocal::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return thd->spcont->get_variable_addr(m_var_idx);
+ return get_rcontext(thd->spcont)->get_variable_addr(m_var_idx);
}
void Item_splocal::print(String *str, enum_query_type)
{
- str->reserve(m_name.length+8);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + 8 + prefix->length);
+ str->append(prefix);
str->append(&m_name);
str->append('@');
str->qs_append(m_var_idx);
@@ -1848,7 +1865,7 @@ void Item_splocal::print(String *str, enum_query_type)
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
- return ctx->set_variable(thd, get_var_idx(), it);
+ return get_rcontext(ctx)->set_variable(thd, get_var_idx(), it);
}
@@ -1930,7 +1947,7 @@ bool Item_splocal::check_cols(uint n)
bool Item_splocal_row_field::fix_fields(THD *thd, Item **ref)
{
- Item *item= thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
return fix_fields_from_item(thd, ref, item);
}
@@ -1940,7 +1957,7 @@ Item_splocal_row_field::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
}
@@ -1949,7 +1966,7 @@ Item_splocal_row_field::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return m_thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ return get_variable(m_thd->spcont)->element_index(m_field_idx);
}
@@ -1958,13 +1975,15 @@ Item_splocal_row_field::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->m_sp);
DBUG_ASSERT(fixed);
- return thd->spcont->get_variable(m_var_idx)->addr(m_field_idx);
+ return get_variable(thd->spcont)->addr(m_field_idx);
}
void Item_splocal_row_field::print(String *str, enum_query_type)
{
- str->reserve(m_name.length + m_field_name.length + 8);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ str->reserve(m_name.length + m_field_name.length + 8 + prefix->length);
+ str->append(prefix);
str->append(&m_name);
str->append('.');
str->append(&m_field_name);
@@ -1978,18 +1997,19 @@ void Item_splocal_row_field::print(String *str, enum_query_type)
bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
- return ctx->set_variable_row_field(thd, m_var_idx, m_field_idx, it);
+ return get_rcontext(ctx)->set_variable_row_field(thd, m_var_idx, m_field_idx,
+ it);
}
bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
{
m_thd= thd;
- if (thd->spcont->find_row_field_by_name_or_error(&m_field_idx,
- m_var_idx,
- m_field_name))
+ if (get_rcontext(thd->spcont)->find_row_field_by_name_or_error(&m_field_idx,
+ m_var_idx,
+ m_field_name))
return true;
- Item *item= thd->spcont->get_variable(m_var_idx)->element_index(m_field_idx);
+ Item *item= get_variable(thd->spcont)->element_index(m_field_idx);
set_handler(item->type_handler());
return fix_fields_from_item(thd, it, item);
}
@@ -1997,9 +2017,12 @@ bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
{
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
// +16 should be enough for .NNN@[""]
- if (str->reserve(m_name.length + 2 * m_field_name.length + 16))
+ if (str->reserve(m_name.length + 2 * m_field_name.length +
+ prefix->length + 16))
return;
+ str->qs_append(prefix);
str->qs_append(&m_name);
str->qs_append('.');
str->qs_append(&m_field_name);
diff --git a/sql/item.h b/sql/item.h
index 2a7b92dd601..42442468be9 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -367,6 +367,39 @@ typedef enum monotonicity_info
class sp_rcontext;
+class Sp_rcontext_handler
+{
+public:
+ virtual ~Sp_rcontext_handler() {}
+ virtual const LEX_CSTRING *get_name_prefix() const= 0;
+ virtual sp_rcontext *get_rcontext(sp_rcontext *ctx) const= 0;
+};
+
+
+class Sp_rcontext_handler_local: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+class Sp_rcontext_handler_package_body: public Sp_rcontext_handler
+{
+public:
+ const LEX_CSTRING *get_name_prefix() const;
+ sp_rcontext *get_rcontext(sp_rcontext *ctx) const;
+};
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_local sp_rcontext_handler_local;
+
+
+extern MYSQL_PLUGIN_IMPORT
+ Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+
class Item_equal;
@@ -2384,13 +2417,20 @@ class Item_splocal :public Item_sp_variable,
public Type_handler_hybrid_field_type
{
protected:
+ const Sp_rcontext_handler *m_rcontext_handler;
+
uint m_var_idx;
Type m_type;
bool append_value_for_log(THD *thd, String *str);
+
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
+ Item_field *get_variable(sp_rcontext *ctx) const;
+
public:
- Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name, uint sp_var_idx,
+ Item_splocal(THD *thd, const Sp_rcontext_handler *rh,
+ const LEX_CSTRING *sp_var_name, uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0);
@@ -2452,10 +2492,11 @@ class Item_splocal_with_delayed_data_type: public Item_splocal
{
public:
Item_splocal_with_delayed_data_type(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
uint sp_var_idx,
uint pos_in_q, uint len_in_q)
- :Item_splocal(thd, sp_var_name, sp_var_idx, &type_handler_null,
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, &type_handler_null,
pos_in_q, len_in_q)
{ }
};
@@ -2474,13 +2515,13 @@ protected:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
const LEX_CSTRING *sp_field_name,
uint sp_var_idx, uint sp_field_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0)
- :Item_splocal(thd, sp_var_name, sp_var_idx, handler,
- pos_in_q, len_in_q),
+ :Item_splocal(thd, rh, sp_var_name, sp_var_idx, handler, pos_in_q, len_in_q),
m_field_name(*sp_field_name),
m_field_idx(sp_field_idx)
{ }
@@ -2498,12 +2539,13 @@ class Item_splocal_row_field_by_name :public Item_splocal_row_field
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field_by_name(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name,
const LEX_CSTRING *sp_field_name,
uint sp_var_idx,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0)
- :Item_splocal_row_field(thd, sp_var_name, sp_field_name,
+ :Item_splocal_row_field(thd, rh, sp_var_name, sp_field_name,
sp_var_idx, 0 /* field index will be set later */,
handler, pos_in_q, len_in_q)
{ }
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 548ce3bac94..385d8de98fc 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -3399,6 +3399,8 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
Item *func= NULL;
LEX *lex= thd->lex;
sp_name *qname;
+ const Sp_handler *sph= &sp_handler_function;
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
if (has_named_parameters(item_list))
{
@@ -3419,13 +3421,18 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name,
arg_count= item_list->elements;
qname= new (thd->mem_root) sp_name(db, name, use_explicit_name);
- sp_handler_function.add_used_routine(lex, thd, qname);
-
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ qname, &sph, &pkgname))
+ return NULL;
+ sph->add_used_routine(lex, thd, qname);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(lex, thd, &pkgname);
if (arg_count > 0)
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname,
- *item_list);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph, *item_list);
else
- func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname);
+ func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(),
+ qname, sph);
lex->safe_to_cache_query= 0;
return func;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4e79b45f5d6..52e593edefd 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -6295,16 +6295,17 @@ longlong Item_func_row_count::val_int()
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name):
- Item_func(thd), Item_sp(thd, context_arg, name)
+ sp_name *name, const Sp_handler *sph):
+ Item_func(thd), Item_sp(thd, context_arg, name), m_handler(sph)
{
maybe_null= 1;
}
Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name_arg, List<Item> &list):
- Item_func(thd, list), Item_sp(thd, context_arg, name_arg)
+ sp_name *name_arg, const Sp_handler *sph,
+ List<Item> &list):
+ Item_func(thd, list), Item_sp(thd, context_arg, name_arg), m_handler(sph)
{
maybe_null= 1;
}
@@ -6443,7 +6444,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0);
- sp_head *sp= sp_handler_function.sp_find_routine(thd, m_name, true);
+ sp_head *sp= m_handler->sp_find_routine(thd, m_name, true);
/*
Checking privileges to execute the function while creating view and
diff --git a/sql/item_func.h b/sql/item_func.h
index 38a40e71544..35db77891b1 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -2772,6 +2772,7 @@ class Item_func_sp :public Item_func,
public Item_sp
{
private:
+ const Sp_handler *m_handler;
bool execute();
@@ -2786,10 +2787,11 @@ protected:
}
public:
- Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name);
+ Item_func_sp(THD *thd, Name_resolution_context *context_arg,
+ sp_name *name, const Sp_handler *sph);
Item_func_sp(THD *thd, Name_resolution_context *context_arg,
- sp_name *name, List<Item> &list);
+ sp_name *name, const Sp_handler *sph, List<Item> &list);
virtual ~Item_func_sp()
{}
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 5156bd08ef2..0b9e515a61f 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -2630,14 +2630,13 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
else
{
sp_variable *spv;
- sp_pcontext *spc;
+ const Sp_rcontext_handler *rh;
LEX *lex;
if ((lex= thd->lex) &&
- (spc= lex->spcont) &&
- (spv= spc->find_variable(&name, false)))
+ (spv= lex->find_variable(&name, &rh)))
{
Item_splocal *splocal= new (thd->mem_root)
- Item_splocal(thd, &name, spv->offset, spv->type_handler(), 0);
+ Item_splocal(thd, rh, &name, spv->offset, spv->type_handler(), 0);
#ifdef DBUG_ASSERT_EXISTS
if (splocal)
splocal->m_sp= lex->sphead;
diff --git a/sql/lex.h b/sql/lex.h
index 41a81c737f7..9833809cd5d 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -90,6 +90,7 @@ static SYMBOL symbols[] = {
{ "BIT", SYM(BIT_SYM)},
{ "BLOB", SYM(BLOB_SYM)},
{ "BLOCK", SYM(BLOCK_SYM)},
+ { "BODY", SYM(BODY_SYM)},
{ "BOOL", SYM(BOOL_SYM)},
{ "BOOLEAN", SYM(BOOLEAN_SYM)},
{ "BOTH", SYM(BOTH)},
@@ -456,6 +457,7 @@ static SYMBOL symbols[] = {
{ "OUTFILE", SYM(OUTFILE)},
{ "OVER", SYM(OVER_SYM)},
{ "OWNER", SYM(OWNER_SYM)},
+ { "PACKAGE", SYM(PACKAGE_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
{ "PAGE", SYM(PAGE_SYM)},
{ "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)},
diff --git a/sql/mdl.cc b/sql/mdl.cc
index bf708c945f4..f03fc89fcc1 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -85,6 +85,7 @@ PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{0, "Waiting for table metadata lock", 0},
{0, "Waiting for stored function metadata lock", 0},
{0, "Waiting for stored procedure metadata lock", 0},
+ {0, "Waiting for stored package body metadata lock", 0},
{0, "Waiting for trigger metadata lock", 0},
{0, "Waiting for event metadata lock", 0},
{0, "Waiting for commit lock", 0},
@@ -3043,6 +3044,7 @@ const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
case MDL_key::TABLE : return "TABLE";
case MDL_key::FUNCTION : return "FUNCTION";
case MDL_key::PROCEDURE : return "PROCEDURE";
+ case MDL_key::PACKAGE_BODY: return "PACKAGE BODY";
case MDL_key::TRIGGER : return "TRIGGER";
case MDL_key::EVENT : return "EVENT";
case MDL_key::COMMIT : return "COMMIT";
diff --git a/sql/mdl.h b/sql/mdl.h
index be9cc806ec2..a537022484f 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -299,6 +299,7 @@ public:
TABLE,
FUNCTION,
PROCEDURE,
+ PACKAGE_BODY,
TRIGGER,
EVENT,
COMMIT,
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 4ee9cda70bf..11c8b49d49c 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3829,6 +3829,8 @@ SHOW_VAR com_status_vars[]= {
{"create_event", STMT_STATUS(SQLCOM_CREATE_EVENT)},
{"create_function", STMT_STATUS(SQLCOM_CREATE_SPFUNCTION)},
{"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)},
+ {"create_package", STMT_STATUS(SQLCOM_CREATE_PACKAGE)},
+ {"create_package_body", STMT_STATUS(SQLCOM_CREATE_PACKAGE_BODY)},
{"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)},
{"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)},
{"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)},
@@ -3848,6 +3850,8 @@ SHOW_VAR com_status_vars[]= {
{"drop_function", STMT_STATUS(SQLCOM_DROP_FUNCTION)},
{"drop_index", STMT_STATUS(SQLCOM_DROP_INDEX)},
{"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)},
+ {"drop_package", STMT_STATUS(SQLCOM_DROP_PACKAGE)},
+ {"drop_package_body", STMT_STATUS(SQLCOM_DROP_PACKAGE_BODY)},
{"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)},
{"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)},
{"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)},
@@ -3904,6 +3908,8 @@ SHOW_VAR com_status_vars[]= {
{"show_create_db", STMT_STATUS(SQLCOM_SHOW_CREATE_DB)},
{"show_create_event", STMT_STATUS(SQLCOM_SHOW_CREATE_EVENT)},
{"show_create_func", STMT_STATUS(SQLCOM_SHOW_CREATE_FUNC)},
+ {"show_create_package", STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE)},
+ {"show_create_package_body",STMT_STATUS(SQLCOM_SHOW_CREATE_PACKAGE_BODY)},
{"show_create_proc", STMT_STATUS(SQLCOM_SHOW_CREATE_PROC)},
{"show_create_table", STMT_STATUS(SQLCOM_SHOW_CREATE)},
{"show_create_trigger", STMT_STATUS(SQLCOM_SHOW_CREATE_TRIGGER)},
@@ -3925,6 +3931,11 @@ SHOW_VAR com_status_vars[]= {
{"show_keys", STMT_STATUS(SQLCOM_SHOW_KEYS)},
{"show_master_status", STMT_STATUS(SQLCOM_SHOW_MASTER_STAT)},
{"show_open_tables", STMT_STATUS(SQLCOM_SHOW_OPEN_TABLES)},
+ {"show_package_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE)},
+#ifndef DBUG_OFF
+ {"show_package_body_code", STMT_STATUS(SQLCOM_SHOW_PACKAGE_BODY_CODE)},
+#endif
+ {"show_package_body_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE_BODY)},
{"show_plugins", STMT_STATUS(SQLCOM_SHOW_PLUGINS)},
{"show_privileges", STMT_STATUS(SQLCOM_SHOW_PRIVILEGES)},
#ifndef DBUG_OFF
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 3e9e94bbbfd..c725bf39eb0 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7788,6 +7788,10 @@ ER_WRONG_INSERT_INTO_SEQUENCE
eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead."
ER_SP_STACK_TRACE
eng "At line %u in %s"
+ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
+ eng "Subroutine '%-.192s' is declared in the package specification but is not defined in the package body"
+ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED
+ eng "Subroutine '%-.192s' has a forward declaration but is not defined"
ER_COMPRESSED_COLUMN_USED_AS_KEY
eng "Compressed column '%-.192s' can't be used in key specification"
ER_UNKNOWN_COMPRESSION_METHOD
diff --git a/sql/sp.cc b/sql/sp.cc
index 773a5479199..d75e40af6ce 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -45,6 +45,17 @@ sp_cache **Sp_handler_function::get_cache(THD *thd) const
return &thd->sp_func_cache;
}
+sp_cache **Sp_handler_package_spec::get_cache(THD *thd) const
+{
+ return &thd->sp_package_spec_cache;
+}
+
+sp_cache **Sp_handler_package_body::get_cache(THD *thd) const
+{
+ return &thd->sp_package_body_cache;
+}
+
+
ulong Sp_handler_procedure::recursion_depth(THD *thd) const
{
return thd->variables.max_sp_recursion_depth;
@@ -85,7 +96,23 @@ bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp,
Sp_handler_procedure sp_handler_procedure;
Sp_handler_function sp_handler_function;
+Sp_handler_package_spec sp_handler_package_spec;
+Sp_handler_package_body sp_handler_package_body;
Sp_handler_trigger sp_handler_trigger;
+Sp_handler_package_procedure sp_handler_package_procedure;
+Sp_handler_package_function sp_handler_package_function;
+
+
+const Sp_handler *Sp_handler_procedure::package_routine_handler() const
+{
+ return &sp_handler_package_procedure;
+}
+
+
+const Sp_handler *Sp_handler_function::package_routine_handler() const
+{
+ return &sp_handler_package_function;
+}
static const
@@ -690,7 +717,7 @@ Sp_handler::db_find_routine(THD *thd,
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
&params);
- if (type() == TYPE_ENUM_PROCEDURE)
+ if (type() != TYPE_ENUM_FUNCTION)
returns= empty_clex_str;
else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
&returns))
@@ -718,7 +745,7 @@ Sp_handler::db_find_routine(THD *thd,
ret= db_load_routine(thd, name, sphp,
sql_mode, params, returns, body, chistics, definer,
- created, modified, creation_ctx);
+ created, modified, NULL, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -788,6 +815,8 @@ Silence_deprecated_warning::handle_condition(
@param[in] thd Thread handler
@param[in] defstr CREATE... string
@param[in] sql_mode SQL mode
+ @param[in] parent The owner package for package routines,
+ or NULL for standalone routines.
@param[in] creation_ctx Creation context of stored routines
@return Pointer on sp_head struct
@@ -796,6 +825,7 @@ Silence_deprecated_warning::handle_condition(
*/
static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx)
{
sp_head *sp;
@@ -816,6 +846,7 @@ static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
}
lex_start(thd);
+ thd->lex->sphead= parent;
thd->push_internal_handler(&warning_handler);
thd->spcont= 0;
@@ -886,6 +917,7 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
const st_sp_chistics &chistics,
const AUTHID &definer,
longlong created, longlong modified,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx) const
{
LEX *old_lex= thd->lex, newlex;
@@ -942,7 +974,7 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
}
{
- *sphp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ *sphp= sp_compile(thd, &defstr, sql_mode, parent, creation_ctx);
/*
Force switching back to the saved current database (if changed),
because it may be NULL. In this case, mysql_change_db() would
@@ -967,6 +999,22 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
(*sphp)->set_info(created, modified, chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
+
+ if (type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ sp_package *package= sphp[0]->get_package();
+ List_iterator<LEX> it(package->m_routine_implementations);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ lex->sphead->set_definer(&definer.user, &definer.host);
+ lex->sphead->set_suid(package->suid());
+ lex->sphead->m_sql_mode= sql_mode;
+ lex->sphead->set_creation_ctx(creation_ctx);
+ lex->sphead->optimize();
+ }
+ }
+
/*
Not strictly necessary to invoke this method here, since we know
that we've parsed CREATE PROCEDURE/FUNCTION and not an
@@ -1058,6 +1106,32 @@ Sp_handler::sp_drop_routine_internal(THD *thd,
}
+int
+Sp_handler::sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if (SP_OK != (ret= db_find_routine_aux(thd, name, table)))
+ return ret;
+ return sp_drop_routine_internal(thd, name, table);
+}
+
+
+int
+Sp_handler_package_spec::
+ sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name) const
+{
+ int ret;
+ if (SP_OK != (ret= db_find_routine_aux(thd, name, table)))
+ return ret;
+ ret= sp_handler_package_body.sp_find_and_drop_routine(thd, table, name);
+ if (ret != SP_KEY_NOT_FOUND && ret != SP_OK)
+ return ret;
+ return Sp_handler::sp_find_and_drop_routine(thd, table, name);
+}
+
+
/**
Write stored-routine object into mysql.proc.
@@ -1142,7 +1216,22 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
{
if (lex->create_info.or_replace())
{
- if ((ret= sp_drop_routine_internal(thd, sp, table)))
+ switch (type()) {
+ case TYPE_ENUM_PACKAGE:
+ // Drop together with its PACKAGE BODY mysql.proc record
+ ret= sp_handler_package_spec.sp_find_and_drop_routine(thd, table, sp);
+ break;
+ case TYPE_ENUM_PACKAGE_BODY:
+ case TYPE_ENUM_FUNCTION:
+ case TYPE_ENUM_PROCEDURE:
+ ret= sp_drop_routine_internal(thd, sp, table);
+ break;
+ case TYPE_ENUM_TRIGGER:
+ case TYPE_ENUM_PROXY:
+ DBUG_ASSERT(0);
+ ret= SP_OK;
+ }
+ if (ret)
goto done;
}
else if (lex->create_info.if_not_exists())
@@ -1400,6 +1489,49 @@ append_comment(String *buf, const LEX_CSTRING &comment)
}
+static bool
+append_package_chistics(String *buf, const st_sp_chistics &chistics)
+{
+ return append_suid(buf, chistics.suid) ||
+ append_comment(buf, chistics.comment);
+}
+
+
+bool
+Sp_handler_package::show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const
+{
+ sql_mode_t old_sql_mode= thd->variables.sql_mode;
+ thd->variables.sql_mode= sql_mode;
+ bool rc=
+ buf->append(STRING_WITH_LEN("CREATE ")) ||
+ (ddl_options.or_replace() &&
+ buf->append(STRING_WITH_LEN("OR REPLACE "))) ||
+ append_definer(thd, buf, &definer.user, &definer.host) ||
+ buf->append(type_lex_cstring()) ||
+ buf->append(" ", 1) ||
+ (ddl_options.if_not_exists() &&
+ buf->append(STRING_WITH_LEN("IF NOT EXISTS "))) ||
+ (db.length > 0 &&
+ (append_identifier(thd, buf, db.str, db.length) ||
+ buf->append('.'))) ||
+ append_identifier(thd, buf, name.str, name.length) ||
+ append_package_chistics(buf, chistics) ||
+ buf->append(" ", 1) ||
+ buf->append(body.str, body.length);
+ thd->variables.sql_mode= old_sql_mode;
+ return rc;
+}
+
+
/**
Delete the record for the stored routine object from mysql.proc
and do binary logging.
@@ -1433,10 +1565,7 @@ Sp_handler::sp_drop_routine(THD *thd,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK)
- ret= sp_drop_routine_internal(thd, name, table);
-
- if (ret == SP_OK &&
+ if (SP_OK == (ret= sp_find_and_drop_routine(thd, table, name)) &&
write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
/*
@@ -1631,9 +1760,12 @@ bool lock_db_routines(THD *thd, const char *db)
longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
- mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ?
- MDL_key::FUNCTION : MDL_key::PROCEDURE,
- db, sp_name, MDL_EXCLUSIVE, MDL_TRANSACTION);
+ const Sp_handler *sph= Sp_handler::handler((stored_procedure_type)
+ sp_type);
+ if (!sph)
+ sph= &sp_handler_procedure;
+ mdl_request->init(sph->get_mdl_type(), db, sp_name,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(mdl_request);
} while (! (nxtres= table->file->ha_index_next_same(table->record[0], keybuf, key_len)));
}
@@ -1780,6 +1912,34 @@ Sp_handler::sp_show_create_routine(THD *thd,
/*
+ A helper class to split package name from a dot-qualified name
+ and return it as a 0-terminated string
+ 'pkg.name' -> 'pkg\0'
+*/
+class Prefix_name_buf: public LEX_CSTRING
+{
+ char m_buf[SAFE_NAME_LEN + 1];
+public:
+ Prefix_name_buf(const THD *thd, const LEX_CSTRING &name)
+ {
+ const char *end;
+ if (!(end= strrchr(name.str, '.')))
+ {
+ static_cast<LEX_CSTRING*>(this)[0]= null_clex_str;
+ }
+ else
+ {
+ str= m_buf;
+ length= end - name.str;
+ set_if_smaller(length, sizeof(m_buf) - 1);
+ memcpy(m_buf, name.str, length);
+ m_buf[length]= '\0';
+ }
+ }
+};
+
+
+/*
In case of recursions, we create multiple copies of the same SP.
This methods checks the current recursion depth.
In case if the recursion limit exceeded, it throws an error
@@ -1794,9 +1954,17 @@ Sp_handler::sp_clone_and_link_routine(THD *thd,
sp_head *sp) const
{
DBUG_ENTER("sp_link_routine");
+ int rc;
ulong level;
sp_head *new_sp;
LEX_CSTRING returns= empty_clex_str;
+ Database_qualified_name lname(name->m_db, name->m_name);
+#ifndef DBUG_OFF
+ uint parent_subroutine_count=
+ !sp->m_parent ? 0 :
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements;
+#endif
/*
String buffer for RETURNS data type must have system charset;
@@ -1838,13 +2006,63 @@ Sp_handler::sp_clone_and_link_routine(THD *thd,
sp_returns_type(thd, retstr, sp);
returns= retstr.lex_cstring();
}
- if (db_load_routine(thd, name, &new_sp,
+
+ if (sp->m_parent)
+ {
+ /*
+ If we're cloning a recursively called package routine,
+ we need to take some special measures:
+ 1. Cut the package name prefix from the routine name: 'pkg1.p1' -> 'p1',
+ to have db_load_routine() generate and parse a query like this:
+ CREATE PROCEDURE p1 ...;
+ rether than:
+ CREATE PROCEDURE pkg1.p1 ...;
+ The latter would be misinterpreted by the parser as a standalone
+ routine 'p1' in the database 'pkg1', which is not what we need.
+ 2. We pass m_parent to db_load_routine() to have it set
+ thd->lex->sphead to sp->m_parent before calling parse_sql().
+ These two measures allow to parse a package subroutine using
+ the grammar for standalone routines, e.g.:
+ CREATE PROCEDURE p1 ... END;
+ instead of going through a more complex query, e.g.:
+ CREATE PACKAGE BODY pkg1 AS
+ PROCEDURE p1 ... END;
+ END;
+ */
+ size_t prefix_length= sp->m_parent->m_name.length + 1;
+ DBUG_ASSERT(prefix_length < lname.m_name.length);
+ DBUG_ASSERT(lname.m_name.str[sp->m_parent->m_name.length] == '.');
+ lname.m_name.str+= prefix_length;
+ lname.m_name.length-= prefix_length;
+ sp->m_parent->m_is_cloning_routine= true;
+ }
+
+
+ rc= db_load_routine(thd, &lname, &new_sp,
sp->m_sql_mode, sp->m_params, returns,
sp->m_body, sp->chistics(),
sp->m_definer,
sp->m_created, sp->m_modified,
- sp->get_creation_ctx()) == SP_OK)
+ sp->m_parent,
+ sp->get_creation_ctx());
+ if (sp->m_parent)
+ sp->m_parent->m_is_cloning_routine= false;
+
+ if (rc == SP_OK)
{
+#ifndef DBUG_OFF
+ /*
+ We've just called the parser to clone the routine.
+ In case of a package routine, make sure that the parser
+ has not added any new subroutines directly to the parent package.
+ The cloned subroutine instances get linked below to the first instance,
+ they must have no direct links from the parent package.
+ */
+ DBUG_ASSERT(!sp->m_parent ||
+ parent_subroutine_count ==
+ sp->m_parent->m_routine_declarations.elements +
+ sp->m_parent->m_routine_implementations.elements);
+#endif
sp->m_last_cached_sp->m_next_cached_sp= new_sp;
new_sp->m_recursion_level= level;
new_sp->m_first_instance= sp;
@@ -1878,7 +2096,7 @@ sp_head *
Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
bool cache_only) const
{
- DBUG_ENTER("sp_find_routine");
+ DBUG_ENTER("Sp_handler::sp_find_routine");
DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d",
(int) name->m_db.length, name->m_db.str,
(int) name->m_name.length, name->m_name.str,
@@ -1895,6 +2113,74 @@ Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name,
/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param pkgname_str - package name
+ @param name - a mixed qualified name, with:
+ * name->m_db set to the database, e.g. "dbname"
+ * name->m_name set to a package-qualified name,
+ e.g. "pkgname.spname".
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened.
+*/
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("sp_find_package_routine");
+ Database_qualified_name pkgname(&name->m_db, &pkgname_str);
+ sp_head *ph= sp_cache_lookup(&thd->sp_package_body_cache, &pkgname);
+ if (!ph && !cache_only)
+ sp_handler_package_body.db_find_and_cache_routine(thd, &pkgname, &ph);
+ if (ph)
+ {
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : 0;
+ sp_package *pkg= ph->get_package();
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *plex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ sp_head *sp= plex ? plex->sphead : NULL;
+ if (sp)
+ DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+/**
+ Find a package routine.
+ See sp_cache_routine() for more information on parameters and return value.
+
+ @param thd - current THD
+ @param name - Qualified name with the following format:
+ * name->m_db is set to the database name, e.g. "dbname"
+ * name->m_name is set to a package-qualified name,
+ e.g. "pkgname.spname", as a single string with a
+ dot character as a separator.
+ @param cache_only - don't load mysql.proc if not cached
+ @retval non-NULL - a pointer to an sp_head object
+ @retval NULL - an error happened
+*/
+sp_head *
+Sp_handler::sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+{
+ DBUG_ENTER("Sp_handler::sp_find_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_find_package_routine(thd, pkgname, name, cache_only));
+}
+
+
+/**
This is used by sql_acl.cc:mysql_routine_grant() and is used to find
the routines in 'routines'.
@@ -1977,7 +2263,9 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
*/
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view)
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view)
{
my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
Query_tables_list::START_SROUTINES_HASH_SIZE,
@@ -1994,6 +2282,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
return FALSE;
prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);
rn->belong_to_view= belong_to_view;
+ rn->m_handler= handler;
rn->m_sp_cache_version= 0;
return TRUE;
}
@@ -2001,6 +2290,268 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
}
+/*
+ Find and cache a routine in a parser-safe reentrant mode.
+
+ If sp_head is not in the cache,
+ its loaded from mysql.proc, parsed using parse_sql(), and cached.
+ Note, as it is called from inside parse_sql() itself,
+ we need to preserve and restore the parser state.
+
+ It's used during parsing of CREATE PACKAGE BODY,
+ to load the corresponding CREATE PACKAGE.
+*/
+int
+Sp_handler::sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *name,
+ sp_head **sp) const
+{
+ int ret;
+ Parser_state *oldps= thd->m_parser_state;
+ thd->m_parser_state= NULL;
+ ret= sp_cache_routine(thd, name, false, sp);
+ thd->m_parser_state= oldps;
+ return ret;
+}
+
+
+/*
+ Check if a routine has a declaration in the CREATE PACKAGE statement,
+ by looking up in thd->sp_package_spec_cache, and by loading from mysql.proc
+ if needed.
+
+ @param thd current thd
+ @param db the database name
+ @param package the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function can be called in arbitrary context:
+ - inside a package routine
+ - inside a standalone routine
+ - inside a anonymous block
+ - outside of any routines
+
+ The state of the package specification (i.e. the CREATE PACKAGE statement)
+ for "package" before the call of this function is not known:
+ it can be cached, or not cached.
+ After the call of this function, the package specification is always cached,
+ unless a fatal error happens.
+*/
+static bool
+is_package_public_routine(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine,
+ stored_procedure_type type)
+{
+ sp_head *sp= NULL;
+ Database_qualified_name tmp(db, package);
+ bool ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, &tmp, &sp);
+ sp_package *spec= (!ret && sp) ? sp->get_package() : NULL;
+ return spec && spec->m_routine_declarations.find(routine, type);
+}
+
+
+/*
+ Check if a routine has a declaration in the CREATE PACKAGE statement
+ by looking up in sp_package_spec_cache.
+
+ @param thd current thd
+ @param db the database name
+ @param pkgname the package name
+ @param name the routine name
+ @param type the routine type
+ @retval true, if the routine has a declaration
+ @retval false, if the routine does not have a declaration
+
+ This function is called in the middle of CREATE PACKAGE BODY parsing,
+ to lookup the current package routines.
+ The package specification (i.e. the CREATE PACKAGE statement) for
+ the current package body must already be loaded and cached at this point.
+*/
+static bool
+is_package_public_routine_quick(THD *thd,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &pkgname,
+ const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ Database_qualified_name tmp(db, pkgname);
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, &tmp);
+ sp_package *pkg= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(pkg); // Must already be cached
+ return pkg && pkg->m_routine_declarations.find(name, type);
+}
+
+
+/*
+ Check if a qualified name, e.g. "CALL name1.name2",
+ refers to a known routine in the package body "pkg".
+*/
+static bool
+is_package_body_routine(THD *thd, sp_package *pkg,
+ const LEX_CSTRING &name1,
+ const LEX_CSTRING &name2,
+ stored_procedure_type type)
+{
+ return Sp_handler::eq_routine_name(pkg->m_name, name1) &&
+ (pkg->m_routine_declarations.find(name2, type) ||
+ pkg->m_routine_implementations.find(name2, type));
+}
+
+
+/*
+ Resolve a qualified routine reference xxx.yyy(), between:
+ - A standalone routine: xxx.yyy
+ - A package routine: current_database.xxx.yyy
+*/
+bool Sp_handler::
+ sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ /*
+ If a qualified routine name was used, e.g. xxx.yyy(),
+ we possibly have a call to a package routine.
+ Rewrite name if name->m_db (xxx) is a known package,
+ and name->m_name (yyy) is a known routine in this package.
+ */
+ LEX_CSTRING tmpdb= thd->db;
+ if (is_package_public_routine(thd, tmpdb, name->m_db, name->m_name, type()) ||
+ // Check if a package routine calls a private routine
+ (caller && caller->m_parent &&
+ is_package_body_routine(thd, caller->m_parent,
+ name->m_db, name->m_name, type())) ||
+ // Check if a package initialization sections calls a private routine
+ (caller && (pkg= caller->get_package()) &&
+ is_package_body_routine(thd, pkg, name->m_db, name->m_name, type())))
+ {
+ pkgname->m_db= tmpdb;
+ pkgname->m_name= name->m_db;
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root, tmpdb,
+ name->m_db, name->m_name);
+ }
+ return false;
+}
+
+
+/*
+ Resolve a non-qualified routine reference yyy(), between:
+ - A standalone routine: current_database.yyy
+ - A package routine: current_database.current_package.yyy
+*/
+bool Sp_handler::
+ sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ sp_package *pkg;
+
+ if (!caller || !caller->m_name.length)
+ {
+ /*
+ We are either in a an anonymous block,
+ or not in a routine at all.
+ */
+ return false; // A standalone routine is called
+ }
+
+ if (caller->m_parent)
+ {
+ // A package routine calls a non-qualified routine
+ int ret= SP_OK;
+ Prefix_name_buf pkgstr(thd, caller->m_name);
+ DBUG_ASSERT(pkgstr.length);
+ LEX_CSTRING tmpname; // Non-qualified m_name
+ tmpname.str= caller->m_name.str + pkgstr.length + 1;
+ tmpname.length= caller->m_name.length - pkgstr.length - 1;
+
+ /*
+ We're here if a package routine calls another non-qualified
+ function or procedure, e.g. yyy().
+ We need to distinguish two cases:
+ - yyy() is another routine from the same package
+ - yyy() is a standalone routine from the same database
+ To detect if yyy() is a package (rather than a standalone) routine,
+ we check if:
+ - yyy() recursively calls itself
+ - yyy() is earlier implemented in the current CREATE PACKAGE BODY
+ - yyy() has a forward declaration
+ - yyy() is declared in the corresponding CREATE PACKAGE
+ */
+ if (eq_routine_name(tmpname, name->m_name) ||
+ caller->m_parent->m_routine_implementations.find(name->m_name, type()) ||
+ caller->m_parent->m_routine_declarations.find(name->m_name, type()) ||
+ is_package_public_routine_quick(thd, caller->m_db,
+ pkgstr, name->m_name, type()))
+ {
+ DBUG_ASSERT(ret == SP_OK);
+ pkgname->copy(thd->mem_root, caller->m_db, pkgstr);
+ *pkg_routine_handler= package_routine_handler();
+ if (name->make_package_routine_name(thd->mem_root, pkgstr, name->m_name))
+ return true;
+ }
+ return ret != SP_OK;
+ }
+
+ if ((pkg= caller->get_package()) &&
+ pkg->m_routine_implementations.find(name->m_name, type()))
+ {
+ pkgname->m_db= caller->m_db;
+ pkgname->m_name= caller->m_name;
+ // Package initialization section is calling a non-qualified routine
+ *pkg_routine_handler= package_routine_handler();
+ return name->make_package_routine_name(thd->mem_root,
+ caller->m_name, name->m_name);
+ }
+
+ return false; // A standalone routine is called
+
+}
+
+
+/**
+ Detect cases when a package routine (rather than a standalone routine)
+ is called, and rewrite sp_name accordingly.
+
+ @param thd Current thd
+ @param caller The caller routine (or NULL if outside of a routine)
+ @param [IN/OUT] name The called routine name
+ @param [OUT] pkgname If the routine is found to be a package routine,
+ pkgname is populated with the package name.
+ Otherwise, it's not touched.
+ @retval false on success
+ @retval true on error (e.g. EOM, could not read CREATE PACKAGE)
+*/
+bool
+Sp_handler::sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const
+{
+ if (!thd->db.length || !(thd->variables.sql_mode & MODE_ORACLE))
+ return false;
+
+ return name->m_explicit_name ?
+ sp_resolve_package_routine_explicit(thd, caller, name,
+ pkg_routine_handler, pkgname) :
+ sp_resolve_package_routine_implicit(thd, caller, name,
+ pkg_routine_handler, pkgname);
+}
+
+
/**
Add routine which is explicitly used by statement to the set of stored
routines used by this statement.
@@ -2023,7 +2574,7 @@ void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx,
const Database_qualified_name *rt) const
{
MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str);
- (void) sp_add_used_routine(prelocking_ctx, arena, &key, 0);
+ (void) sp_add_used_routine(prelocking_ctx, arena, &key, this, 0);
prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
prelocking_ctx->sroutines_list_own_elements=
prelocking_ctx->sroutines_list.elements;
@@ -2116,7 +2667,8 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
}
@@ -2141,7 +2693,8 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
{
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &rt->mdl_request.key, belong_to_view);
+ &rt->mdl_request.key, rt->m_handler,
+ belong_to_view);
}
@@ -2156,9 +2709,6 @@ int Sroutine_hash_entry::sp_cache_routine(THD *thd,
{
char qname_buff[NAME_LEN*2+1+1];
sp_name name(&mdl_request.key, qname_buff);
- MDL_key::enum_mdl_namespace mdl_type= mdl_request.key.mdl_namespace();
- const Sp_handler *sph= Sp_handler::handler(mdl_type);
- DBUG_ASSERT(sph);
/*
Check that we have an MDL lock on this routine, unless it's a top-level
CALL. The assert below should be unambiguous: the first element
@@ -2167,7 +2717,7 @@ int Sroutine_hash_entry::sp_cache_routine(THD *thd,
*/
DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first);
- return sph->sp_cache_routine(thd, &name, lookup_only, sp);
+ return m_handler->sp_cache_routine(thd, &name, lookup_only, sp);
}
@@ -2198,7 +2748,7 @@ int Sp_handler::sp_cache_routine(THD *thd,
int ret= 0;
sp_cache **spc= get_cache(thd);
- DBUG_ENTER("sp_cache_routine");
+ DBUG_ENTER("Sp_handler::sp_cache_routine");
DBUG_ASSERT(spc);
@@ -2249,6 +2799,75 @@ int Sp_handler::sp_cache_routine(THD *thd,
/**
+ Cache a package routine using its package name and a qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param pkgname_str - package name, e.g. "pkgname"
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here.
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+int
+Sp_handler::sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("sp_cache_package_routine");
+ DBUG_ASSERT(type() == TYPE_ENUM_FUNCTION || type() == TYPE_ENUM_PROCEDURE);
+ sp_name pkgname(&name->m_db, &pkgname_cstr, false);
+ sp_head *ph= NULL;
+ int ret= sp_handler_package_body.sp_cache_routine(thd, &pkgname,
+ lookup_only,
+ &ph);
+ if (!ret)
+ {
+ sp_package *pkg= ph ? ph->get_package() : NULL;
+ LEX_CSTRING tmp= name->m_name;
+ const char *dot= strrchr(tmp.str, '.');
+ size_t prefix_length= dot ? dot - tmp.str + 1 : NULL;
+ tmp.str+= prefix_length;
+ tmp.length-= prefix_length;
+ LEX *rlex= pkg ? pkg->m_routine_implementations.find(tmp, type()) : NULL;
+ *sp= rlex ? rlex->sphead : NULL;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Cache a package routine by its fully qualified name.
+ See sp_cache_routine() for more information on parameters and return values.
+
+ @param thd - current THD
+ @param name - name with the following format:
+ * name->m_db is a database name, e.g. "dbname"
+ * name->m_name is a package-qualified name,
+ e.g. "pkgname.spname"
+ @param lookup_only - don't load mysql.proc if not cached
+ @param [OUT] sp - the result is returned here
+ @retval false - loaded or does not exists
+ @retval true - error while loading mysql.proc
+*/
+int Sp_handler::sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+{
+ DBUG_ENTER("Sp_handler::sp_cache_package_routine");
+ Prefix_name_buf pkgname(thd, name->m_name);
+ DBUG_ASSERT(pkgname.length);
+ DBUG_RETURN(sp_cache_package_routine(thd, pkgname, name, lookup_only, sp));
+}
+
+
+/**
Generates the CREATE... string from the table information.
@return
@@ -2385,7 +3004,7 @@ Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table,
thd->lex= &newlex;
newlex.current_select= NULL;
- sp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ sp= sp_compile(thd, &defstr, sql_mode, NULL, creation_ctx);
*free_sp_head= 1;
thd->lex->sphead= NULL;
lex_end(thd->lex);
diff --git a/sql/sp.h b/sql/sp.h
index 4177ff05868..48ed7165c55 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -31,7 +31,9 @@ class Sroutine_hash_entry;
class THD;
class sp_cache;
class sp_head;
+class sp_package;
class sp_pcontext;
+class sp_name;
class Database_qualified_name;
struct st_sp_chistics;
class Stored_program_creation_ctx;
@@ -49,13 +51,28 @@ enum stored_procedure_type
{
TYPE_ENUM_FUNCTION=1,
TYPE_ENUM_PROCEDURE=2,
- TYPE_ENUM_TRIGGER=3,
- TYPE_ENUM_PROXY=4
+ TYPE_ENUM_PACKAGE=3,
+ TYPE_ENUM_PACKAGE_BODY=4,
+ TYPE_ENUM_TRIGGER=5,
+ TYPE_ENUM_PROXY=6
};
class Sp_handler
{
+ bool sp_resolve_package_routine_explicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+ bool sp_resolve_package_routine_implicit(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_hndlr,
+ Database_qualified_name *pkgname)
+ const;
+protected:
int db_find_routine_aux(THD *thd, const Database_qualified_name *name,
TABLE *table) const;
int db_find_routine(THD *thd, const Database_qualified_name *name,
@@ -72,6 +89,7 @@ class Sp_handler
const st_sp_chistics &chistics,
const AUTHID &definer,
longlong created, longlong modified,
+ sp_package *parent,
Stored_program_creation_ctx *creation_ctx) const;
int sp_drop_routine_internal(THD *thd,
const Database_qualified_name *name,
@@ -80,11 +98,37 @@ class Sp_handler
sp_head *sp_clone_and_link_routine(THD *thd,
const Database_qualified_name *name,
sp_head *sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const LEX_CSTRING &pkgname_cstr,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ int sp_cache_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const LEX_CSTRING pkgname_str,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ sp_head *sp_find_package_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+public: // TODO: make it private or protected
+ virtual int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+
public:
virtual ~Sp_handler() {}
static const Sp_handler *handler(enum enum_sql_command cmd);
static const Sp_handler *handler(stored_procedure_type type);
static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns);
+ static bool eq_routine_name(const LEX_CSTRING &name1,
+ const LEX_CSTRING &name2)
+ {
+ return my_strnncoll(system_charset_info,
+ (const uchar *) name1.str, name1.length,
+ (const uchar *) name2.str, name2.length) == 0;
+ }
const char *type_str() const { return type_lex_cstring().str; }
virtual const char *show_create_routine_col1_caption() const
{
@@ -96,6 +140,10 @@ public:
DBUG_ASSERT(0);
return "";
}
+ virtual const Sp_handler *package_routine_handler() const
+ {
+ return this;
+ }
virtual stored_procedure_type type() const= 0;
virtual LEX_CSTRING type_lex_cstring() const= 0;
virtual LEX_CSTRING empty_body_lex_cstring() const
@@ -132,12 +180,22 @@ public:
void add_used_routine(Query_tables_list *prelocking_ctx,
Query_arena *arena,
- const Database_qualified_name *rt) const;
-
- sp_head *sp_find_routine(THD *thd, const Database_qualified_name *name,
- bool cache_only) const;
- int sp_cache_routine(THD *thd, const Database_qualified_name *name,
- bool lookup_only, sp_head **sp) const;
+ const Database_qualified_name *name) const;
+
+ bool sp_resolve_package_routine(THD *thd,
+ sp_head *caller,
+ sp_name *name,
+ const Sp_handler **pkg_routine_handler,
+ Database_qualified_name *pkgname) const;
+ virtual sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const;
+ virtual int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const;
+
+ int sp_cache_routine_reentrant(THD *thd,
+ const Database_qualified_name *nm,
+ sp_head **sp) const;
bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const;
bool sp_show_create_routine(THD *thd,
@@ -163,16 +221,17 @@ public:
@retval true on error
@retval false on success
*/
- bool show_create_sp(THD *thd, String *buf,
- const LEX_CSTRING &db,
- const LEX_CSTRING &name,
- const LEX_CSTRING &params,
- const LEX_CSTRING &returns,
- const LEX_CSTRING &body,
- const st_sp_chistics &chistics,
- const AUTHID &definer,
- const DDL_options_st ddl_options,
- sql_mode_t sql_mode) const;
+ virtual bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+
};
@@ -202,6 +261,7 @@ public:
{
return MDL_key::PROCEDURE;
}
+ const Sp_handler *package_routine_handler() const;
sp_cache **get_cache(THD *) const;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
HASH *get_priv_hash() const;
@@ -212,6 +272,23 @@ public:
};
+class Sp_handler_package_procedure: public Sp_handler_procedure
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
class Sp_handler_function: public Sp_handler
{
public:
@@ -238,6 +315,7 @@ public:
{
return MDL_key::FUNCTION;
}
+ const Sp_handler *package_routine_handler() const;
sp_cache **get_cache(THD *) const;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
HASH *get_priv_hash() const;
@@ -247,6 +325,109 @@ public:
};
+class Sp_handler_package_function: public Sp_handler_function
+{
+public:
+ int sp_cache_routine(THD *thd, const Database_qualified_name *name,
+ bool lookup_only, sp_head **sp) const
+ {
+ return sp_cache_package_routine(thd, name, lookup_only, sp);
+ }
+ sp_head *sp_find_routine(THD *thd,
+ const Database_qualified_name *name,
+ bool cache_only) const
+ {
+ return sp_find_package_routine(thd, name, cache_only);
+ }
+};
+
+
+class Sp_handler_package: public Sp_handler
+{
+public:
+ bool show_create_sp(THD *thd, String *buf,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name,
+ const LEX_CSTRING &params,
+ const LEX_CSTRING &returns,
+ const LEX_CSTRING &body,
+ const st_sp_chistics &chistics,
+ const AUTHID &definer,
+ const DDL_options_st ddl_options,
+ sql_mode_t sql_mode) const;
+};
+
+
+class Sp_handler_package_spec: public Sp_handler_package
+{
+public: // TODO: make it private or protected
+ int sp_find_and_drop_routine(THD *thd, TABLE *table,
+ const Database_qualified_name *name)
+ const;
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PACKAGE")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring() const
+ {
+ static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
+class Sp_handler_package_body: public Sp_handler_package
+{
+public:
+ stored_procedure_type type() const { return TYPE_ENUM_PACKAGE_BODY; }
+ LEX_CSTRING type_lex_cstring() const
+ {
+ static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PACKAGE BODY")};
+ return m_type_str;
+ }
+ LEX_CSTRING empty_body_lex_cstring() const
+ {
+ static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")};
+ return m_empty_body;
+ }
+ const char *show_create_routine_col1_caption() const
+ {
+ return "Package body";
+ }
+ const char *show_create_routine_col3_caption() const
+ {
+ return "Create Package Body";
+ }
+ MDL_key::enum_mdl_namespace get_mdl_type() const
+ {
+ return MDL_key::PACKAGE_BODY;
+ }
+ sp_cache **get_cache(THD *) const;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ HASH *get_priv_hash() const;
+#endif
+};
+
+
class Sp_handler_trigger: public Sp_handler
{
public:
@@ -266,6 +447,10 @@ public:
extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function;
extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_spec sp_handler_package_spec;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_body sp_handler_package_body;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_function sp_handler_package_function;
+extern MYSQL_PLUGIN_IMPORT Sp_handler_package_procedure sp_handler_package_procedure;
extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger;
@@ -286,6 +471,17 @@ inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd)
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_STATUS_FUNC:
return &sp_handler_function;
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ return &sp_handler_package_spec;
+ case SQLCOM_CREATE_PACKAGE_BODY:
+ case SQLCOM_DROP_PACKAGE_BODY:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
+ return &sp_handler_package_body;
default:
break;
}
@@ -300,6 +496,10 @@ inline const Sp_handler *Sp_handler::handler(stored_procedure_type type)
return &sp_handler_procedure;
case TYPE_ENUM_FUNCTION:
return &sp_handler_function;
+ case TYPE_ENUM_PACKAGE:
+ return &sp_handler_package_spec;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return &sp_handler_package_body;
case TYPE_ENUM_TRIGGER:
return &sp_handler_trigger;
case TYPE_ENUM_PROXY:
@@ -316,6 +516,8 @@ inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type)
return &sp_handler_function;
case MDL_key::PROCEDURE:
return &sp_handler_procedure;
+ case MDL_key::PACKAGE_BODY:
+ return &sp_handler_package_body;
case MDL_key::GLOBAL:
case MDL_key::SCHEMA:
case MDL_key::TABLE:
@@ -425,12 +627,16 @@ public:
*/
ulong m_sp_cache_version;
+ const Sp_handler *m_handler;
+
int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const;
};
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
- const MDL_key *key, TABLE_LIST *belong_to_view);
+ const MDL_key *key,
+ const Sp_handler *handler,
+ TABLE_LIST *belong_to_view);
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx);
bool sp_update_sp_used_routines(HASH *dst, HASH *src);
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index cdb5e256e46..c088dc6ba12 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -192,6 +192,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_CREATE_USER:
@@ -211,11 +213,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_PROC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TABLE_STATUS:
@@ -260,6 +265,8 @@ sp_get_flags_for_command(LEX *lex)
break;
case SQLCOM_CREATE_INDEX:
case SQLCOM_CREATE_DB:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
@@ -276,6 +283,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_ROLE:
@@ -502,9 +511,10 @@ sp_head::operator delete(void *ptr, size_t size) throw()
}
-sp_head::sp_head(const Sp_handler *sph)
+sp_head::sp_head(sp_package *parent, const Sp_handler *sph)
:Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
Database_qualified_name(&null_clex_str, &null_clex_str),
+ m_parent(parent),
m_handler(sph),
m_flags(0),
m_tmp_query(NULL),
@@ -529,6 +539,9 @@ sp_head::sp_head(const Sp_handler *sph)
m_param_begin(NULL),
m_param_end(NULL),
m_body_begin(NULL),
+ m_thd_root(NULL),
+ m_thd(NULL),
+ m_pcont(new (&main_mem_root) sp_pcontext()),
m_cont_level(0)
{
m_first_instance= this;
@@ -543,6 +556,7 @@ sp_head::sp_head(const Sp_handler *sph)
m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key,
0, 0);
@@ -551,12 +565,169 @@ sp_head::sp_head(const Sp_handler *sph)
}
+sp_package::sp_package(LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph)
+ :sp_head(NULL, sph),
+ m_current_routine(NULL),
+ m_top_level_lex(top_level_lex),
+ m_rcontext(NULL),
+ m_invoked_subroutine_count(0),
+ m_is_instantiated(false),
+ m_is_cloning_routine(false)
+{
+ init_sp_name(name);
+}
+
+
+sp_package::~sp_package()
+{
+ m_routine_implementations.cleanup();
+ m_routine_declarations.cleanup();
+ m_body= null_clex_str;
+ if (m_current_routine)
+ delete m_current_routine->sphead;
+ delete m_rcontext;
+}
+
+
+/*
+ Test if two routines have equal specifications
+*/
+bool sp_head::eq_routine_spec(const sp_head *sp) const
+{
+ // TODO: Add tests for equal return data types (in case of FUNCTION)
+ // TODO: Add tests for equal argument data types
+ return
+ m_handler->type() == sp->m_handler->type() &&
+ m_pcont->context_var_count() == sp->m_pcont->context_var_count();
+}
+
+
+bool sp_package::validate_after_parser(THD *thd)
+{
+ if (m_handler->type() != TYPE_ENUM_PACKAGE_BODY)
+ return false;
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, this);
+ sp_package *spec= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(spec); // CREATE PACKAGE must already be cached
+ return validate_public_routines(thd, spec) ||
+ validate_private_routines(thd);
+}
+
+
+bool sp_package::validate_public_routines(THD *thd, sp_package *spec)
+{
+ /*
+ Check that all routines declared in CREATE PACKAGE
+ have implementations in CREATE PACKAGE BODY.
+ */
+ List_iterator<LEX> it(spec->m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool sp_package::validate_private_routines(THD *thd)
+{
+ /*
+ Check that all forwad declarations in
+ CREATE PACKAGE BODY have implementations.
+ */
+ List_iterator<LEX> it(m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+LEX *sp_package::LexList::find(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ const char *dot;
+ if (lex->sphead->m_handler->type() == type &&
+ (dot= strrchr(lex->sphead->m_name.str, '.')))
+ {
+ size_t ofs= dot + 1 - lex->sphead->m_name.str;
+ LEX_CSTRING non_qualified_sphead_name= lex->sphead->m_name;
+ non_qualified_sphead_name.str+= ofs;
+ non_qualified_sphead_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(non_qualified_sphead_name, name))
+ return lex;
+ }
+ }
+ return NULL;
+}
+
+
+LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->m_handler->type() == type &&
+ Sp_handler::eq_routine_name(lex->sphead->m_name, name))
+ return lex;
+ }
+ return NULL;
+}
+
+
void
sp_head::init(LEX *lex)
{
DBUG_ENTER("sp_head::init");
- lex->spcont= m_pcont= new sp_pcontext();
+ lex->spcont= m_pcont;
if (!lex->spcont)
DBUG_VOID_RETURN;
@@ -566,7 +737,6 @@ sp_head::init(LEX *lex)
types of stored procedures to simplify reset_lex()/restore_lex() code.
*/
lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
DBUG_VOID_RETURN;
}
@@ -584,9 +754,6 @@ sp_head::init_sp_name(const sp_name *spname)
/* We have to copy strings to get them into the right memroot. */
Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_explicit_name= spname->m_explicit_name;
-
- spname->make_qname(&main_mem_root, &m_qname);
-
DBUG_VOID_RETURN;
}
@@ -681,6 +848,17 @@ sp_head::~sp_head()
}
+void sp_package::LexList::cleanup()
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ lex_end(lex);
+ delete lex;
+ }
+}
+
+
/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
@@ -992,6 +1170,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count++;
m_first_instance->m_first_free_instance= m_next_cached_sp;
if (m_next_cached_sp)
{
@@ -1312,6 +1492,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status|= mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
}
m_flags&= ~IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count--;
DBUG_PRINT("info",
("first free for %p --: %p->%p, level: %lu, flags %x",
m_first_instance,
@@ -1385,8 +1567,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
statement that 'may' affect this.
*/
if (*save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- &sp->m_db, &sp->m_name, sp->m_handler, false))
+ sp->check_execute_access(thd))
{
sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
@@ -1400,9 +1581,10 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
bool sp_head::check_execute_access(THD *thd) const
{
- return check_routine_access(thd, EXECUTE_ACL,
- &m_db, &m_name,
- m_handler, false);
+ return m_parent ? m_parent->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL,
+ &m_db, &m_name,
+ m_handler, false);
}
@@ -1593,6 +1775,42 @@ err_with_cleanup:
}
+/*
+ Execute the package initialization section.
+*/
+bool sp_package::instantiate_if_needed(THD *thd)
+{
+ List<Item> args;
+ if (m_is_instantiated)
+ return false;
+ /*
+ Set m_is_instantiated to true early, to avoid recursion in case if
+ the package initialization section calls routines from the same package.
+ */
+ m_is_instantiated= true;
+ /*
+ Check that the initialization section doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (is_not_allowed_in_function(where))
+ goto err;
+ }
+
+ args.elements= 0;
+ if (execute_procedure(thd, &args))
+ goto err;
+ return false;
+err:
+ m_is_instantiated= false;
+ return true;
+}
+
+
/**
Execute a function.
@@ -1642,6 +1860,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
/*
Check that the function is called with all specified arguments.
@@ -1869,9 +2090,13 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
bool save_log_general= false;
+ sp_package *pkg= get_package();
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
if (args->elements != params)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
@@ -1895,11 +2120,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd;
}
- if (!(nctx= rcontext_create(thd, NULL, args)))
+ if (!pkg)
{
- delete nctx; /* Delete nctx if it was init() that failed. */
- thd->spcont= save_spcont;
- DBUG_RETURN(TRUE);
+ if (!(nctx= rcontext_create(thd, NULL, args)))
+ {
+ delete nctx; /* Delete nctx if it was init() that failed. */
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ if (!pkg->m_rcontext)
+ {
+ Query_arena backup_arena;
+ thd->set_n_backup_active_arena(this, &backup_arena);
+ nctx= pkg->rcontext_create(thd, NULL, args);
+ thd->restore_active_arena(this, &backup_arena);
+ if (!nctx)
+ {
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ pkg->m_rcontext= nctx;
+ }
+ else
+ nctx= pkg->m_rcontext;
}
if (params > 0)
@@ -2105,7 +2351,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!save_spcont)
delete octx;
- delete nctx;
+ if (!pkg)
+ delete nctx;
thd->spcont= save_spcont;
thd->utime_after_lock= utime_before_sp_exec;
@@ -3366,10 +3613,16 @@ sp_instr_set::execute(THD *thd, uint *nextp)
}
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
+{
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
+
+
int
sp_instr_set::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable(thd, m_offset, &m_value);
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
delete_explain_query(thd->lex);
*nextp = m_ip+1;
return res;
@@ -3381,13 +3634,15 @@ sp_instr_set::print(String *str)
/* set name@offset ... */
size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
sp_variable *var = m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
/* 'var' should always be non-null, but just in case... */
if (var)
- rsrv+= var->name.length;
+ rsrv+= var->name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix->str, prefix->length);
if (var)
{
str->qs_append(&var->name);
@@ -3407,8 +3662,9 @@ sp_instr_set::print(String *str)
int
sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field(thd, m_offset, m_field_offset,
- &m_value);
+ int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
+ m_field_offset,
+ &m_value);
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
@@ -3421,16 +3677,18 @@ sp_instr_set_row_field::print(String *str)
/* set name@offset[field_offset] ... */
size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_row());
const Column_definition *def=
var->field_def.row_field_definitions()->elem(m_field_offset);
DBUG_ASSERT(def);
- rsrv+= var->name.length + def->field_name.length;
+ rsrv+= var->name.length + def->field_name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
str->qs_append(&var->name);
str->qs_append('.');
str->qs_append(&def->field_name);
@@ -3452,9 +3710,9 @@ sp_instr_set_row_field::print(String *str)
int
sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field_by_name(thd, m_offset,
- m_field_name,
- &m_value);
+ int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
+ m_field_name,
+ &m_value);
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
@@ -3467,14 +3725,16 @@ sp_instr_set_row_field_by_name::print(String *str)
/* set name.field@offset["field"] ... */
size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
var->field_def.is_cursor_rowtype_ref());
- rsrv+= var->name.length + 2 * m_field_name.length;
+ rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
str->qs_append(&var->name);
str->qs_append('.');
str->qs_append(&m_field_name);
@@ -4639,6 +4899,7 @@ Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
bool
sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, Item *val, LEX *lex,
bool responsible_to_free_lex)
{
@@ -4646,7 +4907,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
return true;
sp_instr_set *sp_set= new (thd->mem_root)
- sp_instr_set(instructions(), spcont,
+ sp_instr_set(instructions(), spcont, rh,
spv->offset, val, lex,
responsible_to_free_lex);
@@ -4659,6 +4920,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
*/
bool
sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex)
{
@@ -4667,7 +4929,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field *sp_set= new (thd->mem_root)
sp_instr_set_row_field(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
field_idx, val,
lex, true);
@@ -4677,6 +4939,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
bool
sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv,
const LEX_CSTRING *field_name,
Item *val, LEX *lex)
@@ -4686,7 +4949,7 @@ sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
*field_name,
val,
@@ -4766,6 +5029,7 @@ sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
bool last= idx + 1 == parameters->argument_count();
sp_variable *spvar= param_spcont->get_context_variable(idx);
if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
spvar, parameters->arguments()[idx],
param_lex, last))
return true;
@@ -4837,3 +5101,44 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
return false;
}
+
+
+/*
+ In Oracle mode stored routines have an optional name
+ at the end of a declaration:
+ PROCEDURE p1 AS
+ BEGIN
+ NULL
+ END p1;
+ Check that the first p1 and the last p1 match.
+*/
+bool sp_head::check_package_routine_end_name(const LEX_CSTRING &end_name) const
+{
+ LEX_CSTRING non_qualified_name= m_name;
+ const char *errpos;
+ size_t ofs;
+ if (!end_name.length)
+ return false; // No end name
+ if (!(errpos= strrchr(m_name.str, '.')))
+ {
+ errpos= m_name.str;
+ goto err;
+ }
+ errpos++;
+ ofs= errpos - m_name.str;
+ non_qualified_name.str+= ofs;
+ non_qualified_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(end_name, non_qualified_name))
+ return false;
+err:
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), end_name.str, errpos);
+ return true;
+}
+
+
+ulong sp_head::sp_cache_version() const
+{
+ return m_parent ? m_parent->sp_cache_version() :
+ m_sp_cache_version;
+
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 7f041058829..4cef33a76a7 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -132,6 +132,7 @@ class sp_head :private Query_arena,
sp_head(const sp_head &); /**< Prevent use of these */
void operator=(sp_head &);
+protected:
MEM_ROOT main_mem_root;
public:
/** Possible values of m_flags */
@@ -170,6 +171,7 @@ public:
HAS_AGGREGATE_INSTR= 16384
};
+ sp_package *m_parent;
const Sp_handler *m_handler;
uint m_flags; // Boolean attributes of a stored routine
@@ -204,13 +206,13 @@ public:
/**
Is this routine being executed?
*/
- bool is_invoked() const { return m_flags & IS_INVOKED; }
+ virtual bool is_invoked() const { return m_flags & IS_INVOKED; }
/**
Get the value of the SP cache version, as remembered
when the routine was inserted into the cache.
*/
- ulong sp_cache_version() const { return m_sp_cache_version; }
+ ulong sp_cache_version() const;
/** Set the value of the SP cache version. */
void set_sp_cache_version(ulong version_arg) const
@@ -224,6 +226,7 @@ public:
sp_rcontext *rcontext_create(THD *thd, Field *retval,
Row_definition_list *list,
bool switch_security_ctx);
+ bool eq_routine_spec(const sp_head *) const;
private:
/**
Version of the stored routine cache at the moment when the
@@ -319,7 +322,7 @@ public:
static void
operator delete(void *ptr, size_t size) throw ();
- sp_head(const Sp_handler *handler);
+ sp_head(sp_package *parent, const Sp_handler *handler);
/// Initialize after we have reset mem_root
void
@@ -399,15 +402,19 @@ public:
@retval false - on success
*/
bool set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, Item *val, LEX *lex,
bool responsible_to_free_lex);
bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex);
bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
sp_variable *spv,
const LEX_CSTRING *field_name,
Item *val, LEX *lex);
+ bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
private:
/**
Generate a code to set a single cursor parameter variable.
@@ -430,7 +437,9 @@ private:
*/
DBUG_ASSERT(m_thd->free_list == NULL);
m_thd->free_list= prm->get_free_list();
- if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm, true))
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, prm->get_item(), prm, true))
return true;
/*
Safety:
@@ -830,9 +839,19 @@ public:
sp_pcontext *get_parse_context() { return m_pcont; }
+ /*
+ Check EXECUTE access:
+ - in case of a standalone rotuine, for the routine itself
+ - in case of a package routine, for the owner package body
+ */
bool check_execute_access(THD *thd) const;
-private:
+ virtual sp_package *get_package()
+ {
+ return NULL;
+ }
+
+protected:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
THD *m_thd; ///< Set if we have reset mem_root
@@ -897,6 +916,92 @@ private:
}; // class sp_head : public Sql_alloc
+class sp_package: public sp_head
+{
+ bool validate_public_routines(THD *thd, sp_package *spec);
+ bool validate_private_routines(THD *thd);
+public:
+ class LexList: public List<LEX>
+ {
+ public:
+ LexList() { elements= 0; }
+ // Find a package routine by a non qualified name
+ LEX *find(const LEX_CSTRING &name, stored_procedure_type type);
+ // Find a package routine by a package-qualified name, e.g. 'pkg.proc'
+ LEX *find_qualified(const LEX_CSTRING &name, stored_procedure_type type);
+ // Check if a routine with the given qualified name already exists
+ bool check_dup_qualified(const LEX_CSTRING &name, const Sp_handler *sph)
+ {
+ if (!find_qualified(name, sph->type()))
+ return false;
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), sph->type_str(), name.str);
+ return true;
+ }
+ bool check_dup_qualified(const sp_head *sp)
+ {
+ return check_dup_qualified(sp->m_name, sp->m_handler);
+ }
+ void cleanup();
+ };
+ /*
+ The LEX for a new package subroutine is initially assigned to
+ m_current_routine. After scanning parameters, return type and chistics,
+ the parser detects if we have a declaration or a definition, e.g.:
+ PROCEDURE p1(a INT);
+ vs
+ PROCEDURE p1(a INT) AS BEGIN NULL; END;
+ (i.e. either semicolon or the "AS" keyword)
+ m_current_routine is then added either to m_routine_implementations,
+ or m_routine_declarations, and then m_current_routine is set to NULL.
+ */
+ LEX *m_current_routine;
+ LexList m_routine_implementations;
+ LexList m_routine_declarations;
+
+ LEX *m_top_level_lex;
+ sp_rcontext *m_rcontext;
+ uint m_invoked_subroutine_count;
+ bool m_is_instantiated;
+ bool m_is_cloning_routine;
+
+ sp_package(LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph);
+ ~sp_package();
+ bool add_routine_declaration(LEX *lex)
+ {
+ return m_routine_declarations.check_dup_qualified(lex->sphead) ||
+ m_routine_declarations.push_back(lex, &main_mem_root);
+ }
+ bool add_routine_implementation(LEX *lex)
+ {
+ return m_routine_implementations.check_dup_qualified(lex->sphead) ||
+ m_routine_implementations.push_back(lex, &main_mem_root);
+ }
+ sp_package *get_package() { return this; }
+ bool is_invoked() const
+ {
+ /*
+ Cannot flush a package out of the SP cache when:
+ - its initialization block is running
+ - one of its subroutine is running
+ */
+ return sp_head::is_invoked() || m_invoked_subroutine_count > 0;
+ }
+ sp_variable *find_package_variable(const LEX_CSTRING *name) const
+ {
+ /*
+ sp_head::m_pcont is a special level for routine parameters.
+ Variables declared inside CREATE PACKAGE BODY reside in m_children.at(0).
+ */
+ sp_pcontext *ctx= m_pcont->child_context(0);
+ return ctx ? ctx->find_variable(name, true) : NULL;
+ }
+ bool validate_after_parser(THD *thd);
+ bool instantiate_if_needed(THD *thd);
+};
+
+
class sp_lex_cursor: public sp_lex_local, public Query_arena
{
LEX_CSTRING m_cursor_name;
@@ -1185,9 +1290,11 @@ class sp_instr_set : public sp_instr
public:
sp_instr_set(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, Item *val,
LEX *lex, bool lex_resp)
- : sp_instr(ip, ctx), m_offset(offset), m_value(val),
+ : sp_instr(ip, ctx),
+ m_rcontext_handler(rh), m_offset(offset), m_value(val),
m_lex_keeper(lex, lex_resp)
{}
@@ -1201,11 +1308,11 @@ public:
virtual void print(String *str);
protected:
-
+ sp_rcontext *get_rcontext(THD *thd) const;
+ const Sp_rcontext_handler *m_rcontext_handler;
uint m_offset; ///< Frame offset
Item *m_value;
sp_lex_keeper m_lex_keeper;
-
}; // class sp_instr_set : public sp_instr
@@ -1223,10 +1330,11 @@ class sp_instr_set_row_field : public sp_instr_set
public:
sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, uint field_offset,
Item *val,
LEX *lex, bool lex_resp)
- : sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
m_field_offset(field_offset)
{}
@@ -1265,10 +1373,11 @@ class sp_instr_set_row_field_by_name : public sp_instr_set
public:
sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx,
+ const Sp_rcontext_handler *rh,
uint offset, const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp)
- : sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
+ : sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp),
m_field_name(field_name)
{}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 6a6920d89e1..e30af3fcde5 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -394,6 +394,9 @@ public:
sp_pcontext *parent_context() const
{ return m_parent; }
+ sp_pcontext *child_context(uint i) const
+ { return i < m_children.elements() ? m_children.at(i) : NULL; }
+
/// Calculate and return the number of handlers to pop between the given
/// context and this one.
///
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 4009f8dce30..2e9ae23d7f9 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -30,6 +30,33 @@
#include "sql_acl.h" // SELECT_ACL
#include "sql_parse.h" // check_table_access
+
+Sp_rcontext_handler_local sp_rcontext_handler_local;
+Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
+
+sp_rcontext *Sp_rcontext_handler_local::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx;
+}
+
+sp_rcontext *Sp_rcontext_handler_package_body::get_rcontext(sp_rcontext *ctx) const
+{
+ return ctx->m_sp->m_parent->m_rcontext;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_local::get_name_prefix() const
+{
+ return &empty_clex_str;
+}
+
+const LEX_CSTRING *Sp_rcontext_handler_package_body::get_name_prefix() const
+{
+ static const LEX_CSTRING sp_package_body_variable_prefix_clex_str=
+ {C_STRING_WITH_LEN("PACKAGE_BODY.")};
+ return &sp_package_body_variable_prefix_clex_str;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// sp_rcontext implementation.
///////////////////////////////////////////////////////////////////////////
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index c4f4cf182da..33d76e1c85a 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -183,7 +183,8 @@ public:
uint instr_ptr;
/// The stored program for which this runtime context is created. Used for
- /// checking if correct runtime context is used for variable handling.
+ /// checking if correct runtime context is used for variable handling,
+ /// and to access the package run-time context.
/// Also used by slow log.
const sp_head *m_sp;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 96ed36da755..951471bca29 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -607,6 +607,7 @@ static MEM_ROOT acl_memroot, grant_memroot;
static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
+static HASH package_spec_priv_hash, package_body_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
static Hash_filo<acl_entry> *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
@@ -650,6 +651,18 @@ HASH *Sp_handler_function::get_priv_hash() const
}
+HASH *Sp_handler_package_spec::get_priv_hash() const
+{
+ return &package_spec_priv_hash;
+}
+
+
+HASH *Sp_handler_package_body::get_priv_hash() const
+{
+ return &package_body_priv_hash;
+}
+
+
/*
Enumeration of ACL/GRANT tables in the mysql database
*/
@@ -1312,6 +1325,8 @@ enum enum_acl_lists
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
FUNC_PRIVILEGES_HASH,
+ PACKAGE_SPEC_PRIVILEGES_HASH,
+ PACKAGE_BODY_PRIVILEGES_HASH,
PROXY_USERS_ACL,
ROLES_MAPPINGS_HASH
};
@@ -5401,7 +5416,10 @@ table_error:
******************************************************************/
struct PRIVS_TO_MERGE
{
- enum what { ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC } what;
+ enum what
+ {
+ ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
+ } what;
const char *db, *name;
};
@@ -5413,6 +5431,10 @@ static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
return PRIVS_TO_MERGE::FUNC;
case TYPE_ENUM_PROCEDURE:
return PRIVS_TO_MERGE::PROC;
+ case TYPE_ENUM_PACKAGE:
+ return PRIVS_TO_MERGE::PACKAGE_SPEC;
+ case TYPE_ENUM_PACKAGE_BODY:
+ return PRIVS_TO_MERGE::PACKAGE_BODY;
case TYPE_ENUM_TRIGGER:
case TYPE_ENUM_PROXY:
break;
@@ -6231,7 +6253,14 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
if (all || data->what == PRIVS_TO_MERGE::FUNC)
changed|= merge_role_routine_grant_privileges(grantee,
data->db, data->name, &role_hash, &func_priv_hash);
-
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_spec_priv_hash);
+ if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash,
+ &package_body_priv_hash);
return !changed; // don't recurse into the subgraph if privs didn't change
}
@@ -7116,6 +7145,8 @@ void grant_free(void)
my_hash_free(&column_priv_hash);
my_hash_free(&proc_priv_hash);
my_hash_free(&func_priv_hash);
+ my_hash_free(&package_spec_priv_hash);
+ my_hash_free(&package_body_priv_hash);
free_root(&grant_memroot,MYF(0));
DBUG_VOID_RETURN;
}
@@ -7182,6 +7213,10 @@ static bool grant_load(THD *thd,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
t_table= tables_priv.table();
@@ -7331,6 +7366,7 @@ static my_bool propagate_role_grants_action(void *role_ptr,
bool grant_reload(THD *thd)
{
HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
+ HASH old_package_spec_priv_hash, old_package_body_priv_hash;
MEM_ROOT old_mem;
int result;
DBUG_ENTER("grant_reload");
@@ -7350,6 +7386,8 @@ bool grant_reload(THD *thd)
old_column_priv_hash= column_priv_hash;
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash;
+ old_package_spec_priv_hash= package_spec_priv_hash;
+ old_package_body_priv_hash= package_body_priv_hash;
/*
Create a new memory pool but save the current memory pool to make an undo
@@ -7367,6 +7405,8 @@ bool grant_reload(THD *thd)
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
+ package_spec_priv_hash= old_package_spec_priv_hash;
+ package_body_priv_hash= old_package_body_priv_hash;
grant_memroot= old_mem; /* purecov: deadcode */
}
else
@@ -7374,6 +7414,8 @@ bool grant_reload(THD *thd)
my_hash_free(&old_column_priv_hash);
my_hash_free(&old_proc_priv_hash);
my_hash_free(&old_func_priv_hash);
+ my_hash_free(&old_package_spec_priv_hash);
+ my_hash_free(&old_package_body_priv_hash);
free_root(&old_mem,MYF(0));
}
@@ -8002,7 +8044,9 @@ bool check_grant_db(THD *thd, const char *db)
if (error)
error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
- check_grant_db_routine(thd, db, &func_priv_hash);
+ check_grant_db_routine(thd, db, &func_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
+ check_grant_db_routine(thd, db, &package_body_priv_hash);
mysql_rwlock_unlock(&LOCK_grant);
@@ -8399,6 +8443,14 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
buff, sizeof(buff)))
return TRUE;
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
+ buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
+ buff, sizeof(buff)))
+ return TRUE;
+
return FALSE;
}
@@ -8624,6 +8676,14 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
buff, sizeof(buff)))
goto end;
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
+ buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
+ buff, sizeof(buff)))
+ goto end;
+
if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
goto end;
}
@@ -9559,6 +9619,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
grant_name_hash= &func_priv_hash;
elements= grant_name_hash->records;
break;
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ grant_name_hash= &package_spec_priv_hash;
+ elements= grant_name_hash->records;
+ break;
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ grant_name_hash= &package_body_priv_hash;
+ elements= grant_name_hash->records;
+ break;
case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
@@ -9597,6 +9665,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
@@ -9679,6 +9749,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
my_hash_delete(grant_name_hash, (uchar*) grant_name);
/*
In our HASH implementation on deletion one elements
@@ -9724,6 +9796,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case COLUMN_PRIVILEGES_HASH:
case PROC_PRIVILEGES_HASH:
case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
{
/*
Save old hash key and its length to be able to properly update
@@ -9906,6 +9980,26 @@ static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
if (search_only)
goto end;
}
+ /* Handle package spec array. */
+ if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
+ /* Handle package body array. */
+ if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
+ drop, user_from, user_to) || found)
+ && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (search_only)
+ goto end;
+ }
}
/* Handle tables table. */
@@ -10632,7 +10726,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
/* Remove procedure access */
if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
- mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user))
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
+ mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
result= -1;
ACL_USER_BASE *user_or_role;
@@ -11177,6 +11273,8 @@ SHOW_VAR acl_statistics[] = {
{"database_grants", (char*)&acl_dbs.elements, SHOW_UINT},
{"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
{"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
+ {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
+ {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
{"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
{"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
{"roles", (char*)&acl_roles.records, SHOW_ULONG},
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 902933ffed8..e7be6ed4e90 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3169,6 +3169,17 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
switch (mdl_type)
{
+ case MDL_key::PACKAGE_BODY:
+ DBUG_ASSERT(rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first);
+ /*
+ No need to cache the package body itself.
+ It gets cached during open_and_process_routine()
+ for the first used package routine. See the package related code
+ in the "case" below.
+ */
+ if (sp_acquire_mdl(thd, rt, ot_ctx))
+ DBUG_RETURN(TRUE);
+ break;
case MDL_key::FUNCTION:
case MDL_key::PROCEDURE:
{
@@ -3183,11 +3194,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
mdl_type != MDL_key::PROCEDURE)
{
+ /*
+ TODO: If this is a package routine, we should not put MDL
+ TODO: on the routine itself. We should put only the package MDL.
+ */
if (sp_acquire_mdl(thd, rt, ot_ctx))
DBUG_RETURN(TRUE);
- DEBUG_SYNC(thd, "after_shared_lock_pname");
-
/* Ensures the routine is up-to-date and cached, if exists. */
if (rt->sp_cache_routine(thd, has_prelocking_list, &sp))
DBUG_RETURN(TRUE);
@@ -3202,8 +3215,24 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
*routine_modifies_data= sp->modifies_data();
if (!has_prelocking_list)
+ {
prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
need_prelocking);
+ if (sp->m_parent)
+ {
+ /*
+ If it's a package routine, we need also to handle the
+ package body, as its initialization section can use
+ some tables and routine calls.
+ TODO: Only package public routines actually need this.
+ TODO: Skip package body handling for private routines.
+ */
+ *routine_modifies_data|= sp->m_parent->modifies_data();
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt,
+ sp->m_parent,
+ need_prelocking);
+ }
+ }
}
}
else
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c696ee897f2..540777ea605 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -913,6 +913,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock)
sp_proc_cache= NULL;
sp_func_cache= NULL;
+ sp_package_spec_cache= NULL;
+ sp_package_body_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -1478,6 +1480,8 @@ void THD::change_user(void)
(my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
}
/**
@@ -1604,6 +1608,8 @@ void THD::cleanup(void)
my_hash_free(&sequences);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
auto_inc_intervals_forced.empty();
auto_inc_intervals_in_cur_stmt_for_binlog.empty();
@@ -3732,7 +3738,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
mvsp->type_handler() == &type_handler_row)
{
// SELECT INTO row_type_sp_variable
- if (thd->spcont->get_variable(mvsp->offset)->cols() != list.elements)
+ if (mvsp->get_rcontext(thd->spcont)->get_variable(mvsp->offset)->cols() !=
+ list.elements)
goto error;
m_var_sp_row= mvsp;
return 0;
@@ -4076,14 +4083,22 @@ bool my_var_user::set(THD *thd, Item *item)
return suv->fix_fields(thd, 0) || suv->update();
}
+
+sp_rcontext *my_var_sp::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
bool my_var_sp::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable(thd, offset, &item);
+ return get_rcontext(thd->spcont)->set_variable(thd, offset, &item);
}
bool my_var_sp_row_field::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable_row_field(thd, offset, m_field_offset, &item);
+ return get_rcontext(thd->spcont)->
+ set_variable_row_field(thd, offset, m_field_offset, &item);
}
@@ -4118,7 +4133,8 @@ int select_dumpvar::send_data(List<Item> &items)
DBUG_RETURN(1);
}
if (m_var_sp_row ?
- thd->spcont->set_variable_row(thd, m_var_sp_row->offset, items) :
+ m_var_sp_row->get_rcontext(thd->spcont)->
+ set_variable_row(thd, m_var_sp_row->offset, items) :
send_data_to_var_list(items))
DBUG_RETURN(1);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7e06dcc7d39..a43f4dec0c9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3166,6 +3166,8 @@ public:
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
+ sp_cache *sp_package_spec_cache;
+ sp_cache *sp_package_body_cache;
/** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
uint query_name_consts;
@@ -5902,6 +5904,7 @@ public:
};
class my_var_sp: public my_var {
+ const Sp_rcontext_handler *m_rcontext_handler;
const Type_handler *m_type_handler;
public:
uint offset;
@@ -5910,13 +5913,17 @@ public:
runtime context is used for variable handling.
*/
sp_head *sp;
- my_var_sp(const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
+ my_var_sp(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
sp_head *s)
- : my_var(j, LOCAL_VAR), m_type_handler(type_handler), offset(o), sp(s) { }
+ : my_var(j, LOCAL_VAR),
+ m_rcontext_handler(rcontext_handler),
+ m_type_handler(type_handler), offset(o), sp(s) { }
~my_var_sp() { }
bool set(THD *thd, Item *val);
my_var_sp *get_my_var_sp() { return this; }
const Type_handler *type_handler() const { return m_type_handler; }
+ sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
};
/*
@@ -5927,9 +5934,11 @@ class my_var_sp_row_field: public my_var_sp
{
uint m_field_offset;
public:
- my_var_sp_row_field(const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
+ my_var_sp_row_field(const Sp_rcontext_handler *rcontext_handler,
+ const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
uint var_idx, uint field_idx, sp_head *s)
- :my_var_sp(varname, var_idx, &type_handler_double/*Not really used*/, s),
+ :my_var_sp(rcontext_handler, varname, var_idx,
+ &type_handler_double/*Not really used*/, s),
m_field_offset(field_idx)
{ }
bool set(THD *thd, Item *val);
@@ -6304,6 +6313,9 @@ public:
Database_qualified_name(const LEX_CSTRING *db, const LEX_CSTRING *name)
:m_db(*db), m_name(*name)
{ }
+ Database_qualified_name(const LEX_CSTRING &db, const LEX_CSTRING &name)
+ :m_db(db), m_name(name)
+ { }
Database_qualified_name(const char *db, size_t db_length,
const char *name, size_t name_length)
{
@@ -6353,6 +6365,34 @@ public:
DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str));
return false;
}
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ char *tmp;
+ size_t length= package.length + 1 + routine.length + 1;
+ if (!(tmp= (char *) alloc_root(mem_root, length)))
+ return true;
+ m_name.length= my_snprintf(tmp, length, "%.*s.%.*s",
+ (int) package.length, package.str,
+ (int) routine.length, routine.str);
+ m_name.str= tmp;
+ return false;
+ }
+
+ bool make_package_routine_name(MEM_ROOT *mem_root,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &package,
+ const LEX_CSTRING &routine)
+ {
+ if (make_package_routine_name(mem_root, package, routine))
+ return true;
+ if (!(m_db.str= strmake_root(mem_root, db.str, db.length)))
+ return true;
+ m_db.length= db.length;
+ return false;
+ }
};
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index d95b1c828b9..1c4c89eb132 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -99,6 +99,15 @@ enum enum_sql_command {
SQLCOM_CREATE_SEQUENCE,
SQLCOM_DROP_SEQUENCE,
SQLCOM_ALTER_SEQUENCE,
+ SQLCOM_CREATE_PACKAGE,
+ SQLCOM_DROP_PACKAGE,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ SQLCOM_DROP_PACKAGE_BODY,
+ SQLCOM_SHOW_CREATE_PACKAGE,
+ SQLCOM_SHOW_CREATE_PACKAGE_BODY,
+ SQLCOM_SHOW_STATUS_PACKAGE,
+ SQLCOM_SHOW_STATUS_PACKAGE_BODY,
+ SQLCOM_SHOW_PACKAGE_BODY_CODE,
/*
When a command is added here, be sure it's also added in mysqld.cc
@@ -175,8 +184,10 @@ class Sql_cmd_call : public Sql_cmd
{
public:
class sp_name *m_name;
- Sql_cmd_call(class sp_name *name)
- :m_name(name)
+ const class Sp_handler *m_handler;
+ Sql_cmd_call(class sp_name *name, const class Sp_handler *handler)
+ :m_name(name),
+ m_handler(handler)
{}
virtual ~Sql_cmd_call()
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index a2a9e5b2f16..b169b9e0b27 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2964,9 +2964,28 @@ void LEX::cleanup_lex_after_parse_error(THD *thd)
*/
if (thd->lex->sphead)
{
+ sp_package *pkg;
thd->lex->sphead->restore_thd_mem_root(thd);
- delete thd->lex->sphead;
- thd->lex->sphead= NULL;
+ if ((pkg= thd->lex->sphead->m_parent))
+ {
+ /*
+ If a syntax error happened inside a package routine definition,
+ then thd->lex points to the routine sublex. We need to restore to
+ the top level LEX.
+ */
+ DBUG_ASSERT(pkg->m_top_level_lex);
+ DBUG_ASSERT(pkg == pkg->m_top_level_lex->sphead);
+ pkg->restore_thd_mem_root(thd);
+ LEX *top= pkg->m_top_level_lex;
+ delete pkg;
+ thd->lex= top;
+ thd->lex->sphead= NULL;
+ }
+ else
+ {
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
}
}
@@ -5198,13 +5217,50 @@ void LEX::set_stmt_init()
};
+/**
+ Find a local or a package body variable by name.
+ @param IN name - the variable name
+ @param OUT ctx - NULL, if the variable was not found,
+ or LEX::spcont (if a local variable was found)
+ or the package top level context
+ (if a package variable was found)
+ @param OUT handler - NULL, if the variable was not found,
+ or a pointer to rcontext handler
+ @retval - the variable (if found), or NULL otherwise.
+*/
+sp_variable *
+LEX::find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const
+{
+ sp_variable *spv;
+ if (spcont && (spv= spcont->find_variable(name, false)))
+ {
+ *ctx= spcont;
+ *rh= &sp_rcontext_handler_local;
+ return spv;
+ }
+ sp_package *pkg= sphead ? sphead->m_parent : NULL;
+ if (pkg && (spv= pkg->find_package_variable(name)))
+ {
+ *ctx= pkg->get_parse_context()->child_context(0);
+ *rh= &sp_rcontext_handler_package_body;
+ return spv;
+ }
+ *ctx= NULL;
+ *rh= NULL;
+ return NULL;
+}
+
+
bool LEX::init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *name)
{
sp_variable *spv;
+ const Sp_rcontext_handler *rh;
/* Best effort lookup for system variable. */
- if (!spcont || !(spv = spcont->find_variable(name, false)))
+ if (!(spv= find_variable(name, &rh)))
{
struct sys_var_with_base tmp= {NULL, *name};
@@ -5323,7 +5379,8 @@ bool LEX::sp_variable_declarations_set_default(THD *thd, int nvars,
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
- spcont, spvar->offset, dflt_value_item,
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, dflt_value_item,
this, last);
if (is == NULL || sphead->add_instr(is))
return true;
@@ -5619,7 +5676,8 @@ sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name,
spvar->default_value= value;
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
- spcont, spvar->offset, value,
+ spcont, &sp_rcontext_handler_local,
+ spvar->offset, value,
this, true);
if (is == NULL || sphead->add_instr(is))
return NULL;
@@ -5697,7 +5755,8 @@ bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop)
{
sp_variable *src= i == 0 ? loop.m_index : loop.m_upper_bound;
args[i]= new (thd->mem_root)
- Item_splocal(thd, &src->name, src->offset, src->type_handler());
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &src->name, src->offset, src->type_handler());
if (args[i] == NULL)
return true;
#ifdef DBUG_ASSERT_EXISTS
@@ -5830,7 +5889,8 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
{
Item_splocal *splocal= new (thd->mem_root)
- Item_splocal(thd, &loop.m_index->name, loop.m_index->offset,
+ Item_splocal(thd, &sp_rcontext_handler_local,
+ &loop.m_index->name, loop.m_index->offset,
loop.m_index->type_handler());
if (splocal == NULL)
return true;
@@ -5842,7 +5902,8 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
return true;
Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
if (!expr ||
- sphead->set_local_variable(thd, spcont, loop.m_index, expr, this, true))
+ sphead->set_local_variable(thd, spcont, &sp_rcontext_handler_local,
+ loop.m_index, expr, this, true))
return true;
return false;
}
@@ -6042,6 +6103,31 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name)
}
+/**
+ When a package routine name is stored in memory in Database_qualified_name,
+ the dot character is used to delimit package name from the routine name,
+ e.g.:
+ m_db= 'test'; -- database 'test'
+ m_name= 'p1.p1'; -- package 'p1', routine 'p1'
+ See database_qualified_name::make_package_routine_name() for details.
+ Disallow package routine names with dots,
+ to avoid ambiguity when interpreting m_name='p1.p1.p1', between:
+ a. package 'p1.p1' + routine 'p1'
+ b. package 'p1' + routine 'p1.p1'
+ m_name='p1.p1.p1' will always mean (a).
+*/
+sp_name *LEX::make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name)
+{
+ sp_name *res= make_sp_name(thd, name);
+ if (res && strchr(res->m_name.str, '.'))
+ {
+ my_error(ER_SP_WRONG_NAME, MYF(0), res->m_name.str);
+ res= NULL;
+ }
+ return res;
+}
+
+
sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2)
{
@@ -6064,15 +6150,25 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
const Sp_handler *sph)
{
+ sp_package *package= get_sp_package();
sp_head *sp;
/* Order is important here: new - reset - init */
- if ((sp= new sp_head(sph)))
+ if ((sp= new sp_head(package, sph)))
{
sp->reset_thd_mem_root(thd);
sp->init(this);
if (name)
- sp->init_sp_name(name);
+ {
+ if (package)
+ sp->make_package_routine_name(sp->get_main_mem_root(),
+ package->m_db,
+ package->m_name,
+ name->m_name);
+ else
+ sp->init_sp_name(name);
+ sp->make_qname(sp->get_main_mem_root(), &sp->m_qname);
+ }
sphead= sp;
}
sp_chistics.init();
@@ -6080,6 +6176,31 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
}
+sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
+ const Sp_handler *sph)
+{
+ sp_package *package= thd->lex->get_sp_package();
+ /*
+ Sp_handler::sp_clone_and_link_routine() generates a standalone-alike
+ statement to clone package routines for recursion, e.g.:
+ CREATE PROCEDURE p1 AS BEGIN NULL; END;
+ Translate a standalone routine handler to the corresponding
+ package routine handler if we're cloning a package routine, e.g.:
+ sp_handler_procedure -> sp_handler_package_procedure
+ sp_handler_function -> sp_handler_package_function
+ */
+ if (package && package->m_is_cloning_routine)
+ sph= sph->package_routine_handler();
+ if (!sphead ||
+ (package &&
+ (sph == &sp_handler_package_procedure ||
+ sph == &sp_handler_package_function)))
+ return make_sp_head(thd, name, sph);
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+}
+
+
bool LEX::sp_body_finalize_procedure(THD *thd)
{
if (sphead->check_unresolved_goto())
@@ -6170,6 +6291,14 @@ LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd,
}
+bool LEX::sp_block_with_exceptions_add_empty(THD *thd)
+{
+ uint ip= sphead->instructions();
+ return sp_block_with_exceptions_finalize_executable_section(thd, ip) ||
+ sp_block_with_exceptions_finalize_exceptions(thd, ip, 0);
+}
+
+
bool LEX::sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive)
{
uint n;
@@ -6588,6 +6717,7 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *a,
const LEX_CSTRING *b,
sp_variable *spv,
@@ -6606,7 +6736,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
spv->field_def.is_cursor_rowtype_ref())
{
if (!(item= new (thd->mem_root)
- Item_splocal_row_field_by_name(thd, a, b, spv->offset,
+ Item_splocal_row_field_by_name(thd, rh, a, b, spv->offset,
&type_handler_null,
pos.pos(), pos.length())))
return NULL;
@@ -6619,7 +6749,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
return NULL;
if (!(item= new (thd->mem_root)
- Item_splocal_row_field(thd, a, b,
+ Item_splocal_row_field(thd, rh, a, b,
spv->offset, row_field_offset,
def->type_handler(),
pos.pos(), pos.length())))
@@ -6633,12 +6763,27 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
}
+my_var *LEX::create_outvar(THD *thd, const LEX_CSTRING *name)
+{
+ const Sp_rcontext_handler *rh;
+ sp_variable *spv;
+ if ((spv= find_variable(name, &rh)))
+ return result ? new (thd->mem_root)
+ my_var_sp(rh, name, spv->offset,
+ spv->type_handler(), sphead) :
+ NULL /* EXPLAIN */;
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str);
+ return NULL;
+}
+
+
my_var *LEX::create_outvar(THD *thd,
const LEX_CSTRING *a,
const LEX_CSTRING *b)
{
+ const Sp_rcontext_handler *rh;
sp_variable *t;
- if (!spcont || !(t= spcont->find_variable(a, false)))
+ if (!(t= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6647,9 +6792,9 @@ my_var *LEX::create_outvar(THD *thd,
if (!t->find_row_field(a, b, &row_field_offset))
return NULL;
return result ?
- new (thd->mem_root) my_var_sp_row_field(a, b, t->offset,
+ new (thd->mem_root) my_var_sp_row_field(rh, a, b, t->offset,
row_field_offset, sphead) :
- NULL;
+ NULL /* EXPLAIN */;
}
@@ -6719,12 +6864,13 @@ Item *LEX::create_item_ident(THD *thd,
const LEX_CSTRING *b,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (spcont && (spv= spcont->find_variable(a, false)) &&
+ if ((spv= find_variable(a, &rh)) &&
(spv->field_def.is_row() ||
spv->field_def.is_table_rowtype_ref() ||
spv->field_def.is_cursor_rowtype_ref()))
- return create_item_spvar_row_field(thd, a, b, spv, start, end);
+ return create_item_spvar_row_field(thd, rh, a, b, spv, start, end);
if ((thd->variables.sql_mode & MODE_ORACLE) && b->length == 7)
{
@@ -6780,8 +6926,9 @@ Item *LEX::create_item_limit(THD *thd,
const LEX_CSTRING *a,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (!spcont || !(spv= spcont->find_variable(a, false)))
+ if (!(spv= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6789,7 +6936,7 @@ Item *LEX::create_item_limit(THD *thd,
Query_fragment pos(thd, sphead, start, end);
Item_splocal *item;
- if (!(item= new (thd->mem_root) Item_splocal(thd, a,
+ if (!(item= new (thd->mem_root) Item_splocal(thd, rh, a,
spv->offset, spv->type_handler(),
pos.pos(), pos.length())))
return NULL;
@@ -6813,8 +6960,9 @@ Item *LEX::create_item_limit(THD *thd,
const LEX_CSTRING *b,
const char *start, const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
- if (!spcont || !(spv= spcont->find_variable(a, false)))
+ if (!(spv= find_variable(a, &rh)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
return NULL;
@@ -6822,7 +6970,7 @@ Item *LEX::create_item_limit(THD *thd,
// Qualified %TYPE variables are not possible
DBUG_ASSERT(!spv->field_def.column_type_ref());
Item_splocal *item;
- if (!(item= create_item_spvar_row_field(thd, a, b, spv, start, end)))
+ if (!(item= create_item_spvar_row_field(thd, rh, a, b, spv, start, end)))
return NULL;
if (item->type() != Item::INT_ITEM)
{
@@ -6868,10 +7016,13 @@ bool LEX::set_variable(struct sys_var_with_base *variable, Item *item)
was previously checked by init_internal_variable().
*/
DBUG_ASSERT(spcont);
- sp_variable *spv= spcont->find_variable(&variable->base_name, false);
+ const Sp_rcontext_handler *rh;
+ sp_pcontext *ctx;
+ sp_variable *spv= find_variable(&variable->base_name, &ctx, &rh);
DBUG_ASSERT(spv);
- /* It is a local variable. */
- return sphead->set_local_variable(thd, spcont, spv, item, this, true);
+ DBUG_ASSERT(ctx);
+ DBUG_ASSERT(rh);
+ return sphead->set_local_variable(thd, ctx, rh, spv, item, this, true);
}
@@ -6891,10 +7042,11 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name,
const char *start,
const char *end)
{
+ const Sp_rcontext_handler *rh;
sp_variable *spv;
DBUG_ASSERT(spcont);
DBUG_ASSERT(sphead);
- if ((spv= spcont->find_variable(name, false)))
+ if ((spv= find_variable(name, &rh)))
{
/* We're compiling a stored procedure and found a variable */
if (!parsing_options.allows_variable)
@@ -6905,11 +7057,11 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name,
Query_fragment pos(thd, sphead, start, end);
Item_splocal *splocal= spv->field_def.is_column_type_ref() ?
- new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, name,
+ new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, rh, name,
spv->offset,
pos.pos(),
pos.length()) :
- new (thd->mem_root) Item_splocal(thd, name,
+ new (thd->mem_root) Item_splocal(thd, rh, name,
spv->offset, spv->type_handler(),
pos.pos(), pos.length());
if (splocal == NULL)
@@ -6940,18 +7092,21 @@ bool LEX::set_variable(const LEX_CSTRING *name1,
const LEX_CSTRING *name2,
Item *item)
{
+ const Sp_rcontext_handler *rh;
+ sp_pcontext *ctx;
sp_variable *spv;
- if (spcont && (spv= spcont->find_variable(name1, false)))
+ if (spcont && (spv= find_variable(name1, &ctx, &rh)))
{
if (spv->field_def.is_table_rowtype_ref() ||
spv->field_def.is_cursor_rowtype_ref())
- return sphead->set_local_variable_row_field_by_name(thd, spcont,
+ return sphead->set_local_variable_row_field_by_name(thd, ctx,
+ rh,
spv, name2,
item, this);
// A field of a ROW variable
uint row_field_offset;
return !spv->find_row_field(name1, name2, &row_field_offset) ||
- sphead->set_local_variable_row_field(thd, spcont,
+ sphead->set_local_variable_row_field(thd, ctx, rh,
spv, row_field_offset,
item, this);
}
@@ -7416,12 +7571,18 @@ bool LEX::add_create_view(THD *thd, DDL_options_st ddl,
bool LEX::call_statement_start(THD *thd, sp_name *name)
{
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
+ const Sp_handler *sph= &sp_handler_procedure;
sql_command= SQLCOM_CALL;
- spname= name;
value_list.empty();
- if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name)))
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ name, &sph, &pkgname))
+ return true;
+ if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name, sph)))
return true;
- sp_handler_procedure.add_used_routine(this, thd, name);
+ sph->add_used_routine(this, thd, name);
+ if (pkgname.m_name.length)
+ sp_handler_package_body.add_used_routine(this, thd, &pkgname);
return false;
}
@@ -7441,6 +7602,98 @@ bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1,
}
+sp_package *LEX::get_sp_package() const
+{
+ return sphead ? sphead->get_package() : NULL;
+}
+
+
+sp_package *LEX::create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name_arg,
+ DDL_options_st options)
+{
+ sp_package *pkg;
+ if (sphead)
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
+ return NULL;
+ }
+ if (set_command_with_check(command, options))
+ return NULL;
+ if (sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ {
+ /*
+ If we start parsing a "CREATE PACKAGE BODY", we need to load
+ the corresponding "CREATE PACKAGE", for the following reasons:
+ 1. "CREATE PACKAGE BODY" is allowed only if "CREATE PACKAGE"
+ was done earlier for the same package name.
+ So if "CREATE PACKAGE" does not exist, we throw an error here.
+ 2. When parsing "CREATE PACKAGE BODY", we need to know all package
+ public and private routine names, to translate procedure and
+ function calls correctly.
+ For example, this statement inside a package routine:
+ CALL p;
+ can be translated to:
+ CALL db.pkg.p; -- p is a known (public or private) package routine
+ CALL db.p; -- p is not a known package routine
+ */
+ sp_head *spec;
+ int ret= sp_handler_package_spec.
+ sp_cache_routine_reentrant(thd, name_arg, &spec);
+ if (!spec)
+ {
+ if (!ret)
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "PACKAGE", ErrConvDQName(name_arg).ptr());
+ return 0;
+ }
+ }
+ if (!(pkg= new sp_package(this, name_arg, sph)))
+ return NULL;
+ pkg->reset_thd_mem_root(thd);
+ pkg->init(this);
+ pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname);
+ sphead= pkg;
+ return pkg;
+}
+
+
+bool LEX::create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ const char *body_start,
+ const char *body_end)
+{
+ if (name2 &&
+ (name2->m_explicit_name != name->m_explicit_name ||
+ strcmp(name2->m_db.str, name->m_db.str) ||
+ !Sp_handler::eq_routine_name(name2->m_name, name->m_name)))
+ {
+ bool exp= name2->m_explicit_name || name->m_explicit_name;
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0),
+ exp ? ErrConvDQName(name2).ptr() : name2->m_name.str,
+ exp ? ErrConvDQName(name).ptr() : name->m_name.str);
+ return true;
+ }
+ sphead->m_body.length= body_end - body_start;
+ if (!(sphead->m_body.str= thd->strmake(body_start, sphead->m_body.length)))
+ return true;
+
+ size_t not_used;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ sphead->m_defstr.length= lip->get_cpp_ptr() - lip->get_cpp_buf();
+ sphead->m_defstr.str= thd->strmake(lip->get_cpp_buf(), sphead->m_defstr.length);
+ trim_whitespace(thd->charset(), &sphead->m_defstr, &not_used);
+
+ sphead->restore_thd_mem_root(thd);
+ sp_package *pkg= sphead->get_package();
+ DBUG_ASSERT(pkg);
+ return pkg->validate_after_parser(thd);
+}
+
+
bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
stored_procedure_type type_arg)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 45fbe38e974..5710c990855 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2343,6 +2343,19 @@ public:
return m_cpp_ptr;
}
+ /**
+ Get the current stream pointer, in the pre-processed buffer,
+ with traling spaces removed.
+ */
+ const char *get_cpp_ptr_rtrim()
+ {
+ const char *p;
+ for (p= m_cpp_ptr;
+ p > m_cpp_buf && my_isspace(system_charset_info, p[-1]);
+ p--)
+ { }
+ return p;
+ }
/** Get the utf8-body string. */
const char *get_body_utf8_str()
{
@@ -3212,15 +3225,10 @@ public:
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name);
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
+ sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name);
sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph);
sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name,
- const Sp_handler *sph)
- {
- if (!sphead)
- return make_sp_head(thd, name, sph);
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
- return NULL;
- }
+ const Sp_handler *sph);
sp_head *make_sp_head_no_recursive(THD *thd,
DDL_options_st options, sp_name *name,
const Sp_handler *sph)
@@ -3231,10 +3239,29 @@ public:
}
bool sp_body_finalize_function(THD *);
bool sp_body_finalize_procedure(THD *);
+ sp_package *create_package_start(THD *thd,
+ enum_sql_command command,
+ const Sp_handler *sph,
+ const sp_name *name,
+ DDL_options_st options);
+ bool create_package_finalize(THD *thd,
+ const sp_name *name,
+ const sp_name *name2,
+ const char *body_start,
+ const char *body_end);
bool call_statement_start(THD *thd, sp_name *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ sp_pcontext **ctx,
+ const Sp_rcontext_handler **rh) const;
+ sp_variable *find_variable(const LEX_CSTRING *name,
+ const Sp_rcontext_handler **rh) const
+ {
+ sp_pcontext *not_used_ctx;
+ return find_variable(name, &not_used_ctx, rh);
+ }
bool init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *name);
bool init_internal_variable(struct sys_var_with_base *variable,
@@ -3318,6 +3345,7 @@ public:
/*
Create an Item corresponding to a ROW field valiable: var.field
@param THD - THD, for mem_root
+ @param rh [OUT] - the rcontext handler (local vs package variables)
@param var - the ROW variable name
@param field - the ROW variable field name
@param spvar - the variable that was previously found by name
@@ -3326,6 +3354,7 @@ public:
@param end - end in the query (for binary log)
*/
Item_splocal *create_item_spvar_row_field(THD *thd,
+ const Sp_rcontext_handler *rh,
const LEX_CSTRING *var,
const LEX_CSTRING *field,
sp_variable *spvar,
@@ -3419,6 +3448,8 @@ public:
Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
Item *make_item_func_substr(THD *thd, Item *a, Item *b);
+ my_var *create_outvar(THD *thd, const LEX_CSTRING *name);
+
/*
Create a my_var instance for a ROW field variable that was used
as an OUT SP parameter: CALL p1(var.field);
@@ -3479,6 +3510,7 @@ public:
bool sp_block_with_exceptions_finalize_exceptions(THD *thd,
uint executable_section_ip,
uint exception_count);
+ bool sp_block_with_exceptions_add_empty(THD *thd);
bool sp_exit_statement(THD *thd, Item *when);
bool sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item);
bool sp_leave_statement(THD *thd, const LEX_CSTRING *label_name);
@@ -3728,6 +3760,7 @@ public:
{
return create_info.vers_info;
}
+ sp_package *get_sp_package() const;
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 67181c6eb5e..f5fe955375d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -558,6 +558,10 @@ void init_update_queries(void)
CF_INSERTS_DATA;
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
@@ -631,6 +635,8 @@ void init_update_queries(void)
CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_STATUS_PACKAGE_BODY]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -665,10 +671,13 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_PACKAGE_BODY]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PACKAGE_BODY_CODE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
@@ -829,6 +838,10 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_DROP_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_ALTER_DB]|= CF_DISALLOW_IN_RO_TRANS;
sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
@@ -3124,7 +3137,7 @@ bool Sql_cmd_call::execute(THD *thd)
By this moment all needed SPs should be in cache so no need to look
into DB.
*/
- if (!(sp= sp_handler_procedure.sp_find_routine(thd, m_name, true)))
+ if (!(sp= m_handler->sp_find_routine(thd, m_name, true)))
{
/*
If the routine is not found, let's still check EXECUTE_ACL to decide
@@ -3705,6 +3718,8 @@ mysql_execute_command(THD *thd)
/* fall through */
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -5859,6 +5874,8 @@ end_with_restore_list:
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
{
if (mysql_create_routine(thd, lex))
goto error;
@@ -5908,6 +5925,8 @@ end_with_restore_list:
}
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
{
#ifdef HAVE_DLOPEN
if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
@@ -6022,6 +6041,8 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_PROC:
case SQLCOM_SHOW_CREATE_FUNC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
@@ -6031,11 +6052,19 @@ end_with_restore_list:
}
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
{
#ifndef DBUG_OFF
+ Database_qualified_name pkgname(&null_clex_str, &null_clex_str);
sp_head *sp;
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
+ if (sph->sp_resolve_package_routine(thd, thd->lex->sphead,
+ lex->spname, &sph, &pkgname))
+ return true;
if (sph->sp_cache_routine(thd, lex->spname, false, &sp))
goto error;
if (!sp || sp->show_routine_code(thd))
@@ -7997,6 +8026,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
THD_STAGE_INFO(thd, stage_freeing_items);
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
thd->end_statement();
thd->cleanup_after_query();
DBUG_ASSERT(thd->Item_change_list::is_empty());
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index dbc03234c44..22b8640d9ee 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2313,6 +2313,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_SHOW_TABLE_STATUS:
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SELECT:
res= mysql_test_select(stmt, tables);
if (res == 2)
@@ -2385,6 +2387,21 @@ static bool check_prepared_statement(Prepared_statement *stmt)
DBUG_RETURN(FALSE);
}
break;
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ if ((res= mysql_test_show_create_routine(stmt, &sp_handler_package_spec)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
+ if ((res= mysql_test_show_create_routine(stmt,
+ &sp_handler_package_body)) == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
case SQLCOM_CREATE_VIEW:
if (lex->create_view->mode == VIEW_ALTER)
{
@@ -2589,6 +2606,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* check_prepared_statemnt sends the metadata packet in case of success */
end:
@@ -3155,6 +3174,8 @@ static void mysql_stmt_execute_common(THD *thd,
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_spec_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_package_body_cache, stored_program_cache_size);
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 2b1b765a5be..fd38d4a7771 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2546,6 +2546,17 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
}
}
+
+static bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host)
+{
+ if (!host->str || !host->str[0])
+ return false;
+ return
+ buffer->append('@') ||
+ append_identifier(thd, buffer, host);
+}
+
+
/*
Append DEFINER clause to the given buffer.
@@ -2557,17 +2568,14 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
definer_host [in] host name part of definer
*/
-void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
+bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
const LEX_CSTRING *definer_host)
{
- buffer->append(STRING_WITH_LEN("DEFINER="));
- append_identifier(thd, buffer, definer_user);
- if (definer_host->str && definer_host->str[0])
- {
- buffer->append('@');
- append_identifier(thd, buffer, definer_host);
- }
- buffer->append(' ');
+ return
+ buffer->append(STRING_WITH_LEN("DEFINER=")) ||
+ append_identifier(thd, buffer, definer_user) ||
+ append_at_host(thd, buffer, definer_host) ||
+ buffer->append(' ');
}
@@ -9028,7 +9036,7 @@ ST_FIELD_INFO proc_fields_info[]=
SKIP_OPEN_TABLE},
{"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
- {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
+ {"ROUTINE_TYPE", 13, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
{"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
{"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
diff --git a/sql/sql_show.h b/sql/sql_show.h
index f6d5d4d2c3c..1b959121f89 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -106,7 +106,7 @@ bool mysqld_show_contributors(THD *thd);
bool mysqld_show_privileges(THD *thd);
char *make_backup_log_name(char *buff, const char *name, const char* log_ext);
uint calc_sum_of_all_status(STATUS_VAR *to);
-void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
+bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
const LEX_CSTRING *definer_host);
int add_status_vars(SHOW_VAR *list);
void remove_status_vars(SHOW_VAR *list);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 5cabcc02aae..37531429f8d 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -487,6 +487,10 @@ public:
ls->length == strlen(ls->str)));
return append(ls->str, (uint32) ls->length);
}
+ bool append(const LEX_CSTRING &ls)
+ {
+ return append(&ls);
+ }
bool append(const char *s, size_t size);
bool append(const char *s, size_t arg_length, CHARSET_INFO *cs);
bool append_ulonglong(ulonglong val);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 29bf7aeb205..4c1e2a51fbf 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -2253,7 +2253,8 @@ add_tables_and_routines_for_triggers(THD *thd,
MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
- &key, table_list->belong_to_view))
+ &key, &sp_handler_trigger,
+ table_list->belong_to_view))
{
trigger->add_used_tables_to_table_list(thd,
&prelocking_ctx->query_tables_last,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2ebcb057c32..35ec2d29d21 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -378,12 +378,13 @@ Item_splocal*
LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar,
const char *start_in_q, const char *end_in_q)
{
+ const Sp_rcontext_handler *rh;
Item_splocal *item;
uint pos_in_q, len_in_q;
/* If necessary, look for the variable. */
if (spcont && !spvar)
- spvar= spcont->find_variable(name, false);
+ spvar= find_variable(name, &rh);
if (!spvar)
{
@@ -398,7 +399,7 @@ LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar,
len_in_q= (uint)(end_in_q - start_in_q);
item= new (thd->mem_root)
- Item_splocal(thd, name, spvar->offset, spvar->type_handler(),
+ Item_splocal(thd, rh, name, spvar->offset, spvar->type_handler(),
pos_in_q, len_in_q);
#ifdef DBUG_ASSERT_EXISTS
@@ -951,6 +952,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token BIT_XOR /* MYSQL-FUNC */
%token BLOB_SYM /* SQL-2003-R */
%token BLOCK_SYM
+%token BODY_SYM /* Oracle-R */
%token BOOLEAN_SYM /* SQL-2003-R */
%token BOOL_SYM
%token BOTH /* SQL-2003-R */
@@ -1340,6 +1342,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token OUT_SYM /* SQL-2003-R */
%token OVER_SYM
%token OWNER_SYM
+%token PACKAGE_SYM /* Oracle-R */
%token PACK_KEYS_SYM
%token PAGE_SYM
%token PAGE_CHECKSUM_SYM
@@ -12885,14 +12888,8 @@ select_outvar:
}
| ident_or_text
{
- sp_variable *t;
-
- if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false)))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- $$ = Lex->result ? (new (thd->mem_root)
- my_var_sp(&$1, t->offset, t->type_handler(),
- Lex->sphead)) :
- NULL;
+ if (!($$= Lex->create_outvar(thd, &$1)) && Lex->result)
+ MYSQL_YYABORT;
}
| ident '.' ident
{
@@ -13947,6 +13944,18 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
+ | CREATE PACKAGE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
+ lex->spname= $3;
+ }
+ | CREATE PACKAGE_SYM BODY_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
+ lex->spname= $4;
+ }
| CREATE TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -13979,6 +13988,20 @@ show_param:
if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
}
+ | PACKAGE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
+ | PACKAGE_SYM BODY_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
| PROCEDURE_SYM CODE_SYM sp_name
{
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
@@ -13989,6 +14012,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
}
+ | PACKAGE_SYM BODY_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -15485,6 +15513,7 @@ keyword_sp_not_data_type:
| AVG_ROW_LENGTH {}
| AVG_SYM {}
| BLOCK_SYM {}
+ | BODY_SYM {}
| BTREE_SYM {}
| CASCADED {}
| CATALOG_NAME_SYM {}
@@ -15660,6 +15689,7 @@ keyword_sp_not_data_type:
| ONE_SYM {}
| ONLINE_SYM {}
| ONLY_SYM {}
+ | PACKAGE_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
| PARTIAL {}
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 1a2b1630d38..c8f93c17dd4 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -237,6 +237,7 @@ void ORAerror(THD *thd, const char *s)
st_trg_execution_order trg_execution_order;
/* enums */
+ enum enum_sp_suid_behaviour sp_suid;
enum enum_view_suid view_suid;
enum sub_select_type unit_type;
enum Condition_information_item::Name cond_info_item_name;
@@ -342,6 +343,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token BIT_XOR /* MYSQL-FUNC */
%token BLOB_SYM /* SQL-2003-R */
%token BLOCK_SYM
+%token BODY_SYM /* Oracle-R */
%token BOOLEAN_SYM /* SQL-2003-R */
%token BOOL_SYM
%token BOTH /* SQL-2003-R */
@@ -731,6 +733,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token OUT_SYM /* SQL-2003-R */
%token OVER_SYM
%token OWNER_SYM
+%token PACKAGE_SYM /* Oracle-R */
%token PACK_KEYS_SYM
%token PAGE_SYM
%token PAGE_CHECKSUM_SYM
@@ -1044,6 +1047,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
sp_opt_label BIN_NUM TEXT_STRING_filesystem ident_or_empty
opt_constraint constraint opt_ident
ident_directly_assignable
+ opt_package_routine_end_name
sp_decl_ident
sp_block_label opt_place opt_db
@@ -1070,7 +1074,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
optionally_qualified_column_ident
%type <simple_string>
- remember_name remember_end remember_tok_start
+ remember_name remember_end remember_end_opt remember_tok_start
wild_and_where
colon_with_pos
@@ -1334,7 +1338,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_extended_describe shutdown
opt_format_json
prepare prepare_src execute deallocate
- statement sp_suid
+ statement
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_list_opt view_list view_select
@@ -1376,17 +1380,28 @@ END_OF_INPUT
%type <num> view_algorithm view_check_option
%type <view_suid> view_suid opt_view_suid
+%type <sp_suid> sp_suid
%type <num> sp_decl_idents sp_decl_idents_init_vars
%type <num> sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
%type <spblock> sp_decl_body_list opt_sp_decl_body_list
+%type <spblock> sp_decl_vars
%type <spblock> sp_decl_non_handler sp_decl_non_handler_list
%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list
+%type <spblock> package_implementation_routine_definition
+%type <spblock> package_implementation_item_declaration
+%type <spblock> package_implementation_declare_section
+%type <spblock> package_implementation_declare_section_list1
+%type <spblock> package_implementation_declare_section_list2
%type <spblock_handlers> sp_block_statements_and_exceptions
+%type <spblock_handlers> package_implementation_executable_section
%type <sp_instr_addr> sp_instr_addr
%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
%type <num> opt_exception_clause exception_handlers
+%type <lex> remember_lex package_routine_lex
+ package_specification_function
+ package_specification_procedure
%type <spname> sp_name opt_sp_name
%type <spvar> sp_param_name sp_param_name_and_type
%type <for_loop> sp_for_loop_index_and_bounds
@@ -2115,6 +2130,248 @@ create:
| create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); }
server_def
{ }
+ | create_or_replace definer_opt PACKAGE_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (!(pkg= Lex->create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE,
+ &sp_handler_package_spec,
+ $5, $1 | $4)))
+ MYSQL_YYABORT;
+ pkg->set_chistics(Lex->sp_chistics);
+ }
+ opt_package_specification_element_list END
+ remember_end_opt opt_sp_name
+ {
+ if (Lex->create_package_finalize(thd, $5, $13, $8, $12))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace definer_opt PACKAGE_SYM BODY_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (!(pkg= Lex->create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ &sp_handler_package_body,
+ $6, $1 | $5)))
+ MYSQL_YYABORT;
+ pkg->set_chistics(Lex->sp_chistics);
+ Lex->sp_block_init(thd);
+ }
+ package_implementation_declare_section
+ {
+ if (Lex->sp_block_with_exceptions_finalize_declarations(thd))
+ MYSQL_YYABORT;
+ }
+ package_implementation_executable_section
+ {
+ $11.hndlrs+= $13.hndlrs;
+ if (Lex->sp_block_finalize(thd, $11))
+ MYSQL_YYABORT;
+ }
+ remember_end_opt opt_sp_name
+ {
+ if (Lex->create_package_finalize(thd, $6, $16, $9, $15))
+ MYSQL_YYABORT;
+ }
+ ;
+
+package_implementation_executable_section:
+ END
+ {
+ if (Lex->sp_block_with_exceptions_add_empty(thd))
+ MYSQL_YYABORT;
+ $$.init(0);
+ }
+ | BEGIN_SYM sp_block_statements_and_exceptions END { $$= $2; }
+ ;
+
+/*
+ Inside CREATE PACKATE BODY, package-wide items (e.g. variables)
+ must be declared before routine definitions.
+*/
+package_implementation_declare_section:
+ package_implementation_declare_section_list1
+ | package_implementation_declare_section_list2
+ | package_implementation_declare_section_list1
+ package_implementation_declare_section_list2
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list1:
+ package_implementation_item_declaration
+ | package_implementation_declare_section_list1
+ package_implementation_item_declaration
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list2:
+ package_implementation_routine_definition
+ | package_implementation_declare_section_list2
+ package_implementation_routine_definition
+ { $$.join($1, $2); }
+ ;
+
+package_routine_lex:
+ {
+ if (!($$= new (thd->mem_root) sp_lex_local(thd, thd->lex)))
+ MYSQL_YYABORT;
+ thd->m_parser_state->m_yacc.reset_before_substatement();
+ }
+ ;
+
+
+package_specification_function:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_FUNCTION;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (!spname)
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_function))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ (void) is_native_function_with_warn(thd, &$3);
+ }
+ opt_sp_parenthesized_fdparam_list
+ sf_return_type
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+ }
+ ;
+
+package_specification_procedure:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_PROCEDURE;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (!spname)
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_procedure))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ }
+ opt_sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+
+ }
+ ;
+
+
+package_implementation_routine_definition:
+ FUNCTION_SYM package_specification_function
+ package_implementation_function_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_implementation($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | PROCEDURE_SYM package_specification_procedure
+ package_implementation_procedure_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_implementation($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | package_specification_element { $$.init(); }
+ ;
+
+
+package_implementation_function_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
+ if (thd->lex->sp_body_finalize_function(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+package_implementation_procedure_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (thd->lex->sp_body_finalize_procedure(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+
+package_implementation_item_declaration:
+ sp_decl_vars ';'
+ ;
+
+opt_package_specification_element_list:
+ /* Empty */
+ | package_specification_element_list
+ ;
+
+package_specification_element_list:
+ package_specification_element
+ | package_specification_element_list package_specification_element
+ ;
+
+package_specification_element:
+ FUNCTION_SYM package_specification_function ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_declaration($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
+ | PROCEDURE_SYM package_specification_procedure ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (pkg->add_routine_declaration($2))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
;
create_function_tail:
@@ -2515,7 +2772,29 @@ sp_chistic:
| MODIFIES_SYM SQL_SYM DATA_SYM
{ Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
| sp_suid
- {}
+ { Lex->sp_chistics.suid= $1; }
+ ;
+
+create_package_chistic:
+ COMMENT_SYM TEXT_STRING_sys
+ { Lex->sp_chistics.comment= $2; }
+ | sp_suid
+ { Lex->sp_chistics.suid= $1; }
+ ;
+
+create_package_chistics:
+ create_package_chistic {}
+ | create_package_chistics create_package_chistic { }
+ ;
+
+opt_create_package_chistics:
+ /* Empty */
+ | create_package_chistics { }
+ ;
+
+opt_create_package_chistics_init:
+ { Lex->sp_chistics.init(); }
+ opt_create_package_chistics
;
/* Create characteristics */
@@ -2525,14 +2804,8 @@ sp_c_chistic:
;
sp_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_SUID;
- }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_NOT_SUID;
- }
+ SQL_SYM SECURITY_SYM DEFINER_SYM { $$= SP_IS_SUID; }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= SP_IS_NOT_SUID; }
;
call:
@@ -2844,7 +3117,7 @@ sp_decl_idents_init_vars:
}
;
-sp_decl_non_handler:
+sp_decl_vars:
sp_decl_idents_init_vars
type_with_opt_collate
sp_opt_default
@@ -2878,6 +3151,10 @@ sp_decl_non_handler:
MYSQL_YYABORT;
$$.init_using_vars($1);
}
+ ;
+
+sp_decl_non_handler:
+ sp_decl_vars
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
{
if (Lex->spcont->declare_condition(thd, &$1, $4))
@@ -3604,6 +3881,12 @@ sp_proc_stmt_goto:
;
+remember_lex:
+ {
+ $$= thd->lex;
+ }
+ ;
+
assignment_source_lex:
{
DBUG_ASSERT(Lex->sphead);
@@ -8840,6 +9123,15 @@ remember_end:
}
;
+remember_end_opt:
+ {
+ if (yychar == YYEMPTY)
+ $$= (char*) YYLIP->get_cpp_ptr_rtrim();
+ else
+ $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
+ }
+ ;
+
select_alias:
/* empty */ { $$=null_clex_str;}
| AS ident { $$=$2; }
@@ -12347,14 +12639,8 @@ select_outvar:
}
| ident_or_text
{
- sp_variable *t;
-
- if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false)))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
- $$ = Lex->result ? (new (thd->mem_root)
- my_var_sp(&$1, t->offset, t->type_handler(),
- Lex->sphead)) :
- NULL;
+ if (!($$= Lex->create_outvar(thd, &$1)) && Lex->result)
+ MYSQL_YYABORT;
}
| ident '.' ident
{
@@ -12454,6 +12740,22 @@ drop:
lex->set_command(SQLCOM_DROP_DB, $3);
lex->name= $4;
}
+ | DROP PACKAGE_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE, $3);
+ if (lex->sphead)
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE"));
+ lex->spname= $4;
+ }
+ | DROP PACKAGE_SYM BODY_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE_BODY, $4);
+ if (lex->sphead)
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE BODY"));
+ lex->spname= $5;
+ }
| DROP FUNCTION_SYM opt_if_exists ident '.' ident
{
LEX *lex= thd->lex;
@@ -13384,6 +13686,18 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
+ | CREATE PACKAGE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
+ lex->spname= $3;
+ }
+ | CREATE PACKAGE_SYM BODY_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
+ lex->spname= $4;
+ }
| CREATE TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -13416,6 +13730,20 @@ show_param:
if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
}
+ | PACKAGE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
+ | PACKAGE_SYM BODY_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
+ MYSQL_YYABORT;
+ }
| PROCEDURE_SYM CODE_SYM sp_name
{
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
@@ -13426,6 +13754,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
}
+ | PACKAGE_SYM BODY_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -14915,6 +15248,7 @@ keyword_directly_not_assignable:
keyword_sp:
keyword_sp_data_type
| keyword_sp_not_data_type
+ | FUNCTION_SYM { /* Oracle-PLSQL-R */}
;
@@ -15269,7 +15603,6 @@ keyword_sp_not_data_type:
| TYPES_SYM {}
| TYPE_SYM {}
| UDF_RETURNS_SYM {}
- | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNDEFINED_SYM {}
| UNDO_BUFFER_SIZE_SYM {}
@@ -15966,6 +16299,18 @@ revoke_command:
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
+ | grant_privileges ON PACKAGE_SYM grant_ident FROM user_and_role_list
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_REVOKE,
+ TYPE_ENUM_PACKAGE))
+ MYSQL_YYABORT;
+ }
+ | grant_privileges ON PACKAGE_SYM BODY_SYM grant_ident FROM user_and_role_list
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_REVOKE,
+ TYPE_ENUM_PACKAGE_BODY))
+ MYSQL_YYABORT;
+ }
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
@@ -16017,6 +16362,20 @@ grant_command:
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
+ | grant_privileges ON PACKAGE_SYM grant_ident TO_SYM grant_list
+ opt_require_clause opt_grant_options
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_GRANT,
+ TYPE_ENUM_PACKAGE))
+ MYSQL_YYABORT;
+ }
+ | grant_privileges ON PACKAGE_SYM BODY_SYM grant_ident TO_SYM grant_list
+ opt_require_clause opt_grant_options
+ {
+ if (Lex->add_grant_command(thd, SQLCOM_GRANT,
+ TYPE_ENUM_PACKAGE_BODY))
+ MYSQL_YYABORT;
+ }
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
LEX *lex= Lex;
@@ -17010,6 +17369,11 @@ sf_tail:
{
if (Lex->sp_body_finalize_function(thd))
MYSQL_YYABORT;
+ if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)
+ {
+ my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0)));
+ }
+ Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE);
}
;
@@ -17055,6 +17419,11 @@ sp_tail_standalone:
}
;
+opt_package_routine_end_name:
+ /* Empty */ { $$= null_clex_str; }
+ | ident { $$= $1; }
+ ;
+
sp_tail_is:
IS
| AS