Skip to content

Commit e1cd3c4

Browse files
committed
MDEV-12252 ROW data type for stored function return values
Adding support for the ROW data type in the stored function RETURNS clause: - explicit ROW(..members...) for both sql_mode=DEFAULT and sql_mode=ORACLE CREATE FUNCTION f1() RETURNS ROW(a INT, b VARCHAR(32)) ... - anchored "ROW TYPE OF [db1.]table1" declarations for sql_mode=DEFAULT CREATE FUNCTION f1() RETURNS ROW TYPE OF test.t1 ... - anchored "[db1.]table1%ROWTYPE" declarations for sql_mode=ORACLE CREATE FUNCTION f1() RETURN test.t1%ROWTYPE ... Adding support for anchored scalar data types in RETURNS clause: - "TYPE OF [db1.]table1.column1" for sql_mode=DEFAULT CREATE FUNCTION f1() RETURNS TYPE OF test.t1.column1; - "[db1.]table1.column1" for sql_mode=ORACLE CREATE FUNCTION f1() RETURN test.t1.column1%TYPE; Details: - Adding a new sql_mode_t parameter to sp_head::create() sp_head::sp_head() sp_package::create() sp_package::sp_package() to guarantee early initialization of sp_head::m_sql_mode. Before this change, this member was not initialized at all during CREATE FUNCTION/PROCEDURE/PACKAGE statements, and was not used. Now it needs to be initialized to write properly the mysql.proc.returns column, according to the create time sql_mode. - Code refactoring to make the things simpler and functions smaller: * Adding a new method Field_row::row_create_fields(THD *thd, List<Spvar_definition> *list) to make a Virtual_tmp_table with Fields for ROW members from an explicit definition. * Adding a new method Field_row::row_create_fields(THD *thd, const Spvar_definition &def) to make a Virtual_tmp_table with Fields for ROW members from an explicit or a table anchored definition. * Adding a new method Item_args::add_array_of_item_field(THD *thd, const Virtual_tmp_table &vtable) to create and array of Item_field corresponding to all Field instances in a Virtual_tmp_table * Removing Item_field_row::row_create_items(). It was decomposed into the new methods described above. * Moving the code from the loop body in sp_rcontext::init_var_items() into a separate method Spvar_definition::make_item_field_row(), to make the code clearer (smaller functions). make_item_field_row() itself uses the new methods described above. - Changing the data type of sp_head::m_return_field_def from Column_definition to Spvar_definition. So now it supports not only SQL column field types, but also explicit ROW and anchored ROW data types, as well as anchored column types. - Adding a new Column_definition parameter to sp_head::create_result_field(). Before this patch, create_result_field() took the definition only from m_return_field_def. Now it's also called with a local Column_definition variable which contains the explicit definition resolved from an anchored defition. - Modifying sql_yacc.yy to support the new grammar. Adding new helper methods: * sf_return_fill_definition_row() * sf_return_fill_definition_rowtype_of() * sf_return_fill_definition_type_of() - Fixing tests in: * Virtual_tmp_table::setup_field_pointers() in sql_select.cc * Send_field::normalize() in field.h * store_column_type() to prevent calling Type_handler_row::field_type(), which is implemented a DBUG_ASSERT(0). Before this patch the affected methods and functions were called only for scalar data types. Now ROW is also possible. - Adding a new virtual method Field::cols() - Overriding methods: Item_func_sp::cols() Item_func_sp::element_index() Item_func_sp::check_cols() Item_func_sp::bring_value() to support the ROW data type. - Extending the rule sp_return_type to support * explicit ROW and anchored ROW data types * anchored scalar data types - Overriding Field_row::sql_type() to print the data type of an explicit ROW.
1 parent dfaf7e2 commit e1cd3c4

37 files changed

+2137
-224
lines changed

mysql-test/main/sp-anchor-row-type-table.result

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,3 +852,88 @@ DROP TABLE t1;
852852
#
853853
# End of 10.4 tests
854854
#
855+
#
856+
# Start of 11.7 tests
857+
#
858+
#
859+
# MDEV-12252 ROW data type for stored function return values
860+
#
861+
CREATE FUNCTION f1() RETURNS ROW TYPE OF step1.step2.step3 RETURN ROW(1,2);
862+
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 '.step3 RETURN ROW(1,2)' at line 1
863+
CREATE FUNCTION f1() RETURNS ROW TYPE OF t1 RETURN ROW(1,2);
864+
SHOW CREATE FUNCTION f1;
865+
Function sql_mode Create Function character_set_client collation_connection Database Collation
866+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS ROW TYPE OF `test`.`t1`
867+
RETURN ROW(1,2) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
868+
SELECT f1();
869+
ERROR 42S02: Table 'test.t1' doesn't exist
870+
DROP FUNCTION f1;
871+
CREATE FUNCTION f1() RETURNS ROW TYPE OF test.t1 RETURN ROW(1,2);
872+
SHOW CREATE FUNCTION f1;
873+
Function sql_mode Create Function character_set_client collation_connection Database Collation
874+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS ROW TYPE OF `test`.`t1`
875+
RETURN ROW(1,2) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
876+
SELECT f1();
877+
ERROR 42S02: Table 'test.t1' doesn't exist
878+
DROP FUNCTION f1;
879+
CREATE TABLE t1 (a INT, b VARCHAR(32));
880+
CREATE FUNCTION f1() RETURNS ROW TYPE OF test.t1 RETURN (SELECT * FROM t1);
881+
SHOW CREATE FUNCTION f1;
882+
Function sql_mode Create Function character_set_client collation_connection Database Collation
883+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS ROW TYPE OF `test`.`t1`
884+
RETURN (SELECT * FROM t1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
885+
CREATE PROCEDURE p1()
886+
BEGIN
887+
DECLARE r ROW TYPE OF t1 DEFAULT f1();
888+
SELECT r.a, r.b;
889+
END;
890+
$$
891+
CALL p1();
892+
r.a r.b
893+
NULL NULL
894+
SELECT f1();
895+
ERROR 21000: Operand should contain 1 column(s)
896+
SELECT f1()=ROW(1,'b1') AS c;
897+
c
898+
NULL
899+
SELECT f1()=ROW(NULL,NULL) AS c;
900+
c
901+
NULL
902+
INSERT INTO t1 VALUES (1,'b1');
903+
CALL p1();
904+
r.a r.b
905+
1 b1
906+
SELECT f1();
907+
ERROR 21000: Operand should contain 1 column(s)
908+
SELECT f1()=ROW(1,'b1') AS c;
909+
c
910+
1
911+
SELECT f1()=ROW(1,'') AS c;
912+
c
913+
0
914+
SELECT f1()=ROW(2,'b1') AS c;
915+
c
916+
0
917+
SELECT f1()=ROW(1,NULL) AS c;
918+
c
919+
NULL
920+
SELECT f1()=ROW(NULL,'b1') AS c;
921+
c
922+
NULL
923+
SHOW CREATE FUNCTION f1;
924+
Function sql_mode Create Function character_set_client collation_connection Database Collation
925+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS ROW TYPE OF `test`.`t1`
926+
RETURN (SELECT * FROM t1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
927+
INSERT INTO t1 VALUES (2,'b2');
928+
CALL p1();
929+
ERROR 21000: Subquery returns more than 1 row
930+
SELECT f1();
931+
ERROR 21000: Operand should contain 1 column(s)
932+
SELECT f1()=ROW(1,'b1') AS c;
933+
ERROR 21000: Subquery returns more than 1 row
934+
DROP PROCEDURE p1;
935+
DROP FUNCTION f1;
936+
DROP TABLE t1;
937+
#
938+
# End of 11.7 tests
939+
#

mysql-test/main/sp-anchor-row-type-table.test

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,3 +939,75 @@ DROP TABLE t1;
939939
--echo #
940940
--echo # End of 10.4 tests
941941
--echo #
942+
943+
--echo #
944+
--echo # Start of 11.7 tests
945+
--echo #
946+
947+
--echo #
948+
--echo # MDEV-12252 ROW data type for stored function return values
949+
--echo #
950+
951+
--error ER_PARSE_ERROR
952+
CREATE FUNCTION f1() RETURNS ROW TYPE OF step1.step2.step3 RETURN ROW(1,2);
953+
954+
CREATE FUNCTION f1() RETURNS ROW TYPE OF t1 RETURN ROW(1,2);
955+
SHOW CREATE FUNCTION f1;
956+
--error ER_NO_SUCH_TABLE
957+
SELECT f1();
958+
DROP FUNCTION f1;
959+
960+
CREATE FUNCTION f1() RETURNS ROW TYPE OF test.t1 RETURN ROW(1,2);
961+
SHOW CREATE FUNCTION f1;
962+
--error ER_NO_SUCH_TABLE
963+
SELECT f1();
964+
DROP FUNCTION f1;
965+
966+
967+
CREATE TABLE t1 (a INT, b VARCHAR(32));
968+
CREATE FUNCTION f1() RETURNS ROW TYPE OF test.t1 RETURN (SELECT * FROM t1);
969+
SHOW CREATE FUNCTION f1;
970+
DELIMITER $$;
971+
CREATE PROCEDURE p1()
972+
BEGIN
973+
DECLARE r ROW TYPE OF t1 DEFAULT f1();
974+
SELECT r.a, r.b;
975+
END;
976+
$$
977+
DELIMITER ;$$
978+
979+
# Testing with no rows
980+
CALL p1();
981+
--error ER_OPERAND_COLUMNS
982+
SELECT f1();
983+
SELECT f1()=ROW(1,'b1') AS c;
984+
SELECT f1()=ROW(NULL,NULL) AS c;
985+
986+
# Testing with one row
987+
INSERT INTO t1 VALUES (1,'b1');
988+
CALL p1();
989+
--error ER_OPERAND_COLUMNS
990+
SELECT f1();
991+
SELECT f1()=ROW(1,'b1') AS c;
992+
SELECT f1()=ROW(1,'') AS c;
993+
SELECT f1()=ROW(2,'b1') AS c;
994+
SELECT f1()=ROW(1,NULL) AS c;
995+
SELECT f1()=ROW(NULL,'b1') AS c;
996+
SHOW CREATE FUNCTION f1;
997+
998+
# Testing with two rows
999+
INSERT INTO t1 VALUES (2,'b2');
1000+
--error ER_SUBQUERY_NO_1_ROW
1001+
CALL p1();
1002+
--error ER_OPERAND_COLUMNS
1003+
SELECT f1();
1004+
--error ER_SUBQUERY_NO_1_ROW
1005+
SELECT f1()=ROW(1,'b1') AS c;
1006+
1007+
DROP PROCEDURE p1;
1008+
DROP FUNCTION f1;
1009+
DROP TABLE t1;
1010+
1011+
--echo #
1012+
--echo # End of 11.7 tests
1013+
--echo #

mysql-test/main/sp-anchor-type.result

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,3 +1071,149 @@ t1 CREATE TABLE `t1` (
10711071
`aa_timestamp5` timestamp(5) NULL DEFAULT NULL,
10721072
`aa_date0` date DEFAULT NULL
10731073
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
1074+
#
1075+
# Start of 11.7 tests
1076+
#
1077+
#
1078+
# MDEV-12252 ROW data type for stored function return values
1079+
#
1080+
CREATE FUNCTION f1() RETURNS TYPE OF a RETURN 1;
1081+
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
1082+
CREATE FUNCTION f1() RETURNS TYPE OF t1.a RETURN (SELECT min(a) FROM t1);
1083+
SHOW CREATE FUNCTION f1;
1084+
Function sql_mode Create Function character_set_client collation_connection Database Collation
1085+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS TYPE OF `test`.`t1`.`a`
1086+
RETURN (SELECT min(a) FROM t1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
1087+
SELECT f1();
1088+
ERROR 42S02: Table 'test.t1' doesn't exist
1089+
CREATE TABLE t1 (b INT);
1090+
SELECT f1();
1091+
ERROR 42S22: Unknown column 'a' in 't1'
1092+
DROP TABLE t1;
1093+
DROP FUNCTION f1;
1094+
CREATE DATABASE db1;
1095+
CREATE FUNCTION db1.f1() RETURNS TYPE OF db1.t1.a
1096+
BEGIN
1097+
RETURN (SELECT min(a) FROM t1);
1098+
END;
1099+
$$
1100+
SHOW CREATE FUNCTION db1.f1;
1101+
Function sql_mode Create Function character_set_client collation_connection Database Collation
1102+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS TYPE OF `db1`.`t1`.`a`
1103+
BEGIN
1104+
RETURN (SELECT min(a) FROM t1);
1105+
END latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
1106+
SELECT db1.f1();
1107+
ERROR 42S02: Table 'db1.t1' doesn't exist
1108+
CREATE TABLE db1.t1 (b TIME);
1109+
SELECT db1.f1();
1110+
ERROR 42S22: Unknown column 'a' in 't1'
1111+
DROP TABLE db1.t1;
1112+
CREATE TABLE db1.t1 (a TIME);
1113+
SELECT db1.f1();
1114+
db1.f1()
1115+
NULL
1116+
INSERT INTO db1.t1 VALUES ('10:20:30');
1117+
SELECT db1.f1();
1118+
db1.f1()
1119+
10:20:30
1120+
DROP TABLE db1.t1;
1121+
DROP FUNCTION db1.f1;
1122+
DROP DATABASE db1;
1123+
CREATE TABLE t1 (a TIME);
1124+
CREATE FUNCTION f1() RETURNS TYPE OF test.t1.a RETURN (SELECT min(a) FROM t1);
1125+
SHOW CREATE FUNCTION f1;
1126+
Function sql_mode Create Function character_set_client collation_connection Database Collation
1127+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS TYPE OF `test`.`t1`.`a`
1128+
RETURN (SELECT min(a) FROM t1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
1129+
SELECT f1();
1130+
f1()
1131+
NULL
1132+
DROP FUNCTION f1;
1133+
DROP TABLE t1;
1134+
CREATE TABLE t1 (a TIME);
1135+
CREATE FUNCTION f1() RETURNS TYPE OF t1.a
1136+
BEGIN
1137+
RETURN (SELECT min(a) FROM t1);
1138+
END;
1139+
$$
1140+
SHOW CREATE FUNCTION f1;
1141+
Function sql_mode Create Function character_set_client collation_connection Database Collation
1142+
f1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS TYPE OF `test`.`t1`.`a`
1143+
BEGIN
1144+
RETURN (SELECT min(a) FROM t1);
1145+
END latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
1146+
SELECT f1();
1147+
f1()
1148+
NULL
1149+
CREATE TABLE t2 AS SELECT f1();
1150+
SHOW CREATE TABLE t2;
1151+
Table Create Table
1152+
t2 CREATE TABLE `t2` (
1153+
`f1()` time DEFAULT NULL
1154+
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
1155+
SELECT * FROM t2;
1156+
f1()
1157+
NULL
1158+
DROP TABLE t2;
1159+
INSERT INTO t1 VALUES ('10:20:30');
1160+
SELECT f1();
1161+
f1()
1162+
10:20:30
1163+
CREATE TABLE t2 AS SELECT f1();
1164+
SHOW CREATE TABLE t2;
1165+
Table Create Table
1166+
t2 CREATE TABLE `t2` (
1167+
`f1()` time DEFAULT NULL
1168+
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
1169+
SELECT * FROM t2;
1170+
f1()
1171+
10:20:30
1172+
DROP TABLE t2;
1173+
DROP TABLE t1;
1174+
CREATE TABLE t1 (a INT);
1175+
INSERT INTO t1 VALUES (10);
1176+
SELECT f1();
1177+
f1()
1178+
10
1179+
CREATE TABLE t2 AS SELECT f1();
1180+
SHOW CREATE TABLE t2;
1181+
Table Create Table
1182+
t2 CREATE TABLE `t2` (
1183+
`f1()` int(11) DEFAULT NULL
1184+
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
1185+
SELECT * FROM t2;
1186+
f1()
1187+
10
1188+
DROP TABLE t2;
1189+
DROP FUNCTION f1;
1190+
DROP TABLE t1;
1191+
#
1192+
# MDEV-35003 Server crashes when reading routines table after creating a function with "returns type of"
1193+
#
1194+
CREATE FUNCTION f1(a TYPE OF t1.a) RETURNS TYPE OF t1.a
1195+
BEGIN
1196+
RETURN (SELECT min(a) FROM t1);
1197+
END;
1198+
$$
1199+
SELECT ROUTINE_NAME, DATA_TYPE, DTD_IDENTIFIER
1200+
FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='test';
1201+
ROUTINE_NAME f1
1202+
DATA_TYPE TYPE OF
1203+
DTD_IDENTIFIER TYPE OF `test`.`t1`.`a`
1204+
SELECT SPECIFIC_NAME, ORDINAL_POSITION, PARAMETER_NAME, DATA_TYPE, DTD_IDENTIFIER
1205+
FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA='test';
1206+
SPECIFIC_NAME f1
1207+
ORDINAL_POSITION 0
1208+
PARAMETER_NAME NULL
1209+
DATA_TYPE TYPE OF
1210+
DTD_IDENTIFIER TYPE OF `test`.`t1`.`a`
1211+
SPECIFIC_NAME f1
1212+
ORDINAL_POSITION 1
1213+
PARAMETER_NAME a
1214+
DATA_TYPE TYPE OF
1215+
DTD_IDENTIFIER TYPE OF `t1`.`a`
1216+
DROP FUNCTION f1;
1217+
#
1218+
# End of 11.7 tests
1219+
#

0 commit comments

Comments
 (0)