From 7ac90069fde56a773de0deb2c4536a13c897bc2b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 13 May 2026 11:06:40 +0400 Subject: [PATCH] MDEV-39587 Package-wide TYPE for variable declarations SET sql_mode=ORACLE; DELIMITER $$ CREATE OR REPLACE PACKAGE pkg AS -- Declare a package public data type TYPE varchar_array IS TABLE OF VARCHAR(2000) INDEX BY INTEGER; END; $$ DELIMITER ; DELIMITER $$ CREATE OR REPLACE PROCEDURE p1 AS v pkg.varchar_array; -- Use the package public data type BEGIN v(0):='test'; SELECT v(0); END; $$ DELIMITER ; Note, the change is done only for sql_mode=ORACLE, because the TYPE declaration is not available for the default mode. Where package-wide types are available -------------------------------------- - Variabe list type: DECLARE var pkg1.type1; - RETURN type for a package routine: CREATE FUNCTION .. RETURN pkg1.type1 ... - Parameter type for a package routine: PROCEDURE p1(param1 pkg1.type1); - Assoc array element type: TYPE assoc1_t IS TABLE OF pkg1.type1 ... - REF CURSOR RETURN type: TYPE cur1_t IS REF CURSOR RETURN pkg1.type1; Change details -------------- - Adding a member Lex_length_and_dec_st::m_foreign_module_type It's set to true when the data type was initialized from a TYPE in foreign routine (e.g. in PACKAGE spec). It's needed to prevent use of qualified identifiers in public contexts, i.e. in schema public routine parameter types and schema publuc function RETURN types. Adding a helper method sp_head::check_applicability() which prevents use of qualified types in public context. - Adding a helper method sp_head::raise_unknown_data_type(). - Adding methods LEX::set_field_type_typedef_package_spec() for 2-step and 3-step qualified indentifiers. It's used in field_type_all_with_typedefs which covers cases: - Variabe list type : DECLARE var pkg1.type1; - RETURN type : CREATE FUNCTION .. RETURN pkg1.type1 ... - Parameter type : PROCEDURE p1(param1 pkg1.type1); - Assoc array element type : TYPE assoc1_t IS TABLE OF pkg1.type1 ... - Adding a method LEX::declare_type_ref_cursor_return_typedef(). It handles cases when a new TYPE REF CURSOR RETURN is declared, for both for qualified RETURN types and non-qualified RETURN types: - TYPE cur0_t IS REF CURSOR RETURN rec1_t; - TYPE cur0_t IS REF CURSOR RETURN pkg1.rec1_t; - TYPE cur0_t IS REF CURSOR RETURN db1.pkg1.rec1_t; The code was moved from LEX::declare_type_ref_cursor() into LEX::declare_type_ref_cursor_return_typedef() and extended to cover qualified RETURN types. - Adding a method Sql_path::find_package_spec_type(). It iterates through all schemas specified in @@path and searches for the given type in the given package. - Adding a helper method sp_pcontext::type_defs_add_ref_cursor() to reuse the code. - Adding a new method sp_package::get_typedef() to search for TYPE definitions in PACKAGE specifications. - Adding a new method sp_head::get_typedef_package_spec() to search for TYPE definitions used by a PROCEDURE or FUNCTION. - Adding a helper method Sp_handler::sp_cache_routine_reentrant_suppress_errors Adding a method Sp_handler::find_package_spec(). --- mysql-test/main/parser.result | 2 +- .../r/sp-package-spec-type-assoc.result | 522 ++++++++++++++++ .../r/sp-package-spec-type-inet6.result | 117 ++++ .../oracle/r/sp-package-spec-type-path.result | 82 +++ .../r/sp-package-spec-type-record.result | 150 +++++ .../r/sp-package-spec-type-refcursor.result | 539 ++++++++++++++++ .../oracle/r/sp-package-spec-type.result | 64 ++ .../suite/compat/oracle/r/type_date.result | 17 +- .../oracle/t/sp-package-spec-type-assoc.test | 556 ++++++++++++++++ .../oracle/t/sp-package-spec-type-inet6.test | 129 ++++ .../oracle/t/sp-package-spec-type-path.test | 113 ++++ .../oracle/t/sp-package-spec-type-record.test | 153 +++++ .../t/sp-package-spec-type-refcursor.test | 591 ++++++++++++++++++ .../compat/oracle/t/sp-package-spec-type.test | 73 +++ .../suite/compat/oracle/t/type_date.test | 8 +- ...-ref_cursor-return-rowtype-of-table.result | 5 +- ...sp-ref_cursor-return-rowtype-of-table.test | 2 +- .../sp-ref_cursor-return-type-of-var.result | 5 +- .../sp-ref_cursor-return-type-of-var.test | 2 +- sql/sp.cc | 46 +- sql/sp.h | 7 + sql/sp_head.cc | 233 +++++++ sql/sp_head.h | 25 + sql/sp_pcontext.cc | 14 + sql/sp_pcontext.h | 19 + sql/sql_lex.cc | 188 ++++-- sql/sql_lex.h | 13 +- sql/sql_path.cc | 53 ++ sql/sql_path.h | 4 + sql/sql_yacc.yy | 40 +- sql/structs.h | 13 + 31 files changed, 3702 insertions(+), 83 deletions(-) create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type-assoc.result create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type-inet6.result create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type-path.result create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type-record.result create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type-refcursor.result create mode 100644 mysql-test/suite/compat/oracle/r/sp-package-spec-type.result create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type-assoc.test create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type-inet6.test create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type-path.test create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type-record.test create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type-refcursor.test create mode 100644 mysql-test/suite/compat/oracle/t/sp-package-spec-type.test diff --git a/mysql-test/main/parser.result b/mysql-test/main/parser.result index ecb7d4f7c1396..edad2c9aa180a 100644 --- a/mysql-test/main/parser.result +++ b/mysql-test/main/parser.result @@ -1300,7 +1300,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp create table t1 (i int, vc serial as (i)); 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 (i))' at line 1 create function fs() returns serial return 1; -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 'serial return 1' at line 1 +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 'return 1' at line 1 create table t1 ( id serial ); show create table t1; Table Create Table diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type-assoc.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-assoc.result new file mode 100644 index 0000000000000..00fe94eab9804 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-assoc.result @@ -0,0 +1,522 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +# A too long identifier chain in the TABLE OF type +CREATE PACKAGE pkg1 AS +TYPE assoc0_t IS TABLE OF cat1.db1.pkg1.rec1_t INDEX BY INTEGER; +END; +$$ +ERROR HY000: Unknown data type: '`cat1`.`db1`.`pkg1`' +# Assoc array cannot have attributes +# Currently this is not allowed by the parser in qualified types +# If we ever allow, make sure to call check_data_type_attributes() +# which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.assoc0_t (0); -- Length attribute +BEGIN +NULL; +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 '(0); -- Length attribute +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.assoc0_t (0,0); -- Length and dec attributes +BEGIN +NULL; +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 '(0,0); -- Length and dec attributes +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.assoc0_t CHARACTER SET latin1; +BEGIN +NULL; +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 'CHARACTER SET latin1; +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.assoc0_t COLLATE latin1_bin; +BEGIN +NULL; +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 'COLLATE latin1_bin; +BEGIN +NULL; +END' at line 2 +DROP PACKAGE pkg1; +# PACKAGE spec types cannot be used in schema routine parameters +# and schema function RETURN +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1(a pkg1.assoc0_t) AS +BEGIN +NULL; +END; +$$ +ERROR HY000: Incorrect usage of parameter_declaration and package_name.type_name +CREATE FUNCTION f1 RETURN pkg1.assoc0_t AS +BEGIN +RETURN NULL; +END; +$$ +ERROR HY000: Incorrect usage of RETURN and package_name.type_name +DROP PACKAGE pkg1; +# A package routine parameter and a package function RETURN +# 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(a pkg1.assoc0_t) AS +BEGIN +SELECT a(0).a, a(0).b; +END; +FUNCTION f1 RETURN pkg1.assoc0_t AS +res pkg1.assoc0_t; +BEGIN +res(0).a:= 10; +res(0).b:= 'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +ERROR HY000: Illegal parameter data type associative_array for operation '' +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; +# RECORD as a package routine parameter and a package function RETURN +# 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(a test.pkg1.assoc0_t) AS +BEGIN +SELECT a(0).a, a(0).b; +END; +FUNCTION f1 RETURN test.pkg1.assoc0_t AS +res test.pkg1.assoc0_t; +BEGIN +res(0).a:= 10; +res(0).b:= 'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +ERROR HY000: Illegal parameter data type associative_array for operation '' +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; +# A self-reference to the current spec with error: 2-step +CREATE PACKAGE pkg1 AS +TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +ERROR HY000: Unknown data type: '`pkg1`.`rec0_t`' +CREATE DATABASE test1; +CREATE PACKAGE test1.pkg1 AS +TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +ERROR HY000: Unknown data type: '`pkg1`.`rec0_t`' +DROP DATABASE test1; +# A self-reference to the current spec with success: 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t; +a0 pkg1.assoc0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +10 b +DROP PROCEDURE p1; +DROP PACKAGE pkg1; +CREATE DATABASE test1; +CREATE PACKAGE test1.pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE test1.p1 AS +r0 pkg1.rec0_t; -- This refers to test1.pkg1.rec0_t +a0 pkg1.assoc0_t; -- This refers to test1.pkg1.assoc0_t +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL test1.p1; +r0.a r0.b +10 b +DROP PROCEDURE test1.p1; +DROP PACKAGE test1.pkg1; +DROP DATABASE test1; +USE test; +# A self-reference to the current spec with error: 3-step +CREATE PACKAGE pkg1 AS +TYPE assoc0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +ERROR HY000: Unknown data type: '`test`.`pkg1`.`rec0_t`' +CREATE DATABASE test1; +CREATE PACKAGE test1.pkg1 AS +TYPE assoc0_t IS TABLE OF test1.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +ERROR HY000: Unknown data type: '`test1`.`pkg1`.`rec0_t`' +DROP DATABASE test1; +# A self-reference to the current spec with success: 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE assoc0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t; +a0 pkg1.assoc0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +10 b +DROP PROCEDURE p1; +DROP PACKAGE pkg1; +CREATE DATABASE test1; +CREATE PACKAGE test1.pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE assoc0_t IS TABLE OF test1.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE test1.p1 AS +r0 pkg1.rec0_t; -- This refers to test1.pkg1.rec0_t +a0 pkg1.assoc0_t; -- This refers to test1.pkg1.assoc0_t +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL test1.p1; +r0.a r0.b +10 b +DROP PROCEDURE test1.p1; +DROP PACKAGE test1.pkg1; +DROP DATABASE test1; +# Various working examples +CREATE TABLE t1 (a INT, b VARCHAR(30)); +CREATE PACKAGE pkg1 AS +TYPE varchar_array IS TABLE OF VARCHAR(2000) INDEX BY INTEGER; +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +TYPE array0_t IS TABLE OF rec0_t INDEX BY INTEGER; +TYPE array1_t IS TABLE OF test.t1.a%TYPE INDEX BY INTEGER; +TYPE array2_t IS TABLE OF test.t1%ROWTYPE INDEX BY INTEGER; +END; +$$ +# 2-Step + PROCEDURE +CREATE PROCEDURE p1 AS +v0 pkg1.varchar_array; +v1 pkg1.rec0_t; +v2 pkg1.array0_t; +v3 pkg1.array1_t; +v4 pkg1.array2_t; +BEGIN +v0(0):='test'; +SELECT v0(0); +v1.a:= 1; +v1.b:= 'b'; +SELECT v1.a, v1.b; +v2(0):= v1; +SELECT v2(0).a, v2(0).b; +v3(0):= 10; +SELECT v3(0); +v4(0):= v1; +SELECT v4(0).a, v4(0).b; +END; +$$ +CALL p1; +v0(0) +test +v1.a v1.b +1 b +v2(0).a v2(0).b +1 b +v3(0) +10 +v4(0).a v4(0).b +1 b +DROP PROCEDURE p1; +# 3-Step + PROCEDURE +CREATE DATABASE test1; +USE test1; +CREATE PROCEDURE p1 AS +v0 test.pkg1.varchar_array; +v1 test.pkg1.rec0_t; +v2 test.pkg1.array0_t; +v3 test.pkg1.array1_t; +v4 test.pkg1.array2_t; +BEGIN +v0(0):='test'; +SELECT v0(0); +v1.a:= 1; +v1.b:= 'b'; +SELECT v1.a, v1.b; +v2(0):= v1; +SELECT v2(0).a, v2(0).b; +v3(0):= 10; +SELECT v3(0); +v4(0):= v1; +SELECT v4(0).a, v4(0).b; +END; +$$ +CALL p1; +v0(0) +test +v1.a v1.b +1 b +v2(0).a v2(0).b +1 b +v3(0) +10 +v4(0).a v4(0).b +1 b +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; +# 2-Step + PACKAGE + Declarations in package PROCEDURE +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v0 pkg1.varchar_array; +v1 pkg1.rec0_t; +v2 pkg1.array0_t; +v3 pkg1.array1_t; +v4 pkg1.array2_t; +BEGIN +v0(0):='test'; +SELECT v0(0); +v1.a:= 1; +v1.b:= 'b'; +SELECT v1.a, v1.b; +v2(0):= v1; +SELECT v2(0).a, v2(0).b; +v3(0):= 10; +SELECT v3(0); +v4(0):= v1; +SELECT v4(0).a, v4(0).b; +END; +END; +$$ +CALL pkg2.p1; +v0(0) +test +v1.a v1.b +1 b +v2(0).a v2(0).b +1 b +v3(0) +10 +v4(0).a v4(0).b +1 b +DROP PACKAGE pkg2; +# 3-Step + PACKAGE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v0 test.pkg1.varchar_array; +v1 test.pkg1.rec0_t; +v2 test.pkg1.array0_t; +v3 test.pkg1.array1_t; +v4 test.pkg1.array2_t; +BEGIN +v0(0):='test'; +SELECT v0(0); +v1.a:= 1; +v1.b:= 'b'; +SELECT v1.a, v1.b; +v2(0):= v1; +SELECT v2(0).a, v2(0).b; +v3(0):= 10; +SELECT v3(0); +v4(0):= v1; +SELECT v4(0).a, v4(0).b; +END; +END; +$$ +CALL pkg2.p1; +v0(0) +test +v1.a v1.b +1 b +v2(0).a v2(0).b +1 b +v3(0) +10 +v4(0).a v4(0).b +1 b +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +# 3-Step + PACKAGE + Declarations in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +v0 test.pkg1.varchar_array; +v1 test.pkg1.rec0_t; +v2 test.pkg1.array0_t; +v3 test.pkg1.array1_t; +v4 test.pkg1.array2_t; +PROCEDURE p1 AS +BEGIN +v0(0):='test'; +SELECT v0(0); +v1.a:= 1; +v1.b:= 'b'; +SELECT v1.a, v1.b; +v2(0):= v1; +SELECT v2(0).a, v2(0).b; +v3(0):= 10; +SELECT v3(0); +v4(0):= v1; +SELECT v4(0).a, v4(0).b; +END; +END; +$$ +CALL pkg2.p1; +v0(0) +test +v1.a v1.b +1 b +v2(0).a v2(0).b +1 b +v3(0) +10 +v4(0).a v4(0).b +1 b +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; +DROP TABLE t1; +# RECORD + Assoc array in different packages: 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +CREATE PACKAGE pkg2 AS +TYPE array0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t; +a0 pkg2.array0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +a0(0):= r0; +SELECT a0(0).a, a0(0).b; +END; +$$ +CALL p1; +a0(0).a a0(0).b +10 b +DROP PROCEDURE p1; +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; +# RECORD + Assoc array in different packages: 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +TYPE array0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE DATABASE test2; +USE test2; +CREATE PROCEDURE p1 AS +r0 test.pkg1.rec0_t; +a0 test1.pkg2.array0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +a0(0):= r0; +SELECT a0(0).a, a0(0).b; +END; +$$ +CALL p1; +a0(0).a a0(0).b +10 b +DROP PROCEDURE p1; +DROP DATABASE test2; +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type-inet6.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-inet6.result new file mode 100644 index 0000000000000..3b918e2393aa8 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-inet6.result @@ -0,0 +1,117 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +CREATE PACKAGE pkg1 AS +TYPE inet6_array IS TABLE OF INET6 INDEX BY INTEGER; +END; +$$ +# 2-Step + PROCEDURE +CREATE PROCEDURE p1 AS +v0 pkg1.inet6_array; +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +$$ +CALL p1; +v0(0) +aa::bb +DROP PROCEDURE p1; +# 2-Step + PACKAGE + Declarations in package PROCEDURE +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v0 pkg1.inet6_array; +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +END; +$$ +CALL pkg2.p1; +v0(0) +aa::bb +DROP PACKAGE pkg2; +# 2-Step + PACKAGE + Declaration in PACKAGE BODY +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +v0 pkg1.inet6_array; +PROCEDURE p1 AS +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +END; +$$ +CALL pkg2.p1; +v0(0) +aa::bb +DROP PACKAGE pkg2; +# 3-Step + PROCEDURE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +CREATE PROCEDURE p1 AS +v0 test.pkg1.inet6_array; +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +$$ +CALL p1; +v0(0) +aa::bb +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; +# 3-Step + PACKAGE +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v0 test.pkg1.inet6_array; +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +END; +$$ +CALL pkg2.p1; +v0(0) +aa::bb +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +# 3-Step + PACKAGE + Declaration in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +v0 test.pkg1.inet6_array; +PROCEDURE p1 AS +BEGIN +v0(0):='AA::BB'; +SELECT v0(0); +END; +END; +$$ +CALL pkg2.p1; +v0(0) +aa::bb +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type-path.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-path.result new file mode 100644 index 0000000000000..f6bd94c23825f --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-path.result @@ -0,0 +1,82 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +# PATH resolving not to CURRENT_SCHEMA +CREATE DATABASE test1; +CREATE PACKAGE test1.pkg1 AS +TYPE rec0_t IS RECORD (a INT, b_test1 VARCHAR(10)); +END; +$$ +CREATE DATABASE test2; +CREATE PACKAGE test2.pkg1 AS +TYPE rec0_t IS RECORD (a INT, b_test2 VARCHAR(10)); +END; +$$ +CREATE PROCEDURE p1 AS +a pkg1.rec0_t; +BEGIN +NULL; +END; +$$ +ERROR HY000: Unknown data type: '`pkg1`.`rec0_t`' +SET PATH 'CURRENT_SCHEMA,test1,test2'; +CREATE PROCEDURE p1 AS +a pkg1.rec0_t; +BEGIN +a.b_test1:= 10; +SELECT a.b_test1; +END; +$$ +CALL test.p1(); +a.b_test1 +10 +DROP PROCEDURE p1; +SET PATH 'CURRENT_SCHEMA,test2,test1'; +CREATE PROCEDURE p1 AS +a pkg1.rec0_t; +BEGIN +a.b_test2:= 10; +SELECT a.b_test2; +END; +$$ +CALL p1(); +a.b_test2 +10 +DROP PROCEDURE p1; +SET PATH DEFAULT; +DROP DATABASE test1; +DROP DATABASE test2; +# PATH resolving to CURRENT_SCHEMA +CREATE DATABASE test1; +CREATE PACKAGE test.pkg1 AS +TYPE rec0_t IS RECORD (a INT, b_test VARCHAR(10)); +END; +$$ +SET PATH 'test1,CURRENT_SCHEMA'; +CREATE PROCEDURE p1 AS +a pkg1.rec0_t; +BEGIN +a.b_test:= 10; +SELECT a.b_test; +END; +$$ +CALL p1(); +a.b_test +10 +DROP PROCEDURE p1; +SET PATH 'CURRENT_SCHEMA,test1'; +CREATE PROCEDURE p1 AS +a pkg1.rec0_t; +BEGIN +a.b_test:= 10; +SELECT a.b_test; +END; +$$ +CALL p1(); +a.b_test +10 +DROP PROCEDURE p1; +DROP PACKAGE pkg1; +DROP DATABASE test1; +SET PATH DEFAULT; diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type-record.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-record.result new file mode 100644 index 0000000000000..6392018a3f534 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-record.result @@ -0,0 +1,150 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +# RECORD cannot have attributes +# Currently this is not allowed by the parser in qualified types +# If we ever allow, make sure to call check_data_type_attributes() +# which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t (0); -- Length attribute +BEGIN +NULL; +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 '(0); -- Length attribute +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t (0,0); -- Length and dec attributes +BEGIN +NULL; +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 '(0,0); -- Length and dec attributes +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t CHARACTER SET latin1; +BEGIN +NULL; +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 'CHARACTER SET latin1; +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t COLLATE latin1_bin; +BEGIN +NULL; +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 'COLLATE latin1_bin; +BEGIN +NULL; +END' at line 2 +DROP PACKAGE pkg1; +# PACKAGE spec types cannot be used in schema routine parameters +# and schema function RETURN +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +CREATE PROCEDURE p1(a pkg1.rec0_t) AS +BEGIN +NULL; +END; +$$ +ERROR HY000: Incorrect usage of parameter_declaration and package_name.type_name +CREATE FUNCTION f1 RETURN pkg1.rec0_t AS +BEGIN +RETURN NULL; +END; +$$ +ERROR HY000: Incorrect usage of RETURN and package_name.type_name +DROP PACKAGE pkg1; +# RECORD as a package routine parameter and a package function RETURN +# 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(r pkg1.rec0_t) AS +BEGIN +SELECT r.a, r.b; +END; +FUNCTION f1 RETURN pkg1.rec0_t AS +res pkg1.rec0_t; +BEGIN +res.a:= 10; +res.b:= 'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +CALL pkg2.p1(); +r.a r.b +10 b +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; +# RECORD as a package routine parameter and a package function RETURN +# 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(r test.pkg1.rec0_t) AS +BEGIN +SELECT r.a, r.b; +END; +FUNCTION f1 RETURN test.pkg1.rec0_t AS +res test.pkg1.rec0_t; +BEGIN +res.a:= 10; +res.b:= 'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +CALL pkg2.p1(); +r.a r.b +10 b +CALL test1.pkg2.p1(); +r.a r.b +10 b +USE test; +CALL test1.pkg2.p1(); +r.a r.b +10 b +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type-refcursor.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-refcursor.result new file mode 100644 index 0000000000000..1d461ac8c6b00 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type-refcursor.result @@ -0,0 +1,539 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +# REFCURSOR cannot have attributes +# Currently this is not allowed by the parser in qualified types +# If we ever allow, make sure to call check_data_type_attributes() +# which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.cur0_t (0); -- Length attribute +BEGIN +NULL; +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 '(0); -- Length attribute +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.cur0_t (0,0); -- Length and dec attributes +BEGIN +NULL; +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 '(0,0); -- Length and dec attributes +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.cur0_t CHARACTER SET latin1; +BEGIN +NULL; +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 'CHARACTER SET latin1; +BEGIN +NULL; +END' at line 2 +CREATE PROCEDURE p1 AS +r0 pkg1.cur0_t COLLATE latin1_bin; +BEGIN +NULL; +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 'COLLATE latin1_bin; +BEGIN +NULL; +END' at line 2 +DROP PACKAGE pkg1; +# PACKAGE spec types cannot be used in schema routine parameters +# and schema function RETURN +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +CREATE PROCEDURE p1(a pkg1.cur0_t) AS +BEGIN +NULL; +END; +$$ +ERROR HY000: Incorrect usage of parameter_declaration and package_name.type_name +CREATE FUNCTION f1 RETURN pkg1.cur0_t AS +BEGIN +RETURN NULL; +END; +$$ +ERROR HY000: Incorrect usage of RETURN and package_name.type_name +DROP PACKAGE pkg1; +# A package routine parameter and a package function RETURN +# 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(c pkg1.cur0_t) AS +v0 INT; +v1 VARCHAR(32); +BEGIN +FETCH c INTO v0, v1; +SELECT v0, v1; +CLOSE c; +END; +FUNCTION f1 RETURN pkg1.cur0_t AS +res pkg1.cur0_t; +BEGIN +OPEN res FOR SELECT 1,'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +CALL pkg2.p1(); +v0 v1 +1 b +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; +# A package routine parameter and a package function RETURN +# 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p2(c test.pkg1.cur0_t) AS +v0 INT; +v1 VARCHAR(32); +BEGIN +FETCH c INTO v0, v1; +SELECT v0, v1; +CLOSE c; +END; +FUNCTION f1 RETURN test.pkg1.cur0_t AS +res test.pkg1.cur0_t; +BEGIN +OPEN res FOR SELECT 1, 'b'; +RETURN res; +END; +PROCEDURE p1 AS +BEGIN +CALL p2(f1()); +END; +END; +$$ +CALL pkg2.p1(); +v0 v1 +1 b +CALL test1.pkg2.p1(); +v0 v1 +1 b +USE test; +CALL test1.pkg2.p1(); +v0 v1 +1 b +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; +# A self-reference to the current spec with success: 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; -- Self ref +END; +$$ +# Using from the same database +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t; +c0 pkg1.cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO r0; +CLOSE c0; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +1 b +DROP PROCEDURE p1; +# Using from a different database +CREATE DATABASE test1; +CREATE PROCEDURE test1.p1 AS +r0 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO r0; +CLOSE c0; +SELECT r0.a, r0.b; +NULL; +END; +$$ +CALL test1.p1; +r0.a r0.b +1 b +DROP DATABASE test1; +DROP PACKAGE pkg1; +# A self-reference to the current spec with success: 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; -- Self ref +END; +$$ +# Using from the same database +CREATE PROCEDURE p1 AS +r0 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO r0; +CLOSE c0; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +1 b +DROP PROCEDURE p1; +# Using from a different database +CREATE DATABASE test1; +CREATE PROCEDURE test1.p1 AS +r0 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO r0; +CLOSE c0; +SELECT r0.a, r0.b; +NULL; +END; +$$ +CALL test1.p1; +r0.a r0.b +1 b +DROP DATABASE test1; +DROP PACKAGE pkg1; +# Various working examples +CREATE TABLE t1 (a INT, b VARCHAR(30)); +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +TYPE cur0_t IS REF CURSOR; +TYPE cur1_t IS REF CURSOR RETURN rec0_t; +TYPE cur2_t IS REF CURSOR RETURN test.t1%ROWTYPE; +END; +$$ +# 2-Step + PROCEDURE +CREATE PROCEDURE p1 AS +v1 pkg1.rec0_t; +c0 pkg1.cur0_t; +c1 pkg1.cur1_t; +c2 pkg1.cur2_t; +fetch0 INT; +fetch1 VARCHAR(30); +BEGIN +v1.a:= 1; +v1.b:= 'b'; +OPEN c0 FOR SELECT 10,'bb0'; +FETCH c0 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c0; +OPEN c1 FOR SELECT 11,'bb1'; +FETCH c1 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c1; +OPEN c2 FOR SELECT 12,'bb2'; +FETCH c2 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c2; +END; +$$ +CALL p1; +fetch0 fetch1 +10 bb0 +fetch0 fetch1 +11 bb1 +fetch0 fetch1 +12 bb2 +DROP PROCEDURE p1; +# 3-Step + PROCEDURE +CREATE DATABASE test1; +USE test1; +CREATE PROCEDURE p1 AS +v1 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +c1 test.pkg1.cur1_t; +c2 test.pkg1.cur2_t; +fetch0 INT; +fetch1 VARCHAR(30); +BEGIN +v1.a:= 1; +v1.b:= 'b'; +OPEN c0 FOR SELECT 10,'bb0'; +FETCH c0 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c0; +OPEN c1 FOR SELECT 11,'bb1'; +FETCH c1 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c1; +OPEN c2 FOR SELECT 12,'bb2'; +FETCH c2 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c2; +END; +$$ +CALL p1; +fetch0 fetch1 +10 bb0 +fetch0 fetch1 +11 bb1 +fetch0 fetch1 +12 bb2 +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; +# 2-Step + PACKAGE + Declarations in package PROCEDURE +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v1 pkg1.rec0_t; +c0 pkg1.cur0_t; +c1 pkg1.cur1_t; +c2 pkg1.cur2_t; +fetch0 INT; +fetch1 VARCHAR(30); +BEGIN +v1.a:= 1; +v1.b:= 'b'; +OPEN c0 FOR SELECT 10,'bb0'; +FETCH c0 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c0; +OPEN c1 FOR SELECT 11,'bb1'; +FETCH c1 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c1; +OPEN c2 FOR SELECT 12,'bb2'; +FETCH c2 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c2; +END; +END; +$$ +CALL pkg2.p1; +fetch0 fetch1 +10 bb0 +fetch0 fetch1 +11 bb1 +fetch0 fetch1 +12 bb2 +DROP PACKAGE pkg2; +# 3-Step + PACKAGE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +PROCEDURE p1 AS +v1 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +c1 test.pkg1.cur1_t; +c2 test.pkg1.cur2_t; +fetch0 INT; +fetch1 VARCHAR(30); +BEGIN +v1.a:= 1; +v1.b:= 'b'; +OPEN c0 FOR SELECT 10,'bb0'; +FETCH c0 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c0; +OPEN c1 FOR SELECT 11,'bb1'; +FETCH c1 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c1; +OPEN c2 FOR SELECT 12,'bb2'; +FETCH c2 INTO fetch0, fetch1; +SELECT fetch0, fetch1; +CLOSE c2; +END; +END; +$$ +CALL pkg2.p1; +fetch0 fetch1 +10 bb0 +fetch0 fetch1 +11 bb1 +fetch0 fetch1 +12 bb2 +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +# 3-Step + PACKAGE + Declarations in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +CREATE PACKAGE pkg2 AS +PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS +v1 test.pkg1.rec0_t; +c0 test.pkg1.cur0_t; +c1 test.pkg1.cur1_t; +c2 test.pkg1.cur2_t; +fetch0 INT; +fetch1 VARCHAR(30); +PROCEDURE p1 AS +BEGIN +NULL; +END; +END; +$$ +ERROR HY000: 'sys_refcursor' is not allowed in this context +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; +DROP TABLE t1; +# RECORD and REFCURSOR in different packages +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +# 2-step type in RETURN in a schema procedure +CREATE PROCEDURE p1 AS +TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; +c0 cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b',3; +END; +$$ +ERROR HY000: Illegal parameter data types row<2> and row<3> for operation 'OPEN..FOR' +CREATE PROCEDURE p1 AS +TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; +c0 cur0_t; +v0 INT; +v1 VARCHAR(10); +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO v0, v1; +SELECT v0, v1; +CLOSE c0; +END; +$$ +CALL p1; +v0 v1 +1 b +DROP PROCEDURE p1; +# 3-step type in RETURN in a schema procedure +CREATE PROCEDURE p1 AS +TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; +c0 cur0_t; +BEGIN +OPEN c0 FOR SELECT 1,'b',3; +END; +$$ +ERROR HY000: Illegal parameter data types row<2> and row<3> for operation 'OPEN..FOR' +CREATE PROCEDURE p1 AS +TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; +c0 cur0_t; +v0 INT; +v1 VARCHAR(10); +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO v0, v1; +SELECT v0, v1; +CLOSE c0; +END; +$$ +CALL p1; +v0 v1 +1 b +DROP PROCEDURE p1; +# 2-step type in RETURN in another package +CREATE PACKAGE pkg2 AS +TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; +END; +$$ +# 2-step cursor type with 2-step type in RETURN +CREATE PROCEDURE p1 AS +c0 pkg2.cur0_t; +v0 INT; +v1 VARCHAR(10); +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO v0, v1; +SELECT v0, v1; +CLOSE c0; +END; +$$ +CALL p1; +v0 v1 +1 b +DROP PROCEDURE p1; +# 3-step cursor type with 2-step type in RETURN +CREATE PROCEDURE p1 AS +c0 test.pkg2.cur0_t; +v0 INT; +v1 VARCHAR(10); +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO v0, v1; +SELECT v0, v1; +CLOSE c0; +END; +$$ +CALL p1; +v0 v1 +1 b +DROP PROCEDURE p1; +# 3-step cursor type (different db) with 2-step type in RETURN +CREATE DATABASE test1; +USE test1; +CREATE PROCEDURE p1 AS +c0 test.pkg2.cur0_t; +v0 INT; +v1 VARCHAR(10); +BEGIN +OPEN c0 FOR SELECT 1,'b'; +FETCH c0 INTO v0, v1; +SELECT v0, v1; +CLOSE c0; +END; +$$ +CALL p1; +v0 v1 +1 b +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/r/sp-package-spec-type.result b/mysql-test/suite/compat/oracle/r/sp-package-spec-type.result new file mode 100644 index 0000000000000..5aca893b8bbfe --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-package-spec-type.result @@ -0,0 +1,64 @@ +SET sql_mode=ORACLE; +# +# MDEV-39587 Package-wide TYPE for variable declarations +# +# PACKAGE gets dropped making an orphan type reference: 2-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +CREATE PROCEDURE p1 AS +r0 pkg1.rec0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +10 b +DROP PACKAGE pkg1; +CALL p1; +ERROR HY000: Failed to load routine test.p1 (internal code -6). For more details, run SHOW WARNINGS +SHOW WARNINGS; +Level Code Message +Error 4161 Unknown data type: '`pkg1`.`rec0_t`' +Error 1457 Failed to load routine test.p1 (internal code -6). For more details, run SHOW WARNINGS +DROP PROCEDURE p1; +# PACKAGE gets dropped making an orphan type reference: 3-step +CREATE PACKAGE pkg1 AS +TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +CREATE DATABASE test1; +USE test1; +CREATE PROCEDURE p1 AS +r0 test.pkg1.rec0_t; +BEGIN +r0.a:= 10; +r0.b:= 'b'; +SELECT r0.a, r0.b; +END; +$$ +CALL p1; +r0.a r0.b +10 b +DROP PACKAGE test.pkg1; +CALL p1; +ERROR HY000: Failed to load routine test1.p1 (internal code -6). For more details, run SHOW WARNINGS +SHOW WARNINGS; +Level Code Message +Error 4161 Unknown data type: '`test`.`pkg1`.`rec0_t`' +Error 1457 Failed to load routine test1.p1 (internal code -6). For more details, run SHOW WARNINGS +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; +# Too long identifier chain +CREATE PROCEDURE p1 AS +v1 cat1.db1.pkg1.type1; +BEGIN +NULL; +END; +$$ +ERROR HY000: Unknown data type: '`cat1`.`db1`.`pkg1`' diff --git a/mysql-test/suite/compat/oracle/r/type_date.result b/mysql-test/suite/compat/oracle/r/type_date.result index 1d4a16c0d289a..ce3258a4142b7 100644 --- a/mysql-test/suite/compat/oracle/r/type_date.result +++ b/mysql-test/suite/compat/oracle/r/type_date.result @@ -126,7 +126,7 @@ DROP TABLE t1; # # Qualified syntax is not supported yet in SP # See MDEV-23353 Qualified data types in SP -# For now it's trying to parse as "RETURN mariadb_schema.DATE%ROWTYPE" +# For now it's trying to parse mariadb_schema as a package name # SET sql_mode=ORACLE; CREATE FUNCTION f1() RETURN mariadb_schema.DATE AS @@ -134,26 +134,17 @@ BEGIN RETURN CURRENT_DATE; 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 CURRENT_DATE; -END' at line 2 +ERROR HY000: Unknown data type: '`mariadb_schema`.`DATE`' CREATE PROCEDURE p1(a mariadb_schema.DATE) AS BEGIN NULL; 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 -NULL; -END' at line 1 +ERROR HY000: Unknown data type: '`mariadb_schema`.`DATE`' CREATE PROCEDURE p1() AS a mariadb_schema.DATE; BEGIN NULL; 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 '; -BEGIN -NULL; -END' at line 2 +ERROR HY000: Unknown data type: '`mariadb_schema`.`DATE`' diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type-assoc.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-assoc.test new file mode 100644 index 0000000000000..884d37c0cf1d5 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-assoc.test @@ -0,0 +1,556 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + +--echo # A too long identifier chain in the TABLE OF type + +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PACKAGE pkg1 AS + TYPE assoc0_t IS TABLE OF cat1.db1.pkg1.rec1_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + + +--echo # Assoc array cannot have attributes +--echo # Currently this is not allowed by the parser in qualified types +--echo # If we ever allow, make sure to call check_data_type_attributes() +--echo # which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.assoc0_t (0); -- Length attribute +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.assoc0_t (0,0); -- Length and dec attributes +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.assoc0_t CHARACTER SET latin1; +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.assoc0_t COLLATE latin1_bin; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + +--echo # PACKAGE spec types cannot be used in schema routine parameters +--echo # and schema function RETURN + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +--error ER_WRONG_USAGE +CREATE PROCEDURE p1(a pkg1.assoc0_t) AS +BEGIN + NULL; +END; +$$ +--error ER_WRONG_USAGE +CREATE FUNCTION f1 RETURN pkg1.assoc0_t AS +BEGIN + RETURN NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + +--echo # A package routine parameter and a package function RETURN +--echo # 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +# Assoc array is not supported as a routine parameter and RETURN anyway +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(a pkg1.assoc0_t) AS + BEGIN + SELECT a(0).a, a(0).b; + END; + FUNCTION f1 RETURN pkg1.assoc0_t AS + res pkg1.assoc0_t; + BEGIN + res(0).a:= 10; + res(0).b:= 'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; + +--echo # RECORD as a package routine parameter and a package function RETURN +--echo # 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE assoc0_t IS TABLE OF rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +USE test1; + +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +# Assoc array is not supported as a routine parameter and RETURN anyway +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(a test.pkg1.assoc0_t) AS + BEGIN + SELECT a(0).a, a(0).b; + END; + FUNCTION f1 RETURN test.pkg1.assoc0_t AS + res test.pkg1.assoc0_t; + BEGIN + res(0).a:= 10; + res(0).b:= 'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; + + +--echo # A self-reference to the current spec with error: 2-step + +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PACKAGE pkg1 AS + TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PACKAGE test1.pkg1 AS + TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ +DROP DATABASE test1; + + +--echo # A self-reference to the current spec with success: 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t; + a0 pkg1.assoc0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP PACKAGE pkg1; + +CREATE DATABASE test1; +DELIMITER $$; +CREATE PACKAGE test1.pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE test1.p1 AS + r0 pkg1.rec0_t; -- This refers to test1.pkg1.rec0_t + a0 pkg1.assoc0_t; -- This refers to test1.pkg1.assoc0_t +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL test1.p1; +DROP PROCEDURE test1.p1; +DROP PACKAGE test1.pkg1; +DROP DATABASE test1; +USE test; + + +--echo # A self-reference to the current spec with error: 3-step + +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PACKAGE pkg1 AS + TYPE assoc0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PACKAGE test1.pkg1 AS + TYPE assoc0_t IS TABLE OF test1.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ +DROP DATABASE test1; + + +--echo # A self-reference to the current spec with success: 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t; + a0 pkg1.assoc0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP PACKAGE pkg1; + + +CREATE DATABASE test1; +DELIMITER $$; +CREATE PACKAGE test1.pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF test1.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +CREATE PROCEDURE test1.p1 AS + r0 pkg1.rec0_t; -- This refers to test1.pkg1.rec0_t + a0 pkg1.assoc0_t; -- This refers to test1.pkg1.assoc0_t +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL test1.p1; +DROP PROCEDURE test1.p1; +DROP PACKAGE test1.pkg1; +DROP DATABASE test1; + + +--echo # Various working examples +CREATE TABLE t1 (a INT, b VARCHAR(30)); + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE varchar_array IS TABLE OF VARCHAR(2000) INDEX BY INTEGER; + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); + TYPE array0_t IS TABLE OF rec0_t INDEX BY INTEGER; + TYPE array1_t IS TABLE OF test.t1.a%TYPE INDEX BY INTEGER; + TYPE array2_t IS TABLE OF test.t1%ROWTYPE INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +--echo # 2-Step + PROCEDURE +DELIMITER $$; +CREATE PROCEDURE p1 AS + v0 pkg1.varchar_array; + v1 pkg1.rec0_t; + v2 pkg1.array0_t; + v3 pkg1.array1_t; + v4 pkg1.array2_t; +BEGIN + v0(0):='test'; + SELECT v0(0); + v1.a:= 1; + v1.b:= 'b'; + SELECT v1.a, v1.b; + v2(0):= v1; + SELECT v2(0).a, v2(0).b; + v3(0):= 10; + SELECT v3(0); + v4(0):= v1; + SELECT v4(0).a, v4(0).b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # 3-Step + PROCEDURE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PROCEDURE p1 AS + v0 test.pkg1.varchar_array; + v1 test.pkg1.rec0_t; + v2 test.pkg1.array0_t; + v3 test.pkg1.array1_t; + v4 test.pkg1.array2_t; +BEGIN + v0(0):='test'; + SELECT v0(0); + v1.a:= 1; + v1.b:= 'b'; + SELECT v1.a, v1.b; + v2(0):= v1; + SELECT v2(0).a, v2(0).b; + v3(0):= 10; + SELECT v3(0); + v4(0):= v1; + SELECT v4(0).a, v4(0).b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; + +--echo # 2-Step + PACKAGE + Declarations in package PROCEDURE +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v0 pkg1.varchar_array; + v1 pkg1.rec0_t; + v2 pkg1.array0_t; + v3 pkg1.array1_t; + v4 pkg1.array2_t; + BEGIN + v0(0):='test'; + SELECT v0(0); + v1.a:= 1; + v1.b:= 'b'; + SELECT v1.a, v1.b; + v2(0):= v1; + SELECT v2(0).a, v2(0).b; + v3(0):= 10; + SELECT v3(0); + v4(0):= v1; + SELECT v4(0).a, v4(0).b; + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; + +--echo # 3-Step + PACKAGE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v0 test.pkg1.varchar_array; + v1 test.pkg1.rec0_t; + v2 test.pkg1.array0_t; + v3 test.pkg1.array1_t; + v4 test.pkg1.array2_t; + BEGIN + v0(0):='test'; + SELECT v0(0); + v1.a:= 1; + v1.b:= 'b'; + SELECT v1.a, v1.b; + v2(0):= v1; + SELECT v2(0).a, v2(0).b; + v3(0):= 10; + SELECT v3(0); + v4(0):= v1; + SELECT v4(0).a, v4(0).b; + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +--echo # 3-Step + PACKAGE + Declarations in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + v0 test.pkg1.varchar_array; + v1 test.pkg1.rec0_t; + v2 test.pkg1.array0_t; + v3 test.pkg1.array1_t; + v4 test.pkg1.array2_t; + PROCEDURE p1 AS + BEGIN + v0(0):='test'; + SELECT v0(0); + v1.a:= 1; + v1.b:= 'b'; + SELECT v1.a, v1.b; + v2(0):= v1; + SELECT v2(0).a, v2(0).b; + v3(0):= 10; + SELECT v3(0); + v4(0):= v1; + SELECT v4(0).a, v4(0).b; + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +DROP PACKAGE pkg1; +DROP TABLE t1; + + +--echo # RECORD + Assoc array in different packages: 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +DELIMITER ;$$ + +DELIMITER $$; +CREATE PACKAGE pkg2 AS + TYPE array0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +DELIMITER $$; +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t; + a0 pkg2.array0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + a0(0):= r0; + SELECT a0(0).a, a0(0).b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; + + +--echo # RECORD + Assoc array in different packages: 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + TYPE array0_t IS TABLE OF test.pkg1.rec0_t INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test2; +USE test2; +DELIMITER $$; +CREATE PROCEDURE p1 AS + r0 test.pkg1.rec0_t; + a0 test1.pkg2.array0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + a0(0):= r0; + SELECT a0(0).a, a0(0).b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP DATABASE test2; + +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type-inet6.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-inet6.test new file mode 100644 index 0000000000000..7020524dcfb10 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-inet6.test @@ -0,0 +1,129 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE inet6_array IS TABLE OF INET6 INDEX BY INTEGER; +END; +$$ +DELIMITER ;$$ + +--echo # 2-Step + PROCEDURE +DELIMITER $$; +CREATE PROCEDURE p1 AS + v0 pkg1.inet6_array; +BEGIN + v0(0):='AA::BB'; + SELECT v0(0); +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # 2-Step + PACKAGE + Declarations in package PROCEDURE +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v0 pkg1.inet6_array; + BEGIN + v0(0):='AA::BB'; + SELECT v0(0); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; + +--echo # 2-Step + PACKAGE + Declaration in PACKAGE BODY +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + v0 pkg1.inet6_array; + PROCEDURE p1 AS + BEGIN + v0(0):='AA::BB'; + SELECT v0(0); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; + + +--echo # 3-Step + PROCEDURE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PROCEDURE p1 AS + v0 test.pkg1.inet6_array; +BEGIN + v0(0):='AA::BB'; + SELECT v0(0); +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; + +--echo # 3-Step + PACKAGE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v0 test.pkg1.inet6_array; + BEGIN + v0(0):='AA::BB'; + SELECT v0(0); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +--echo # 3-Step + PACKAGE + Declaration in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + v0 test.pkg1.inet6_array; + PROCEDURE p1 AS + BEGIN + v0(0):='AA::BB'; + SELECT v0(0); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type-path.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-path.test new file mode 100644 index 0000000000000..dfb114ee644a4 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-path.test @@ -0,0 +1,113 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + + +--echo # PATH resolving not to CURRENT_SCHEMA + +CREATE DATABASE test1; +DELIMITER $$; +CREATE PACKAGE test1.pkg1 AS + TYPE rec0_t IS RECORD (a INT, b_test1 VARCHAR(10)); +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test2; +DELIMITER $$; +CREATE PACKAGE test2.pkg1 AS + TYPE rec0_t IS RECORD (a INT, b_test2 VARCHAR(10)); +END; +$$ +DELIMITER ;$$ + +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PROCEDURE p1 AS + a pkg1.rec0_t; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ + + +# Have pkg1.rec0_t resolve to test1.pkg1.rec0_t +SET PATH 'CURRENT_SCHEMA,test1,test2'; + +DELIMITER $$; +CREATE PROCEDURE p1 AS + a pkg1.rec0_t; +BEGIN + a.b_test1:= 10; + SELECT a.b_test1; +END; +$$ +DELIMITER ;$$ +CALL test.p1(); +DROP PROCEDURE p1; + + +# Have pkg1.rec0_t resolve to test2.pkg1.rec0_t +SET PATH 'CURRENT_SCHEMA,test2,test1'; + +DELIMITER $$; +CREATE PROCEDURE p1 AS + a pkg1.rec0_t; +BEGIN + a.b_test2:= 10; + SELECT a.b_test2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; + +SET PATH DEFAULT; + +DROP DATABASE test1; +DROP DATABASE test2; + + +--echo # PATH resolving to CURRENT_SCHEMA + +CREATE DATABASE test1; +DELIMITER $$; +CREATE PACKAGE test.pkg1 AS + TYPE rec0_t IS RECORD (a INT, b_test VARCHAR(10)); +END; +$$ +DELIMITER ;$$ + +SET PATH 'test1,CURRENT_SCHEMA'; +DELIMITER $$; +CREATE PROCEDURE p1 AS + a pkg1.rec0_t; +BEGIN + a.b_test:= 10; + SELECT a.b_test; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; + +SET PATH 'CURRENT_SCHEMA,test1'; +DELIMITER $$; +CREATE PROCEDURE p1 AS + a pkg1.rec0_t; +BEGIN + a.b_test:= 10; + SELECT a.b_test; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; + +DROP PACKAGE pkg1; +DROP DATABASE test1; + +SET PATH DEFAULT; diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type-record.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-record.test new file mode 100644 index 0000000000000..dba685eba0ecd --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-record.test @@ -0,0 +1,153 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + +--echo # RECORD cannot have attributes +--echo # Currently this is not allowed by the parser in qualified types +--echo # If we ever allow, make sure to call check_data_type_attributes() +--echo # which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t (0); -- Length attribute +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t (0,0); -- Length and dec attributes +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t CHARACTER SET latin1; +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t COLLATE latin1_bin; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + +--echo # PACKAGE spec types cannot be used in schema routine parameters +--echo # and schema function RETURN + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +--error ER_WRONG_USAGE +CREATE PROCEDURE p1(a pkg1.rec0_t) AS +BEGIN + NULL; +END; +$$ +--error ER_WRONG_USAGE +CREATE FUNCTION f1 RETURN pkg1.rec0_t AS +BEGIN + RETURN NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + +--echo # RECORD as a package routine parameter and a package function RETURN +--echo # 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(r pkg1.rec0_t) AS + BEGIN + SELECT r.a, r.b; + END; + FUNCTION f1 RETURN pkg1.rec0_t AS + res pkg1.rec0_t; + BEGIN + res.a:= 10; + res.b:= 'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1(); +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; + +--echo # RECORD as a package routine parameter and a package function RETURN +--echo # 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +USE test1; + +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(r test.pkg1.rec0_t) AS + BEGIN + SELECT r.a, r.b; + END; + FUNCTION f1 RETURN test.pkg1.rec0_t AS + res test.pkg1.rec0_t; + BEGIN + res.a:= 10; + res.b:= 'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1(); +CALL test1.pkg2.p1(); +USE test; +CALL test1.pkg2.p1(); +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type-refcursor.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-refcursor.test new file mode 100644 index 0000000000000..b0a32cf47e278 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type-refcursor.test @@ -0,0 +1,591 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + +--echo # REFCURSOR cannot have attributes +--echo # Currently this is not allowed by the parser in qualified types +--echo # If we ever allow, make sure to call check_data_type_attributes() +--echo # which will raise ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.cur0_t (0); -- Length attribute +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.cur0_t (0,0); -- Length and dec attributes +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.cur0_t CHARACTER SET latin1; +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1 AS + r0 pkg1.cur0_t COLLATE latin1_bin; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + + +--echo # PACKAGE spec types cannot be used in schema routine parameters +--echo # and schema function RETURN + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +--error ER_WRONG_USAGE +CREATE PROCEDURE p1(a pkg1.cur0_t) AS +BEGIN + NULL; +END; +$$ +--error ER_WRONG_USAGE +CREATE FUNCTION f1 RETURN pkg1.cur0_t AS +BEGIN + RETURN NULL; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg1; + + +--echo # A package routine parameter and a package function RETURN +--echo # 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(c pkg1.cur0_t) AS + v0 INT; + v1 VARCHAR(32); + BEGIN + FETCH c INTO v0, v1; + SELECT v0, v1; + CLOSE c; + END; + FUNCTION f1 RETURN pkg1.cur0_t AS + res pkg1.cur0_t; + BEGIN + OPEN res FOR SELECT 1,'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1(); +DROP PACKAGE pkg2; +DROP PACKAGE pkg1; + + +--echo # A package routine parameter and a package function RETURN +--echo # 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(10)); + TYPE cur0_t IS REF CURSOR RETURN rec0_t; +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +USE test1; + +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p2(c test.pkg1.cur0_t) AS + v0 INT; + v1 VARCHAR(32); + BEGIN + FETCH c INTO v0, v1; + SELECT v0, v1; + CLOSE c; + END; + FUNCTION f1 RETURN test.pkg1.cur0_t AS + res test.pkg1.cur0_t; + BEGIN + OPEN res FOR SELECT 1, 'b'; + RETURN res; + END; + PROCEDURE p1 AS + BEGIN + CALL p2(f1()); + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1(); +CALL test1.pkg2.p1(); +USE test; +CALL test1.pkg2.p1(); +USE test1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; +DROP PACKAGE pkg1; + + +--echo # A self-reference to the current spec with success: 2-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; -- Self ref +END; +$$ + +--echo # Using from the same database +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t; + c0 pkg1.cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO r0; + CLOSE c0; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # Using from a different database +CREATE DATABASE test1; +DELIMITER $$; +CREATE PROCEDURE test1.p1 AS + r0 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO r0; + CLOSE c0; + SELECT r0.a, r0.b; + NULL; +END; +$$ +DELIMITER ;$$ +CALL test1.p1; +DROP DATABASE test1; + +DROP PACKAGE pkg1; + + +--echo # A self-reference to the current spec with success: 3-step + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; -- Self ref +END; +$$ + +--echo # Using from the same database +CREATE PROCEDURE p1 AS + r0 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO r0; + CLOSE c0; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # Using from a different database +CREATE DATABASE test1; +DELIMITER $$; +CREATE PROCEDURE test1.p1 AS + r0 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO r0; + CLOSE c0; + SELECT r0.a, r0.b; + NULL; +END; +$$ +DELIMITER ;$$ +CALL test1.p1; +DROP DATABASE test1; + +DROP PACKAGE pkg1; + + +--echo # Various working examples +CREATE TABLE t1 (a INT, b VARCHAR(30)); + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); + TYPE cur0_t IS REF CURSOR; + TYPE cur1_t IS REF CURSOR RETURN rec0_t; + TYPE cur2_t IS REF CURSOR RETURN test.t1%ROWTYPE; +END; +$$ +DELIMITER ;$$ + +--echo # 2-Step + PROCEDURE +DELIMITER $$; +CREATE PROCEDURE p1 AS + v1 pkg1.rec0_t; + c0 pkg1.cur0_t; + c1 pkg1.cur1_t; + c2 pkg1.cur2_t; + fetch0 INT; + fetch1 VARCHAR(30); +BEGIN + v1.a:= 1; + v1.b:= 'b'; + + OPEN c0 FOR SELECT 10,'bb0'; + FETCH c0 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c0; + + OPEN c1 FOR SELECT 11,'bb1'; + FETCH c1 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c1; + + OPEN c2 FOR SELECT 12,'bb2'; + FETCH c2 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c2; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # 3-Step + PROCEDURE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PROCEDURE p1 AS + v1 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; + c1 test.pkg1.cur1_t; + c2 test.pkg1.cur2_t; + fetch0 INT; + fetch1 VARCHAR(30); +BEGIN + v1.a:= 1; + v1.b:= 'b'; + + OPEN c0 FOR SELECT 10,'bb0'; + FETCH c0 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c0; + + OPEN c1 FOR SELECT 11,'bb1'; + FETCH c1 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c1; + + OPEN c2 FOR SELECT 12,'bb2'; + FETCH c2 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c2; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; + +--echo # 2-Step + PACKAGE + Declarations in package PROCEDURE +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v1 pkg1.rec0_t; + c0 pkg1.cur0_t; + c1 pkg1.cur1_t; + c2 pkg1.cur2_t; + fetch0 INT; + fetch1 VARCHAR(30); + BEGIN + v1.a:= 1; + v1.b:= 'b'; + + OPEN c0 FOR SELECT 10,'bb0'; + FETCH c0 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c0; + + OPEN c1 FOR SELECT 11,'bb1'; + FETCH c1 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c1; + + OPEN c2 FOR SELECT 12,'bb2'; + FETCH c2 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c2; + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; + +--echo # 3-Step + PACKAGE + Declarations in package PROCEDURE +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +CREATE PACKAGE BODY pkg2 AS + PROCEDURE p1 AS + v1 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; + c1 test.pkg1.cur1_t; + c2 test.pkg1.cur2_t; + fetch0 INT; + fetch1 VARCHAR(30); + BEGIN + v1.a:= 1; + v1.b:= 'b'; + + OPEN c0 FOR SELECT 10,'bb0'; + FETCH c0 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c0; + + OPEN c1 FOR SELECT 11,'bb1'; + FETCH c1 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c1; + + OPEN c2 FOR SELECT 12,'bb2'; + FETCH c2 INTO fetch0, fetch1; + SELECT fetch0, fetch1; + CLOSE c2; + END; +END; +$$ +DELIMITER ;$$ +CALL pkg2.p1; +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +--echo # 3-Step + PACKAGE + Declarations in PACKAGE BODY +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PACKAGE pkg2 AS + PROCEDURE p1; +END; +$$ +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE PACKAGE BODY pkg2 AS + v1 test.pkg1.rec0_t; + c0 test.pkg1.cur0_t; + c1 test.pkg1.cur1_t; + c2 test.pkg1.cur2_t; + fetch0 INT; + fetch1 VARCHAR(30); + PROCEDURE p1 AS + BEGIN + NULL; + END; +END; +$$ +DELIMITER ;$$ +DROP PACKAGE pkg2; +DROP DATABASE test1; +USE test; + +DROP PACKAGE pkg1; +DROP TABLE t1; + + +--echo # RECORD and REFCURSOR in different packages + +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +DELIMITER ;$$ + +--echo # 2-step type in RETURN in a schema procedure +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE PROCEDURE p1 AS + TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; + c0 cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b',3; +END; +$$ +DELIMITER ;$$ + +DELIMITER $$; +CREATE PROCEDURE p1 AS + TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; + c0 cur0_t; + v0 INT; + v1 VARCHAR(10); +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO v0, v1; + SELECT v0, v1; + CLOSE c0; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + + +--echo # 3-step type in RETURN in a schema procedure + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +CREATE PROCEDURE p1 AS + TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; + c0 cur0_t; +BEGIN + OPEN c0 FOR SELECT 1,'b',3; +END; +$$ +DELIMITER ;$$ + + +DELIMITER $$; +CREATE PROCEDURE p1 AS + TYPE cur0_t IS REF CURSOR RETURN test.pkg1.rec0_t; + c0 cur0_t; + v0 INT; + v1 VARCHAR(10); +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO v0, v1; + SELECT v0, v1; + CLOSE c0; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + + +--echo # 2-step type in RETURN in another package +DELIMITER $$; +CREATE PACKAGE pkg2 AS + TYPE cur0_t IS REF CURSOR RETURN pkg1.rec0_t; +END; +$$ +DELIMITER ;$$ + +--echo # 2-step cursor type with 2-step type in RETURN +DELIMITER $$; +CREATE PROCEDURE p1 AS + c0 pkg2.cur0_t; + v0 INT; + v1 VARCHAR(10); +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO v0, v1; + SELECT v0, v1; + CLOSE c0; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +--echo # 3-step cursor type with 2-step type in RETURN +DELIMITER $$; +CREATE PROCEDURE p1 AS + c0 test.pkg2.cur0_t; + v0 INT; + v1 VARCHAR(10); +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO v0, v1; + SELECT v0, v1; + CLOSE c0; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + + +--echo # 3-step cursor type (different db) with 2-step type in RETURN +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PROCEDURE p1 AS + c0 test.pkg2.cur0_t; + v0 INT; + v1 VARCHAR(10); +BEGIN + OPEN c0 FOR SELECT 1,'b'; + FETCH c0 INTO v0, v1; + SELECT v0, v1; + CLOSE c0; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; + +DROP PACKAGE pkg2; + +DROP PACKAGE pkg1; diff --git a/mysql-test/suite/compat/oracle/t/sp-package-spec-type.test b/mysql-test/suite/compat/oracle/t/sp-package-spec-type.test new file mode 100644 index 0000000000000..fea995492c767 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-package-spec-type.test @@ -0,0 +1,73 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-39587 Package-wide TYPE for variable declarations +--echo # + +--echo # PACKAGE gets dropped making an orphan type reference: 2-step +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +DELIMITER ;$$ + +DELIMITER $$; +CREATE PROCEDURE p1 AS + r0 pkg1.rec0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PACKAGE pkg1; +--error ER_SP_PROC_TABLE_CORRUPT +CALL p1; +SHOW WARNINGS; +DROP PROCEDURE p1; + + +--echo # PACKAGE gets dropped making an orphan type reference: 3-step +DELIMITER $$; +CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD(a INT, b VARCHAR(30)); +END; +$$ +DELIMITER ;$$ + +CREATE DATABASE test1; +USE test1; +DELIMITER $$; +CREATE PROCEDURE p1 AS + r0 test.pkg1.rec0_t; +BEGIN + r0.a:= 10; + r0.b:= 'b'; + SELECT r0.a, r0.b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PACKAGE test.pkg1; +--error ER_SP_PROC_TABLE_CORRUPT +CALL p1; +SHOW WARNINGS; +DROP PROCEDURE p1; +DROP DATABASE test1; +USE test; + + +--echo # Too long identifier chain + +DELIMITER $$; +--error ER_UNKNOWN_DATA_TYPE +CREATE PROCEDURE p1 AS + v1 cat1.db1.pkg1.type1; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ diff --git a/mysql-test/suite/compat/oracle/t/type_date.test b/mysql-test/suite/compat/oracle/t/type_date.test index 31901a50722a3..a91b534cbb452 100644 --- a/mysql-test/suite/compat/oracle/t/type_date.test +++ b/mysql-test/suite/compat/oracle/t/type_date.test @@ -75,24 +75,24 @@ DROP TABLE t1; --echo # --echo # Qualified syntax is not supported yet in SP --echo # See MDEV-23353 Qualified data types in SP ---echo # For now it's trying to parse as "RETURN mariadb_schema.DATE%ROWTYPE" +--echo # For now it's trying to parse mariadb_schema as a package name --echo # SET sql_mode=ORACLE; DELIMITER $$; ---error ER_PARSE_ERROR +--error ER_UNKNOWN_DATA_TYPE CREATE FUNCTION f1() RETURN mariadb_schema.DATE AS BEGIN RETURN CURRENT_DATE; END; $$ ---error ER_PARSE_ERROR +--error ER_UNKNOWN_DATA_TYPE CREATE PROCEDURE p1(a mariadb_schema.DATE) AS BEGIN NULL; END; $$ ---error ER_PARSE_ERROR +--error ER_UNKNOWN_DATA_TYPE CREATE PROCEDURE p1() AS a mariadb_schema.DATE; BEGIN diff --git a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.result b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.result index 23ed048127f8d..b2f2c83a4f069 100644 --- a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.result +++ b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.result @@ -8,10 +8,7 @@ BEGIN NULL; 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 '.v0%ROWTYPE; -BEGIN -NULL; -END' at line 2 +ERROR HY000: Unknown data type: '`cat1`.`db1`.`t1`' CREATE PROCEDURE p1 AS TYPE cur0_t IS REF CURSOR RETURN cat1.db1.t1%ROWTYPE; BEGIN diff --git a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.test b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.test index 30dbcda9d965b..687eac3dd8736 100644 --- a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.test +++ b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-rowtype-of-table.test @@ -9,7 +9,7 @@ SET sql_mode=ORACLE; # DELIMITER $$; ---error ER_PARSE_ERROR +--error ER_UNKNOWN_DATA_TYPE CREATE PROCEDURE p1 AS TYPE cur0_t IS REF CURSOR RETURN cat1.db1.t1.v0%ROWTYPE; BEGIN diff --git a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.result b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.result index 1980db64be5b0..bbac678175012 100644 --- a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.result +++ b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.result @@ -11,10 +11,7 @@ BEGIN NULL; 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 '.v0%TYPE; -BEGIN -NULL; -END' at line 2 +ERROR HY000: Unknown data type: '`cat1`.`db1`.`t1`' CREATE PROCEDURE p1 AS TYPE cur0_t IS REF CURSOR RETURN db1.t1.v0%TYPE; BEGIN diff --git a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.test b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.test index 1470281ec99ff..d0e5908287529 100644 --- a/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.test +++ b/plugin/type_cursor/mysql-test/type_cursor/sp-ref_cursor-return-type-of-var.test @@ -10,7 +10,7 @@ SET sql_mode=ORACLE; --echo # DELIMITER $$; ---error ER_PARSE_ERROR +--error ER_UNKNOWN_DATA_TYPE CREATE PROCEDURE p1 AS TYPE cur0_t IS REF CURSOR RETURN cat1.db1.t1.v0%TYPE; BEGIN diff --git a/sql/sp.cc b/sql/sp.cc index cbaf72b939814..d041fc310732f 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -2524,6 +2524,42 @@ Sp_handler::sp_cache_routine_reentrant(THD *thd, } +/* + Find and cache a routine in a parser safe mode and suppress all errors. +*/ +int +Sp_handler::sp_cache_routine_reentrant_suppress_errors(THD *thd, + const Database_qualified_name *name, + sp_head **sp) const +{ + Dummy_error_handler err_handler; + thd->push_internal_handler(&err_handler); + int ret= sp_cache_routine_reentrant(thd, name, sp); + thd->pop_internal_handler(); + return ret; +} + + +/* + Find and cache a PACKAGE spec in a parser-safe reentrant mode. + @param db - The database name + @param package - The package name + @retval - A null ptr if some error happened. + Or a pointer to the PACKAGE spec. +*/ +sp_package *Sp_handler::find_package_spec(THD *thd, + const Lex_ident_db &db, + const LEX_CSTRING &package) +{ + sp_head *sp= nullptr; + Database_qualified_name tmp(db, package); + bool ret= sp_handler_package_spec. + sp_cache_routine_reentrant_suppress_errors(thd, &tmp, &sp); + sp_package *spec= (!ret && sp) ? sp->get_package() : nullptr; + return spec; +} + + /** 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 @@ -2557,15 +2593,7 @@ is_package_public_routine(THD *thd, const LEX_CSTRING &routine, enum_sp_type type) { - sp_head *sp= NULL; - Database_qualified_name tmp(db, package); - - Dummy_error_handler err_handler; - thd->push_internal_handler(&err_handler); - bool ret= sp_handler_package_spec.sp_cache_routine_reentrant(thd, &tmp, &sp); - thd->pop_internal_handler(); - - sp_package *spec= (!ret && sp) ? sp->get_package() : NULL; + sp_package *spec= Sp_handler::find_package_spec(thd, db, package); return spec && spec->m_routine_declarations.find(routine, type); } diff --git a/sql/sp.h b/sql/sp.h index 47f2495db3e26..05ea336996026 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -137,6 +137,9 @@ class Sp_handler const Sp_handler *sph= handler(type); return sph ? sph->sp_handler_mysql_proc() : NULL; } + static sp_package *find_package_spec(THD *thd, + const Lex_ident_db &db, + const LEX_CSTRING &package); const char *type_str() const { return type_lex_cstring().str; } virtual const char *show_create_routine_col1_caption() const @@ -211,6 +214,10 @@ class Sp_handler const Database_qualified_name *nm, sp_head **sp) const; + int sp_cache_routine_reentrant_suppress_errors(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, const Database_qualified_name *name) const; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 5d52b7f109dac..68520011d6d38 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -4247,3 +4247,236 @@ ulong sp_head::sp_cache_version() const { return m_parent ? m_parent->sp_cache_version() : m_sp_cache_version; } + + +/* + Check if the column definition "def" with type "type" can be used in "this". + + These kind of declarations are not allowed in schema routines: + PROCEDURE p1(a pkg1.type1) - a schema routine parameter type + FUNCTION f1() RETURN pkg1.type1; - a schema function RETURN types + because they show up in INFORMATION_SCHEMA.PARAMETERS. + The code is not ready to handle this correctly. + + Note, using them for package routines are OK, because package routines + are not displayed in INFORMATION_SCHEMA. +*/ +bool sp_head::check_applicability(THD *thd, + const Lex_field_type_st &def, + column_definition_type_t type) +{ + if (def.foreign_module_type()) + { + if ((m_handler == &sp_handler_procedure || + m_handler == &sp_handler_function)/*i.e. not a package routine*/ && + (type == COLUMN_DEFINITION_ROUTINE_PARAM || + type == COLUMN_DEFINITION_FUNCTION_RETURN)) + { + my_error(ER_WRONG_USAGE, MYF(0), + type == COLUMN_DEFINITION_ROUTINE_PARAM ? + "parameter_declaration" : "RETURN", + "package_name.type_name"); + return true; + } + } + return false; +} + + +void sp_head::raise_unknown_data_type(const Lex_ident_db_normalized &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + char buf[128]; + my_snprintf(buf, sizeof(buf), "%.*sQ.%.*sQ.%.*sQ", + (int) db.length, db.str, + (int) package.length, package.str, + (int) type.length, type.str); + my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), buf); +} + + +void sp_head::raise_unknown_data_type(const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + char buf[128]; + my_snprintf(buf, sizeof(buf), "%.*sQ.%.*sQ", + (int) package.length, package.str, + (int) type.length, type.str); + my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), buf); +} + + +/* + Get the definition of the TYPE `dbn`.`package`.`type`. + If the definition is not found, then an error message is raises. + + @param OUT tdef - The pointer to the found TYPE definition + @param dbn - The nornamized database name (used for error messages) + @param package - The package name (used for error messages) + @param type - The data type name + + @retval false - If the TYPE definition was found and written to "tdef" + @retval true - If the data type was found found or some error happened. +*/ + +bool sp_package::get_typedef(THD *thd, + const sp_type_def **tdef, + const Lex_ident_db_normalized dbn, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + if (!(tdef[0]= get_parse_context()->child_context(0)-> + find_type_def(type, false))) + { + if (dbn.str) + raise_unknown_data_type(dbn, package, type); + else + raise_unknown_data_type(package, type); + return true; + } + return false; +} + + +/* + Get a 2-step qualified TYPE defined in a PACKAGE + consisting by the package name and type name. + Handles the following cases: + - A PACKAGE refers to its own type using a qualified name + - A routine refers to the PACKAGE in the database of the routine + using a qualified type + - Otherwise, the database which contains a package "package" with the type + "type" is resolved using the @@PATH variable. + + @param OUT tdef - The pointer to the TYPE definition + @param package - The package name + @param type - The data type name + + @retval false - The package "package" with the data type "type" was found, + and tdef[0] was set to the pointer of the found type. + @retval true - The data type was not found, or some error happened + during type resolution. +*/ +bool sp_head::get_typedef_package_spec(THD *thd, + const sp_type_def **tdef, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + /* + Catch a special case first: a reference to the current package spec. + Note, cast to Lex_ident_routine is safe below because m_name gets + to sp_head after Lex_ident_routine::check_name_with_error(). + */ + if (m_handler == &sp_handler_package_spec && + Lex_ident_routine(m_name).streq(package)) + { + /* + The data type pkg1.type1 refers the current package specifications + for the packge pkg1: + CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER; + END; + Avoid a recursive find_package_spec_type(). + */ + sp_package *spec= get_package(); + DBUG_ASSERT(spec); + return spec->get_typedef(thd, tdef, + Lex_ident_db_normalized(), package, type); + } + + /* + Another special case - a two step reference to a type + in the package of the db of the current routine (test1 in this example): + CREATE OR REPLACE PROCEDURE test1.p1 AS + r0 pkg1.rec0_t; -- A reference to test1.pkg1.rec0_t + BEGIN ... + */ + if (m_db.str) + { + Lex_ident_db_normalized ndb; + if (!(ndb= thd->to_ident_db_normalized_with_error(m_db)).str) + return true; + sp_package *spec; + if ((spec= Sp_handler::find_package_spec(thd, ndb, package)) && + (tdef[0]= spec->get_parse_context()->child_context(0)-> + find_type_def(type, false))) + { + return spec->get_typedef(thd, tdef, ndb, package, type); + } + } + + // Now go with the general case + Lex_ident_db_normalized resolved_db; + if (thd->variables.path.find_package_spec_type(thd, &resolved_db, + package, type)) + return true; // An internal error + if (!resolved_db.str) + { + raise_unknown_data_type(package, type); + return true; // 2-step package spec was not found + } + return get_typedef_package_spec(thd, tdef, + Lex_ident_sys(resolved_db.str, + resolved_db.length), + package, type); +} + + +/* + Get a 3-step qualified TYPE defines in a PACKAGE + + @param OUT tdef - The pointer to the TYPE definition + @param db - The database name + @param package - The package name + @param type - The data type name + + @retval false - The database "db" with the package "package" + with the data type "type" was found, + and tdef was set to the pointer of the found type. + @retval true - The data type was not found, or some error happened + during type resolution. +*/ +bool sp_head::get_typedef_package_spec(THD *thd, + const sp_type_def **tdef, + const Lex_ident_sys_st &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + Lex_ident_db_normalized dbn= thd->to_ident_db_normalized_with_error(db); + if (!dbn.str) + return true; + + /* + Catch a special case first: a reference to the current package spec. + Note, cast to Lex_ident_routine is safe below because m_name gets + to sp_head after Lex_ident_routine::check_name_with_error(). + */ + if (m_handler == &sp_handler_package_spec && + dbn.streq(db) && + Lex_ident_routine(m_name).streq(package)) + { + /* + The data type db1.pkg1.type1 refers the current package specifications + for the packge pkg1: + USE db1; + CREATE PACKAGE pkg1 AS + TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); + TYPE assoc0_t IS TABLE OF db1.pkg1.rec0_t INDEX BY INTEGER; + END; + Avoid a recursive find_package_spec_type(). + */ + sp_package *spec= get_package(); + DBUG_ASSERT(spec); + return spec->get_typedef(thd, tdef, dbn, package, type); + } + + sp_package *spec= Sp_handler::find_package_spec(thd, dbn, package); + if (!spec) + { + raise_unknown_data_type(dbn, package, type); + return true; // 3-step package spec was not found + } + return spec->get_typedef(thd, tdef, dbn, package, type); +} diff --git a/sql/sp_head.h b/sql/sp_head.h index 7f7f14074a22c..1f20d6f66c426 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1004,6 +1004,25 @@ class sp_head :private Query_arena, virtual void init_psi_share(); + static void raise_unknown_data_type(const Lex_ident_db_normalized &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); + static void raise_unknown_data_type(const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); + bool check_applicability(THD *thd, const Lex_field_type_st &def, + column_definition_type_t type); + + + bool get_typedef_package_spec(THD *thd, + const sp_type_def **tdef, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); + + bool get_typedef_package_spec(THD *thd, + const sp_type_def **tdef, + const Lex_ident_sys_st &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); protected: MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root @@ -1187,6 +1206,12 @@ class sp_package: public sp_head sp_pcontext *ctx= m_pcont->child_context(0); return ctx ? ctx->find_variable(name, true) : NULL; } + bool get_typedef(THD *thd, + const sp_type_def **tdef, + const Lex_ident_db_normalized dbn, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); + bool validate_after_parser(THD *thd); bool instantiate_if_needed(THD *thd); }; diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 2aeab10d7925b..7adfdff7b63fd 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -445,6 +445,20 @@ sp_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name, } +bool sp_pcontext::type_defs_add_ref_cursor(THD *thd, + const Lex_ident_column &name, + const Type_handler *th, + const Spvar_definition &return_def, + bool is_prepared) +{ + sp_type_def_ref *tdef= new (thd->mem_root) sp_type_def_ref(name, th, + return_def, is_prepared); + if (unlikely(!tdef || type_defs_add(thd, tdef))) + return true; // EOM + return false; +} + + sp_condition_value * sp_pcontext::find_declared_or_predefined_condition(THD *thd, const LEX_CSTRING *name) diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 421b708b9fa9d..5495f98856468 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -764,6 +764,25 @@ class sp_pcontext : public Sql_alloc, return sp_type_def_list::type_defs_add(def); } + /* + Add a new TYPE for REF CURSOR, e.g.: + TYPE type1 IS REF CURSOR; + TYPE type1 IS REF CURSOR RETURN t1%ROWTYPE; + TYPE type1 IS REF CURSOR RETURN pkg1.rec1; + @param THD - The THD + @param name - The name of the new data type + @param th - The type handler of the new TYPE + (usually &type_handler_sys_refcursor) + @param return_def - The definition of the RETURN type + @param is_prepared - If sp_prepare_create_field() has already + been called for return_def. + See details in class sp_type_def_ref. + */ + bool type_defs_add_ref_cursor(THD *thd, const Lex_ident_column &name, + const Type_handler *th, + const Spvar_definition &return_def, + bool is_prepared); + private: /// Constructor for a tree node. /// @param prev the parent parsing context diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 18f1ca7bb11e6..9295bdcee8331 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -6792,8 +6792,10 @@ sp_variable *LEX::sp_param_init(LEX_CSTRING *name) bool LEX::sp_param_fill_definition(sp_variable *spvar, const Lex_field_type_st &def) { + constexpr column_definition_type_t dtype= COLUMN_DEFINITION_ROUTINE_PARAM; return - last_field->set_attributes(thd, def, COLUMN_DEFINITION_ROUTINE_PARAM) || + sphead->check_applicability(thd, def, dtype) || + last_field->set_attributes(thd, def, dtype) || sphead->fill_spvar_definition(thd, last_field, &spvar->name); } @@ -6852,8 +6854,10 @@ bool LEX::sp_param_set_default_and_finalize(sp_variable *spvar, bool LEX::sf_return_fill_definition(const Lex_field_type_st &def) { + constexpr column_definition_type_t dtype= COLUMN_DEFINITION_FUNCTION_RETURN; return - last_field->set_attributes(thd, def, COLUMN_DEFINITION_FUNCTION_RETURN) || + sphead->check_applicability(thd, def, dtype) || + last_field->set_attributes(thd, def, dtype) || sphead->fill_field_definition(thd, last_field); } @@ -13270,9 +13274,89 @@ bool LEX::declare_type_assoc_array(THD *thd, } +/* + Declare a new REF CURSOR type with these semantics: + TYPE t IS REF CURSOR RETURN db.package.type; -- a 3-step package spec type + TYPE t IS REF CURSOR RETURN package.type; -- a 2-step package spec type + TYPE t IS REF CURSOR RETURN type; -- a 1-step semantic + I.e. the data type in RETURN refers to a previously defined TYPE. + + @param thd - The thd + @param type_name - The name of the new data type + @param db - The database name. Can be a null identifier {0,0}. + @param package - The package name. Can be a null identifier {0,0}. + @param type - The type name. + + @retval true - If could not create a new TYPE. Possible reasons: + * the data type specified by [db.]package.type + was not found + * something went wrong during mysql.proc reading. + * EOM happened + @retval false - If the new TYPE was successfully created. +*/ +bool LEX::declare_type_ref_cursor_return_typedef(THD *thd, + const Lex_ident_sys_st &type_name, + const Lex_ident_sys_st &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + const Lex_ident_plugin sr= "sys_refcursor"_Lex_ident_plugin; + const Type_handler *th= Type_handler::handler_by_name_or_error(thd, sr); + if (unlikely(!th)) + return true; + + const sp_type_def *rt= nullptr; // The typedef of the RETURN type + if (db.str) // A 3-step RETURN + { + // TYPE c0 IS REF CURSOR RETURN db1.pkg1.rec1_t; + if (sphead->get_typedef_package_spec(thd, &rt, db, package, type)) + return true; + } + else if (package.str) // A 2-step RETURN + { + // TYPE c0 IS REF CURSOR RETURN pkg1.rec1_t; + if (sphead->get_typedef_package_spec(thd, &rt, package, type)) + return true; + } + else // A 1-step RETURN + { + // TYPE c0 IS REF CURSOR RETURN rec1_t; + if (!(rt= find_type_def(type))) + { + my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), type.str); + return true; + } + } + if (!dynamic_cast(rt->type_handler()) || + !dynamic_cast(rt)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + type.str, "REF CURSOR RETURN"); + return true; + } + Row_definition_list *row= static_cast(rt)-> + field->deep_copy(thd); + if (!row) + return true; // EOM + + if (check_ref_cursor_components(row)) + return true; + + const Spvar_definition return_def(&type_handler_row, row); + return spcont->type_defs_add_ref_cursor(thd, Lex_ident_column(type_name), th, + return_def, false/*is_prepared*/); +} + + +/* + Declare a new REF CURSOR type with these semantics: + TYPE t IS REF CURSOR; + TYPE t IS REF CURSOR RETURN db1.t1%ROWTYPE; + TYPE t IS REF CURSOR RETURN t1%ROWTYPE; + TYPE t IS REF CURSOR RETURN rec_var%TYPE; +*/ bool LEX::declare_type_ref_cursor(THD *thd, const Lex_ident_sys_st &type_name, - const Lex_ident_sys_st &return_type_name, const Qualified_column_ident *rowtype, const Qualified_column_ident *vartype, const Lex_ident_cli_st &syntax_error_token) @@ -13346,41 +13430,9 @@ bool LEX::declare_type_ref_cursor(THD *thd, return_def= Spvar_definition(ti, sp_rcontext_addr(nullptr, 0)); } } - else if (!return_type_name.is_null()) - { - /* - An explicit data type in the RETURN clause: - TYPE c0 IS REF CURSOR RETURN rec0_t; - */ - const sp_type_def *rt= find_type_def(return_type_name); - if (!rt) - { - my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), return_type_name.str); - return true; - } - if (!dynamic_cast(rt->type_handler())) - { - my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), - return_type_name.str, "REF CURSOR RETURN"); - return true; - } - Row_definition_list *row= static_cast(rt)-> - field->deep_copy(thd); - if (!row) - return true; // EOM - if (check_ref_cursor_components(row)) - return true; - - return_def= Spvar_definition(&type_handler_row, row); - } - sp_type_def_ref *tdef= - new (thd->mem_root) sp_type_def_ref(Lex_ident_column(type_name), th, - return_def, is_prepared); - if (unlikely(!tdef || spcont->type_defs_add(thd, tdef))) - return true; // EOM - - return false; + return spcont->type_defs_add_ref_cursor(thd, Lex_ident_column(type_name), th, + return_def, is_prepared); } @@ -13491,6 +13543,68 @@ bool LEX::set_field_type_typedef(Lex_field_type_st *type, } +/* + Set a PACKAGE data type to "res" for a 2-step qualified name, + consisting by the package name and type name. + Handles the following cases: + - A PACKAGE refers to its own type using a qualified name + - A routine refers to the PACKAGE in the same database with the routine + using a qualified type + - Otherwise, the database which contains a package "package" with the type + "type" is resolved using the @@PATH variable. + + @param OUT res - The data type to write to + @param package - The package name + @param type - The data type name + + @retval false - The package "package" with the data type "type" was found, + and res[0] was set to the found type. + @retval true - The data type was not found, or some error happened + during type resolution. +*/ +bool LEX::set_field_type_typedef_package_spec(Lex_field_type_st *res, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + const sp_type_def *tdef= nullptr; + if (sphead->get_typedef_package_spec(thd, &tdef, package, type)) + return true; + res->set(tdef->type_handler(), nullptr/*CHARSET_INFO*/); + res->set_foreign_module_type(true); + last_field->set_attr_const_generic_ptr(0, tdef); + return false; +} + + +/* + Set a PACKAGE data type to "res" by a 3-step qualified name, + consisting by the database name, package name and type name. + + @param OUT res - The data type to write to + @param db - The database name + @param package - The package name + @param type - The data type name + + @retval false - The package "package" with the data type "type" was found, + and res[0] was set to the found type. + @retval true - The data type was not found, or some error happened + during type resolution. +*/ +bool LEX::set_field_type_typedef_package_spec(Lex_field_type_st *res, + const Lex_ident_sys_st &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + const sp_type_def *tdef= nullptr; + if (sphead->get_typedef_package_spec(thd, &tdef, db, package, type)) + return true; + res->set(tdef->type_handler(), nullptr/*CHARSET_INFO*/); + res->set_foreign_module_type(true); + last_field->set_attr_const_generic_ptr(0, tdef); + return false; +} + + bool sp_expr_lex::sp_repeat_loop_finalize(THD *thd) { uint ip= sphead->instructions(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e86650716896d..9d94b1e851098 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -5135,10 +5135,14 @@ struct LEX: public Query_tables_list Spvar_definition *value); bool declare_type_ref_cursor(THD *thd, const Lex_ident_sys_st &type_name, - const Lex_ident_sys_st &return_type_name, const Qualified_column_ident *rowtype, const Qualified_column_ident *vartype, const Lex_ident_cli_st &syntax_error_token); + bool declare_type_ref_cursor_return_typedef(THD *thd, + const Lex_ident_sys_st &type_name, + const Lex_ident_sys_st &db/*can be null ident*/, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); bool set_field_type_typedef(Lex_field_type_st *type, const LEX_CSTRING &name, const Lex_length_and_dec_st &attr, @@ -5148,6 +5152,13 @@ struct LEX: public Query_tables_list const LEX_CSTRING &name, const Lex_length_and_dec_st &attr, const Lex_column_charset_collation_attrs_st &coll); + bool set_field_type_typedef_package_spec(Lex_field_type_st *res, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); + bool set_field_type_typedef_package_spec(Lex_field_type_st *res, + const Lex_ident_sys_st &db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); bool map_data_type(const Lex_ident_sys_st &schema, Lex_field_type_st *type) const; diff --git a/sql/sql_path.cc b/sql/sql_path.cc index 982fbffaf985e..a260aa7747b9e 100644 --- a/sql/sql_path.cc +++ b/sql/sql_path.cc @@ -172,6 +172,59 @@ bool Sql_path::resolve(THD *thd, sp_head *caller, sp_name *name, } +/* + Iterate through all schemas in @@path and find a package "package" with the + type "type" + + @param thd - The THD + @param OUT db - The found database which contains a type package.type + If db->str is nullptr the nothing was found. + @param package - The package to find. + @param type - The type to find. + @retval true - If error happened during resolution. + @retval false - If no errors happened during resolution. + Note, if nothing was found and no errors happened, + the return value is false. +*/ +bool Sql_path::find_package_spec_type(THD *thd, + Lex_ident_db_normalized *db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type) +{ + *db= Lex_ident_db_normalized(); + for (size_t i= 0; i < m_count; i++) + { + sp_package *pkg; + Lex_ident_db_normalized ncdb; + if (is_cur_schema(i)) + { + if (!thd->db.str) + continue; + if (!(ncdb= thd->copy_db_normalized()).str) + return true; // EOM + } + else + { + /* + Conversion to Lex_ident_db_normalized is safe because + database names get into Sql_path in normalized form. + */ + ncdb= Lex_ident_db_normalized(m_schemas[i].str, m_schemas[i].length); + } + if (!(pkg= Sp_handler::find_package_spec(thd, ncdb, package))) + continue; + sp_type_def *tdef= pkg->get_parse_context()->child_context(0)-> + find_type_def(type, false); + if (tdef) + { + *db= ncdb; + return false; // Found a TYPE declaration in a CREATE PACKAGE + } + } + return false; // Nothing found +} + + void Sql_path::free() { if (m_count) diff --git a/sql/sql_path.h b/sql/sql_path.h index e6e40efdbeff4..0e063e3489a8f 100644 --- a/sql/sql_path.h +++ b/sql/sql_path.h @@ -38,6 +38,10 @@ struct Sql_path bool resolve(THD *thd, sp_head *caller, sp_name *name, const Sp_handler **sph, Database_qualified_name *pkgname) const; + + bool find_package_spec_type(THD *thd, Lex_ident_db_normalized *db, + const Lex_ident_sys_st &package, + const Lex_ident_sys_st &type); /* Initialize the path variable with default values */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 269d497058c2f..8273f98d80816 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6643,6 +6643,16 @@ field_type_all_with_typedefs: if (unlikely(Lex->set_field_type_udt_or_typedef(&$$, $1, $2, $4))) MYSQL_YYABORT; } + | sp_decl_ident '.' ident + { + if (Lex->set_field_type_typedef_package_spec(&$$, $1, $3)) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident '.' ident + { + if (Lex->set_field_type_typedef_package_spec(&$$, $1, $3, $5)) + MYSQL_YYABORT; + } ; field_type_numeric: @@ -20349,7 +20359,7 @@ package_implementation_routine_definition: pkg->m_current_routine= NULL; $$.init(); } - | package_specification_element { $$.init(); } + | package_specification_routine { $$.init(); } ; @@ -20402,7 +20412,7 @@ package_specification_element_list: | package_specification_element_list package_specification_element ; -package_specification_element: +package_specification_routine: FUNCTION_SYM package_specification_function ';' { sp_package *pkg= Lex->get_sp_package(); @@ -20419,6 +20429,13 @@ package_specification_element: } ; +package_specification_element: + package_specification_routine +%ifdef ORACLE + | sp_decl_type ';' +%endif + ; + %ifdef ORACLE @@ -20793,15 +20810,22 @@ sp_decl_type: } | typed_ident IS REF_SYM CURSOR_SYM { - if (unlikely(Lex->declare_type_ref_cursor(thd, $1, Lex_ident_sys(), + if (unlikely(Lex->declare_type_ref_cursor(thd, $1, nullptr, nullptr, $4))) MYSQL_YYABORT; $$.init(); } - | typed_ident IS REF_SYM CURSOR_SYM RETURN_ORACLE_SYM sp_decl_ident + | typed_ident IS REF_SYM CURSOR_SYM RETURN_ORACLE_SYM + optionally_qualified_column_ident { - if (unlikely(Lex->declare_type_ref_cursor(thd, $1, $6, - nullptr, nullptr, $5))) + /* + Conversion of $6 components to Lex_ident_sys is safe, + according to the grammar. + */ + if (unlikely(Lex->declare_type_ref_cursor_return_typedef(thd, $1, + Lex_ident_sys($6->db.str, $6->db.length), + Lex_ident_sys($6->table.str, $6->table.length), + Lex_ident_sys($6->m_column.str, $6->m_column.length)))) MYSQL_YYABORT; $$.init(); } @@ -20809,7 +20833,7 @@ sp_decl_type: optionally_qualified_column_ident PERCENT_ORACLE_SYM TYPE_SYM { - if (unlikely(Lex->declare_type_ref_cursor(thd, $1, Lex_ident_sys(), + if (unlikely(Lex->declare_type_ref_cursor(thd, $1, nullptr, $6, $8))) MYSQL_YYABORT; $$.init(); @@ -20818,7 +20842,7 @@ sp_decl_type: optionally_qualified_column_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM { - if (unlikely(Lex->declare_type_ref_cursor(thd, $1, Lex_ident_sys(), + if (unlikely(Lex->declare_type_ref_cursor(thd, $1, $6, nullptr, $8))) MYSQL_YYABORT; $$.init(); diff --git a/sql/structs.h b/sql/structs.h index 764d37eaf1cfa..85c9cce0f3891 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -665,6 +665,7 @@ struct Lex_length_and_dec_st bool m_has_explicit_dec:1; bool m_length_overflowed:1; bool m_dec_overflowed:1; + bool m_foreign_module_type:1; // Is a type from CREATE PACKAGE static_assert(LEX_CHARSET_COLLATION_TYPE_BITS <= 8, "Lex_length_and_dec_st::m_collation_type bits check"); @@ -685,6 +686,7 @@ struct Lex_length_and_dec_st m_has_explicit_dec= false; m_length_overflowed= false; m_dec_overflowed= false; + m_foreign_module_type= false; } void set_length_only(uint32 length) { @@ -695,6 +697,7 @@ struct Lex_length_and_dec_st m_has_explicit_dec= false; m_length_overflowed= false; m_dec_overflowed= false; + m_foreign_module_type= false; } void set_dec_only(uint8 dec) { @@ -705,6 +708,7 @@ struct Lex_length_and_dec_st m_has_explicit_dec= true; m_length_overflowed= false; m_dec_overflowed= false; + m_foreign_module_type= false; } void set_length_and_dec(uint32 length, uint8 dec) { @@ -715,8 +719,13 @@ struct Lex_length_and_dec_st m_has_explicit_dec= true; m_length_overflowed= false; m_dec_overflowed= false; + m_foreign_module_type= false; } void set(const char *length, const char *dec); + void set_foreign_module_type(bool value) + { + m_foreign_module_type= value; + } uint32 length() const { return m_length; @@ -741,6 +750,10 @@ struct Lex_length_and_dec_st { return m_dec_overflowed; } + bool foreign_module_type() const + { + return m_foreign_module_type; + } };