diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-08-18 23:36:42 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2018-02-25 21:08:19 +0400 |
commit | 583eb96c2492adb87e88a014b24eb0724fb00257 (patch) | |
tree | 501cb4e5e3855400e79df8911ac43ef1f89300b3 | |
parent | 83ea839fb15dd5ed616d2b3152ccc5472ee5e5e6 (diff) | |
download | mariadb-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
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, ¶ms); - 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 ¶ms, + 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); @@ -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 ¶ms, - 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 ¶ms, + 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 ¶ms, + 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, ¬_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, ¬_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 |