From ed305c0fd52d17110e9cc7c2e515d1f881de1102 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 7 Apr 2017 13:40:27 +0400 Subject: [PATCH] MDEV-12461 TYPE OF and ROW TYPE OF anchored data types --- mysql-test/r/sp-anchor-row-type-cursor.result | 982 +++++++++++++++ mysql-test/r/sp-anchor-row-type-table.result | 681 +++++++++++ mysql-test/r/sp-anchor-type.result | 967 +++++++++++++++ mysql-test/r/sp-security-anchor-type.result | 263 ++++ mysql-test/r/trigger.result | 20 + mysql-test/t/sp-anchor-row-type-cursor.test | 1073 +++++++++++++++++ mysql-test/t/sp-anchor-row-type-table.test | 761 ++++++++++++ mysql-test/t/sp-anchor-type.test | 677 +++++++++++ mysql-test/t/sp-security-anchor-type.test | 328 +++++ mysql-test/t/trigger.test | 23 + sql/lex.h | 1 + sql/sql_yacc.yy | 105 +- sql/sql_yacc_ora.yy | 2 + 13 files changed, 5866 insertions(+), 17 deletions(-) create mode 100644 mysql-test/r/sp-anchor-row-type-cursor.result create mode 100644 mysql-test/r/sp-anchor-row-type-table.result create mode 100644 mysql-test/r/sp-anchor-type.result create mode 100644 mysql-test/r/sp-security-anchor-type.result create mode 100644 mysql-test/t/sp-anchor-row-type-cursor.test create mode 100644 mysql-test/t/sp-anchor-row-type-table.test create mode 100644 mysql-test/t/sp-anchor-type.test create mode 100644 mysql-test/t/sp-security-anchor-type.test diff --git a/mysql-test/r/sp-anchor-row-type-cursor.result b/mysql-test/r/sp-anchor-row-type-cursor.result new file mode 100644 index 0000000000000..144bd2e89c11f --- /dev/null +++ b/mysql-test/r/sp-anchor-row-type-cursor.result @@ -0,0 +1,982 @@ +# +# Start of 10.3 tests +# +# +# MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +# +# A complete working example +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +CREATE PROCEDURE p1() +BEGIN +DECLARE c CURSOR FOR SELECT a,b FROM t1; +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE rec ROW TYPE OF c; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; +OPEN c; +read_loop: LOOP +FETCH c INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT rec.a ,rec.b FROM dual; +INSERT INTO t2 VALUES (rec.a, rec.b); +END LOOP; +CLOSE c; +END; +END; +$$ +CALL p1(); +rec.a rec.b +10 b10 +rec.a rec.b +20 b20 +rec.a rec.b +30 b30 +SELECT * FROM t2; +a b +10 b10 +20 b20 +30 b30 +DROP PROCEDURE p1; +DROP TABLE t2; +DROP TABLE t1; +# +# cursor ROW TYPE referring to a table in a non-existing database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM tes2.t1; +BEGIN +DECLARE rec ROW TYPE OF cur; +END; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'tes2.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +ERROR 42S02: Table 'tes2.t1' doesn't exist +DROP TABLE t1; +DROP PROCEDURE p1; +# +# cursor ROW TYPE referring to a table in the current database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +DROP PROCEDURE p1; +# +# cursor ROW TYPE referring to a table in an explicitly specified database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM test.t1; +BEGIN +DECLARE rec ROW TYPE OF cur; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Cursor ROW TYPE referring to a view in the current database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM v1; +BEGIN +DECLARE rec ROW TYPE OF cur; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.v1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# cursor ROW TYPE referring to a view in an explicitly specified database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM test.v1; +BEGIN +DECLARE rec ROW TYPE OF cur; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.v1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Checking that all cursor ROW TYPE fields are NULL by default +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +SELECT rec1.a, rec1.b, rec1.c, rec1.d; +END; +END; +$$ +CALL p1(); +rec1.a rec1.b rec1.c rec1.d +NULL NULL NULL NULL +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A cursor ROW TYPE variable with a ROW expression as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb'); +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A cursor ROW TYPE variable with an incompatible ROW expression as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb','ccc'); +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A cursor ROW TYPE variable with a ROW variable as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW(a INT, b VARCHAR(10)) DEFAULT ROW(10,'bbb'); +DECLARE rec2 ROW TYPE OF cur DEFAULT rec1; +SELECT rec2.a, rec2.b; +END; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A ROW variable using a cursor ROW TYPE variable as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb'); +DECLARE rec2 ROW(a INT, b VARCHAR(10)) DEFAULT rec1; +SELECT rec2.a, rec2.b; +END; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning cursor ROW TYPE variables with a different column count +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +DECLARE rec2 ROW TYPE OF cur2; +SET rec2=rec1; +END; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP PROCEDURE p1; +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +DECLARE rec2 ROW TYPE OF cur2; +SET rec1=rec2; +END; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 3 column(s) +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning compatible cursor ROW TYPE variables (equal number of fields) +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +DECLARE rec2 ROW TYPE OF cur2; +SET rec1.a= 10; +SET rec1.b= 'bbb'; +SET rec2=rec1; +SELECT rec2.x, rec2.y; +END; +END; +$$ +CALL p1(); +rec2.x rec2.y +10 bbb +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning between incompatible cursor ROW TYPE and explicit ROW variables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +DECLARE rec2 ROW(x INT,y INT,z INT); +SET rec2.x= 10; +SET rec2.y= 20; +SET rec2.z= 30; +SET rec1= rec2; +END; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning between compatible cursor ROW TYPE and explicit ROW variables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +DECLARE rec2 ROW(x INT,y INT); +SET rec2.x= 10; +SET rec2.y= 20; +SET rec1= rec2; +SELECT rec1.a, rec1.b; +SET rec1.a= 11; +SET rec1.b= 21; +SET rec2= rec1; +SELECT rec2.x, rec2.y; +END; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 20 +rec2.x rec2.y +11 21 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning cursor ROW TYPE from a ROW expression +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +SET rec1= ROW(10,20); +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 20 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a cursor ROW TYPE variable with a wrong field count +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec2 ROW TYPE OF cur2; +OPEN cur1; +FETCH cur1 INTO rec2; +CLOSE cur1; +END; +END; +$$ +CALL p1(); +ERROR HY000: Incorrect number of FETCH variables +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a cursor ROW TYPE variable +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE rec ROW TYPE OF cur; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; +OPEN cur; +read_loop: LOOP +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT rec.a, rec.b, rec.c, rec.d; +INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); +END LOOP; +CLOSE cur; +END; +END; +$$ +CALL p1(); +rec.a rec.b rec.c rec.d +10 bb1 11111.1 12.31 +rec.a rec.b rec.c rec.d +20 bb2 22222.2 12.32 +rec.a rec.b rec.c rec.d +30 bb3 33333.3 12.33 +SELECT * FROM t2; +a b c d +10 bb1 11111.1 12.31 +20 bb2 22222.2 12.32 +30 bb3 33333.3 12.33 +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a cursor ROW TYPE variable, ROW TYPE OF cur declared inside the LOOP +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE cur CURSOR FOR SELECT * FROM t1; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; +OPEN cur; +read_loop: LOOP +BEGIN +DECLARE rec ROW TYPE OF cur; +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT rec.a, rec.b, rec.c, rec.d; +INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); +END; +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +rec.a rec.b rec.c rec.d +10 bb1 11111.1 12.31 +rec.a rec.b rec.c rec.d +20 bb2 22222.2 12.32 +rec.a rec.b rec.c rec.d +30 bb3 33333.3 12.33 +SELECT * FROM t2; +a b c d +10 bb1 11111.1 12.31 +20 bb2 22222.2 12.32 +30 bb3 33333.3 12.33 +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a cursor ROW TYPE variable with different column names +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bbb'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec2 ROW TYPE OF cur2; +OPEN cur1; +FETCH cur1 INTO rec2; +SELECT rec2.x, rec2.y; +CLOSE cur1; +END; +END; +$$ +CALL p1(); +rec2.x rec2.y +10 bbb +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a cursor ROW TYPE variable, with truncation +# +SET sql_mode=''; +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (10,'11x'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +DECLARE cur2 CURSOR FOR SELECT * FROM t2; +BEGIN +DECLARE rec2 ROW TYPE OF cur2; +OPEN cur1; +FETCH cur1 INTO rec2; +SELECT rec2.a, rec2.b; +CLOSE cur1; +END; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 11 +Warnings: +Warning 1265 Data truncated for column 'b' at row 1 +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +SET sql_mode=DEFAULT; +# +# cursor ROW TYPE variables are not allowed in LIMIT +# +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1 DEFAULT (1,2); +SELECT * FROM t1 LIMIT rec1.a; +END; +END; +$$ +ERROR HY000: A variable of a non-integer based type in LIMIT clause +DROP TABLE t1; +# +# cursor ROW TYPE variable fields as OUT parameters +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(OUT a INT,OUT b VARCHAR(10)) +BEGIN +SET a=10; +SET b='bb'; +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +CALL p1(rec1.a, rec1.b); +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p2(); +rec1.a rec1.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Passing the entire cursor ROW TYPE variable +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10))) +BEGIN +SELECT a.a, a.b; +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur DEFAULT ROW(10,'bb'); +CALL p1(rec1); +END; +END; +$$ +CALL p2(); +a.a a.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Passing the entire cursor ROW TYPE variable as an OUT parameter +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(OUT a ROW(a INT, b VARCHAR(10))) +BEGIN +SET a= ROW(10,'bb'); +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur; +CALL p1(rec1); +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p2(); +rec1.a rec1.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Assigning a cursor ROW TYPE field to an OUT parameter +# +CREATE PROCEDURE p1 (INOUT res INTEGER) +BEGIN +DECLARE a INT DEFAULT 10; +DECLARE cur1 CURSOR FOR SELECT a FROM DUAL; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +OPEN cur1; +FETCH cur1 INTO rec1; +CLOSE cur1; +SET res=rec1.a; +END; +END; +$$ +CALL p1(@res); +SELECT @res; +@res +10 +SET @res=NULL; +DROP PROCEDURE p1; +# +# Testing Item_splocal_row_field_by_name::print +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur1 DEFAULT ROW(10,'bb'); +EXPLAIN EXTENDED SELECT rec.a, rec.b; +END; +END; +$$ +CALL p1(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select rec.a@0["a"] AS `rec.a`,rec.b@0["b"] AS `rec.b` +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Run time error in the cursor statement +# +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT +10 AS a, +CONCAT(_latin1'a' COLLATE latin1_bin, +_latin1'a' COLLATE latin1_swedish_ci) AS b; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +OPEN cur1; +FETCH cur1 INTO rec1; +CLOSE cur1; +SELECT a,b; +END; +END; +$$ +CALL p1(); +ERROR HY000: Illegal mix of collations (latin1_bin,EXPLICIT) and (latin1_swedish_ci,EXPLICIT) for operation 'concat' +DROP PROCEDURE p1; +# +# Non-existing field +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur1; +SELECT rec.c; +END; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'c' +ALTER TABLE t1 ADD c INT; +ALTER PROCEDURE p1 COMMENT 'test'; +CALL p1(); +rec.c +NULL +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that field names are case insensitive +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur DEFAULT ROW(10,'bb'); +SELECT rec.A, rec.B; +END; +END; +$$ +CALL p1(); +rec.A rec.B +10 bb +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that cursor ROW TYPE uses temporary tables vs shadowed real tables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur DEFAULT ROW(10,'bb'); +SELECT rec.A, rec.B; +END; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'A' +DROP TEMPORARY TABLE t1; +ALTER PROCEDURE p1 COMMENT 'test'; +CALL p1(); +rec.A rec.B +10 bb +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that the structure of cursor ROW TYPE variables is determined at the DECLARE CURSOR instantiation time +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +DROP TABLE t1; +CREATE TABLE t1 (a INT, b VARCHAR(32), c INT); +BEGIN +DECLARE rec ROW TYPE OF cur; -- This has a column "c" + SET rec.c=10; +END; +END; +$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur; -- This does not have a column "c" + DROP TABLE t1; +CREATE TABLE t1 (a INT, b VARCHAR(32), c INT); +SET rec.c=10; +END; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'c' +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Duplicate field nams in a cursor referenced by cursor ROW TYPE +# +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT * FROM t1, t2; +BEGIN +DECLARE rec ROW TYPE OF cur; +SELECT rec.a; +SET rec.a=10; +END; +END; +$$ +CALL p1(); +ERROR 42S21: Duplicate column name 'a' +DROP PROCEDURE p1; +DROP TABLE t2; +DROP TABLE t1; +# +# Tricky field names a cursor referenced by cursor ROW TYPE +# +SET NAMES utf8; +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 VALUES ('a'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur CURSOR FOR SELECT a, CONCAT(a,'a'), CONCAT(a,'ö') FROM t1; +BEGIN +DECLARE rec ROW TYPE OF cur; +OPEN cur; +FETCH cur INTO rec; +CLOSE cur; +SELECT rec.a, rec.`CONCAT(a,'a')`, rec.`CONCAT(a,'ö')`; +END; +END; +$$ +CALL p1(); +rec.a rec.`CONCAT(a,'a')` rec.`CONCAT(a,'ö')` +a aa aö +DROP PROCEDURE p1; +DROP TABLE t1; +SET NAMES latin1; +# +# Using definitions recursively (cursor ROW TYPE variables in another cursor SELECT) +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'b1'),(20,'b2'),(30,'b3'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT a,b FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(0,'b0'); +DECLARE cur2 CURSOR FOR SELECT rec1.a AS a, rec1.b AS b FROM t1; +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE rec2 ROW TYPE OF cur2; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; +OPEN cur2; +read_loop: LOOP +FETCH cur2 INTO rec2; +IF done THEN +LEAVE read_loop; +END IF; +SELECT rec2.a, rec2.b; +END LOOP; +CLOSE cur2; +END; +END; +END; +$$ +CALL p1(); +rec2.a rec2.b +0 b0 +rec2.a rec2.b +0 b0 +rec2.a rec2.b +0 b0 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing queries with auto-generated Items. +# An instance of Item_func_conv_charset is created during the below SELECT query. +# We check here that during an implicit cursor OPEN +# done in sp_instr_cursor_copy_struct::exec_core() +# all temporary Items are created on a proper memory root and are safely destroyed. +# +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1, b VARCHAR(10) CHARACTER SET utf8); +INSERT INTO t1 VALUES (0xFF, 'a'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT CONCAT(a,b) AS c FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +OPEN cur1; +FETCH cur1 INTO rec1; +CLOSE cur1; +SELECT HEX(rec1.c); +END; +END; +$$ +CALL p1(); +HEX(rec1.c) +C3BF61 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# SELECT INTO + cursor ROW TYPE variable with a wrong column count +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT 10, 'b0', 'c0'; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +SELECT * FROM t1 INTO rec1; +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +ERROR 21000: The used SELECT statements have a different number of columns +DROP TABLE t1; +DROP PROCEDURE p1; +# +# SELECT INTO + multiple cursor ROW TYPE variables +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +SELECT * FROM t1 INTO rec1, rec1; +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# SELECT INTO + cursor ROW TYPE working example +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +BEGIN +DECLARE rec1 ROW TYPE OF cur1; +SELECT * FROM t1 INTO rec1; +SELECT rec1.a, rec1.b; +END; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 b10 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# diff --git a/mysql-test/r/sp-anchor-row-type-table.result b/mysql-test/r/sp-anchor-row-type-table.result new file mode 100644 index 0000000000000..21c931abf4472 --- /dev/null +++ b/mysql-test/r/sp-anchor-row-type-table.result @@ -0,0 +1,681 @@ +# +# Start of 10.3 tests +# +# +# MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +# +# Referring to a table in a non-existing database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF test2.t1; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test2.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +ERROR 42S02: Table 'test2.t1' doesn't exist +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Referring to a table in the current database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF t1; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Referring to a table in an explicitly specified database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF test.t1; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Referring to a view in the current database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF v1; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.v1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Referring to a view in an explicitly specified database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF test.v1; +CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.v1' doesn't exist +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `rec.a` bigint(11) DEFAULT NULL, + `rec.b` varchar(10) DEFAULT NULL, + `rec.c` double DEFAULT NULL, + `rec.d` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Checking that all table ROW TYPE fields are NULL by default +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +SELECT rec1.a, rec1.b, rec1.c, rec1.d; +END; +$$ +CALL p1(); +rec1.a rec1.b rec1.c rec1.d +NULL NULL NULL NULL +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A table ROW TYPE variable with a ROW expression as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb'); +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A table ROW TYPE variable with an incompatible ROW expression as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb','ccc'); +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A table ROW TYPE variable with a ROW variable as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW(a INT, b VARCHAR(10)) DEFAULT ROW(10,'bbb'); +DECLARE rec2 ROW TYPE OF t1 DEFAULT rec1; +SELECT rec2.a, rec2.b; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# A ROW variable using a table ROW TYPE variable as a default +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb'); +DECLARE rec2 ROW(a INT, b VARCHAR(10)) DEFAULT rec1; +SELECT rec2.a, rec2.b; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 bbb +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning table ROW TYPE variables with a different column count +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +DECLARE rec2 ROW TYPE OF t2; +SET rec2=rec1; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP PROCEDURE p1; +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +DECLARE rec2 ROW TYPE OF t2; +SET rec1=rec2; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 3 column(s) +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning compatible table ROW TYPE variables (equal number of fields) +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +DECLARE rec2 ROW TYPE OF t2; +SET rec1.a= 10; +SET rec1.b= 'bbb'; +SET rec2=rec1; +SELECT rec2.x, rec2.y; +END; +$$ +CALL p1(); +rec2.x rec2.y +10 bbb +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning between incompatible table ROW TYPE and explicit ROW variables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +DECLARE rec2 ROW(x INT,y INT,z INT); +SET rec2.x= 10; +SET rec2.y= 20; +SET rec2.z= 30; +SET rec1= rec2; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning between compatible table ROW TYPE and explicit ROW variables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +DECLARE rec2 ROW(x INT,y INT); +SET rec2.x= 10; +SET rec2.y= 20; +SET rec1= rec2; +SELECT rec1.a, rec1.b; +SET rec1.a= 11; +SET rec1.b= 21; +SET rec2= rec1; +SELECT rec2.x, rec2.y; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 20 +rec2.x rec2.y +11 21 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Assigning table ROW TYPE from a ROW expression +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +SET rec1= ROW(10,20); +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 20 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a table ROW TYPE variable with a wrong field count +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec2 ROW TYPE OF t2; +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +OPEN cur1; +FETCH cur1 INTO rec2; +CLOSE cur1; +END; +$$ +CALL p1(); +ERROR HY000: Incorrect number of FETCH variables +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a table ROW TYPE variable +# +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE rec ROW TYPE OF t1; +DECLARE cur CURSOR FOR SELECT * FROM t1; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; +OPEN cur; +read_loop: LOOP +FETCH cur INTO rec; +IF done THEN +LEAVE read_loop; +END IF; +SELECT rec.a, rec.b, rec.c, rec.d; +INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); +END LOOP; +CLOSE cur; +END; +$$ +CALL p1(); +rec.a rec.b rec.c rec.d +10 bb1 11111.1 12.31 +rec.a rec.b rec.c rec.d +20 bb2 22222.2 12.32 +rec.a rec.b rec.c rec.d +30 bb3 33333.3 12.33 +SELECT * FROM t2; +a b c d +10 bb1 11111.1 12.31 +20 bb2 22222.2 12.32 +30 bb3 33333.3 12.33 +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a table ROW TYPE variable with different column names +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bbb'); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec2 ROW TYPE OF t2; +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +OPEN cur1; +FETCH cur1 INTO rec2; +SELECT rec2.x, rec2.y; +CLOSE cur1; +END; +$$ +CALL p1(); +rec2.x rec2.y +10 bbb +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +# +# Fetching a cursor into a table ROW TYPE variable, with truncation +# +SET sql_mode=''; +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (10,'11x'); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec2 ROW TYPE OF t2; +DECLARE cur1 CURSOR FOR SELECT * FROM t1; +OPEN cur1; +FETCH cur1 INTO rec2; +SELECT rec2.a, rec2.b; +CLOSE cur1; +END; +$$ +CALL p1(); +rec2.a rec2.b +10 11 +Warnings: +Warning 1265 Data truncated for column 'b' at row 1 +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +SET sql_mode=DEFAULT; +# +# table ROW TYPE variables are not allowed in LIMIT +# +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT (1,2); +SELECT * FROM t1 LIMIT rec1.a; +END; +$$ +ERROR HY000: A variable of a non-integer based type in LIMIT clause +DROP TABLE t1; +# +# table ROW TYPE variable fields as OUT parameters +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(OUT a INT,OUT b VARCHAR(10)) +BEGIN +SET a=10; +SET b='bb'; +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +CALL p1(rec1.a, rec1.b); +SELECT rec1.a, rec1.b; +END; +$$ +CALL p2(); +rec1.a rec1.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Passing the entire table ROW TYPE variable +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10))) +BEGIN +SELECT a.a, a.b; +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bb'); +CALL p1(rec1); +END; +$$ +CALL p2(); +a.a a.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Passing the entire table ROW TYPE variable as an OUT parameter +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1(OUT a ROW(a INT, b VARCHAR(10))) +BEGIN +SET a= ROW(10,'bb'); +END; +$$ +CREATE PROCEDURE p2() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +CALL p1(rec1); +SELECT rec1.a, rec1.b; +END; +$$ +CALL p2(); +rec1.a rec1.b +10 bb +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Assigning a table ROW TYPE field to an OUT parameter +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1 (INOUT res INTEGER) +BEGIN +DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'b0'); +SET res=rec1.a; +END; +$$ +CALL p1(@res); +SELECT @res; +@res +10 +SET @res=NULL; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing Item_splocal_row_field_by_name::print +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); +EXPLAIN EXTENDED SELECT rec.a, rec.b; +END; +$$ +CALL p1(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select rec.a@0["a"] AS `rec.a`,rec.b@0["b"] AS `rec.b` +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Non-existing field +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF t1; +SELECT rec.c; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'c' +ALTER TABLE t1 ADD c INT; +CALL p1(); +rec.c +NULL +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that field names are case insensitive +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); +SELECT rec.A, rec.B; +END; +$$ +CALL p1(); +rec.A rec.B +10 bb +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that table ROW TYPE uses temporary tables vs shadowed real tables +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); +SELECT rec.A, rec.B; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'A' +DROP TEMPORARY TABLE t1; +CALL p1(); +rec.A rec.B +10 bb +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Testing that the structure of table ROW TYPE variables is determined at the very beginning and is not changed after ALTER +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +ALTER TABLE t1 ADD c INT; +BEGIN +DECLARE rec ROW TYPE OF t1; -- this will not have column "c" + SET rec.c=10; +END; +END; +$$ +CALL p1(); +ERROR HY000: Row variable 'rec' does not have a field 'c' +DROP TABLE t1; +DROP PROCEDURE p1; +# +# SELECT INTO + table ROW TYPE variable with a wrong column count +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +SELECT 10,'a','b' FROM t1 INTO rec1; +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +ERROR 21000: The used SELECT statements have a different number of columns +DROP TABLE t1; +DROP PROCEDURE p1; +# +# SELECT INTO + multiple table ROW TYPE variables +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +SELECT 10,'a' FROM t1 INTO rec1, rec1; +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +ERROR 21000: Operand should contain 2 column(s) +DROP TABLE t1; +DROP PROCEDURE p1; +# +# SELECT INTO + table ROW TYPE working example +# +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +DECLARE rec1 ROW TYPE OF t1; +SELECT * FROM t1 INTO rec1; +SELECT rec1.a, rec1.b; +END; +$$ +CALL p1(); +rec1.a rec1.b +10 b10 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +# +# MDEV-12347 Valgrind reports invalid read errors in Item_field_row::element_index_by_name +# +CREATE TABLE t1 (a INT, b ENUM('b0','b1','b12','b3')); +CREATE PROCEDURE p1() +BEGIN +BEGIN +DECLARE rec ROW TYPE OF t1; +SET rec.b='b0'; +SELECT rec.b; +END; +END; +$$ +CALL p1(); +rec.b +b0 +DROP TABLE t1; +DROP PROCEDURE p1; +CREATE TABLE t1 (a INT, b SET('b0','b1','b12','b3')); +CREATE PROCEDURE p1() +BEGIN +BEGIN +DECLARE rec ROW TYPE OF t1; +SET rec.b='b0'; +SELECT rec.b; +END; +END; +$$ +CALL p1(); +rec.b +b0 +DROP TABLE t1; +DROP PROCEDURE p1; diff --git a/mysql-test/r/sp-anchor-type.result b/mysql-test/r/sp-anchor-type.result new file mode 100644 index 0000000000000..44f67531b2f0e --- /dev/null +++ b/mysql-test/r/sp-anchor-type.result @@ -0,0 +1,967 @@ +# +# Start of 10.3 tests +# +# +# MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +# +# Missing table +# +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a; +END; +$$ +CALL p1(); +ERROR 42S02: Table 'test.t1' doesn't exist +DROP PROCEDURE p1; +# +# Missing column +# +CREATE TABLE t1 (b INT); +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a; +END; +$$ +CALL p1(); +ERROR 42S22: Unknown column 'a' in 't1' +DROP PROCEDURE p1; +DROP TABLE t1; +# +# One TYPE OF variable +# +CREATE TABLE t1 (a INT); +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a; +SET a= 123; +SELECT a; +END; +$$ +CALL p1(); +a +123 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Two TYPE OF variables, with a truncation warning on assignment +# +SET sql_mode=''; +CREATE TABLE t1 (a TINYINT, b INT); +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a; +DECLARE b TYPE OF t1.b; +SET a= 200; +SET b= 200; +SELECT a, b; +END; +$$ +CALL p1(); +a b +127 200 +Warnings: +Warning 1264 Out of range value for column 'a' at row 1 +DROP PROCEDURE p1; +DROP TABLE t1; +SET sql_mode=DEFAULT; +# +# TYPE OF variables for fields with various attributes +# +CREATE TABLE t1 ( +id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, +a TINYINT NOT NULL, +b INT NOT NULL, +ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +UNIQUE(a) +); +CREATE PROCEDURE p1() +BEGIN +DECLARE id TYPE OF t1.id; +DECLARE a TYPE OF t1.a; +DECLARE b TYPE OF t1.b; +DECLARE ts TYPE OF t1.ts; +SELECT id, a, b, ts; +CREATE TABLE t2 AS SELECT id, a, b, ts; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +id a b ts +NULL NULL NULL NULL +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int(11) DEFAULT NULL, + `a` tinyint(4) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `ts` timestamp NULL DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# TYPE OF + virtual columns +# +CREATE TABLE t1 ( +a INT NOT NULL, +b VARCHAR(32), +c INT AS (a + 10) VIRTUAL, +d VARCHAR(5) AS (left(b,5)) PERSISTENT +); +CREATE PROCEDURE p1() +BEGIN +DECLARE c TYPE OF t1.c; +DECLARE d TYPE OF t1.d; +SELECT c, d; +CREATE TABLE t2 AS SELECT c, d; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +c d +NULL NULL +Table Create Table +t2 CREATE TABLE `t2` ( + `c` int(11) DEFAULT NULL, + `d` varchar(5) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# TYPE OF + the ZEROFILL attribute +# +CREATE TABLE t1 ( +dz DECIMAL(10,3) ZEROFILL +); +CREATE PROCEDURE p1() +BEGIN +DECLARE dzr TYPE OF t1.dz DEFAULT 10; +DECLARE dzt DECIMAL(10,3) ZEROFILL DEFAULT 10; +SELECT dzr, dzt; +CREATE TABLE t2 AS SELECT dzr,dzt; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +dzr dzt +0000010.000 0000010.000 +Table Create Table +t2 CREATE TABLE `t2` ( + `dzr` decimal(10,3) unsigned DEFAULT NULL, + `dzt` decimal(10,3) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Temporary tables shadow real tables for TYPE OF purposes +# +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 VALUES ('t1'); +CREATE TEMPORARY TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a DEFAULT 11; +CREATE TABLE t2 AS SELECT a; +END; +$$ +# +# Should use INT(11) as TYPE OF, as in the temporary table +# +CALL p1(); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +a +11 +DROP TABLE t2; +SELECT * FROM t1; +a +10 +DROP TEMPORARY TABLE t1; +SELECT * FROM t1; +a +t1 +# +# Should use VARCHAR(10) as TYPE OF, as in the real table +# +CALL p1(); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +a +11 +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# "TYPE OF t1.a" searches for "t1" in the current database +# +CREATE TABLE t1 (a VARCHAR(10)); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (a INT); +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a DEFAULT 11; +CREATE TABLE test.t2 AS SELECT a; +END; +$$ +# +# This interprets "TYPE OF t1.a" as VARCHAR(10), as in test.t1.a +# +USE test; +CALL test.p1(); +SHOW CREATE TABLE test.t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE test.t2; +# +# This interprets "TYPE OF t1.a" as INT, as in test1.t1.a +# +USE test1; +CALL test.p1(); +SHOW CREATE TABLE test.t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE test.t2; +# +# Error if there is no an active database +# +DROP DATABASE test1; +CALL test.p1(); +ERROR 3D000: No database selected +USE test; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# A reference to a table in a non-existing database +# +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF test1.t1.a; +CREATE TABLE t1 AS SELECT a; +END; +$$ +CALL p1; +ERROR 42S02: Table 'test1.t1' doesn't exist +DROP PROCEDURE p1; +# +# A reference to a table in a different database +# +CREATE TABLE t1(a INT); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (a VARCHAR(10)); +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF t1.a; +DECLARE b TYPE OF test1.t1.a; +CREATE TABLE t2 AS SELECT a,b; +END; +$$ +CALL p1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `b` varchar(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +DROP TABLE t2; +DROP DATABASE test1; +DROP TABLE t1; +# +# Using a table before it appears in a TYPE OF declaration + multiple TYPE OF declarations +# +CREATE TABLE t1 (a INT, b VARCHAR(10)); +INSERT INTO t1 (a,b) VALUES (10,'b10'); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a,b) VALUES (11, 'b11'); +SELECT * FROM t1; +BEGIN +DECLARE va TYPE OF t1.a DEFAULT 30; +DECLARE vb TYPE OF t1.b DEFAULT 'b30'; +INSERT INTO t1 (a,b) VALUES (12,'b12'); +SELECT * FROM t1; +INSERT INTO t1 (a,b) VALUES (va, vb); +SELECT * FROM t1; +END; +BEGIN +DECLARE va TYPE OF t1.a DEFAULT 40; +DECLARE vb TYPE OF t1.b DEFAULT 'b40'; +INSERT INTO t1 (a,b) VALUES (va,vb); +SELECT * FROM t1; +END; +END; +$$ +CALL p1; +a b +10 b10 +11 b11 +a b +10 b10 +11 b11 +12 b12 +a b +10 b10 +11 b11 +12 b12 +30 b30 +a b +10 b10 +11 b11 +12 b12 +30 b30 +40 b40 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# TYPE OF variables + TABLE vs VIEW +# +CREATE TABLE t1 ( +bit6 BIT(6), +bit7 BIT(7), +bit8 BIT(8), +i1 TINYINT, +i2 SMALLINT, +i3 MEDIUMINT, +i4 INT, +i8 BIGINT, +ff FLOAT, +fd DOUBLE, +cc CHAR(10), +cv VARCHAR(10), +cvu VARCHAR(10) CHARACTER SET utf8, +t1 TINYTEXT, +t2 TEXT, +t3 MEDIUMTEXT, +t4 LONGTEXT, +enum1 ENUM('a','b','c'), +set1 SET('a','b','c'), +blob1 TINYBLOB, +blob2 BLOB, +blob3 MEDIUMBLOB, +blob4 LONGBLOB, +yy YEAR, +dd DATE, +tm0 TIME, +tm3 TIME(3), +tm6 TIME(6), +dt0 DATETIME, +dt3 DATETIME(3), +dt6 DATETIME(6), +ts0 TIMESTAMP, +ts3 TIMESTAMP(3), +ts6 TIMESTAMP(6), +dc100 DECIMAL(10,0), +dc103 DECIMAL(10,3), +dc209 DECIMAL(20,9) +); +CREATE PROCEDURE p1(command enum('create','select')) +BEGIN +DECLARE bit6 TYPE OF t1.bit6 DEFAULT 0x30; +DECLARE bit7 TYPE OF t1.bit7 DEFAULT 0x41; +DECLARE bit8 TYPE OF t1.bit8 DEFAULT 0x7E; +DECLARE i1 TYPE OF t1.i1 DEFAULT 11; +DECLARE i2 TYPE OF t1.i2 DEFAULT 12; +DECLARE i3 TYPE OF t1.i3 DEFAULT 13; +DECLARE i4 TYPE OF t1.i4 DEFAULT 14; +DECLARE i8 TYPE OF t1.i8 DEFAULT 18; +DECLARE ff TYPE OF t1.ff DEFAULT 21; +DECLARE fd TYPE OF t1.fd DEFAULT 22; +DECLARE cc TYPE OF t1.cc DEFAULT 'char'; +DECLARE cv TYPE OF t1.cv DEFAULT 'varchar'; +DECLARE cvu TYPE OF t1.cvu DEFAULT 'varcharu8'; +DECLARE t1 TYPE OF t1.t1 DEFAULT 'text1'; +DECLARE t2 TYPE OF t1.t2 DEFAULT 'text2'; +DECLARE t3 TYPE OF t1.t3 DEFAULT 'text3'; +DECLARE t4 TYPE OF t1.t4 DEFAULT 'text4'; +DECLARE enum1 TYPE OF t1.enum1 DEFAULT 'b'; +DECLARE set1 TYPE OF t1.set1 DEFAULT 'a,c'; +DECLARE blob1 TYPE OF t1.blob1 DEFAULT 'blob1'; +DECLARE blob2 TYPE OF t1.blob2 DEFAULT 'blob2'; +DECLARE blob3 TYPE OF t1.blob3 DEFAULT 'blob3'; +DECLARE blob4 TYPE OF t1.blob4 DEFAULT 'blob4'; +DECLARE yy TYPE OF t1.yy DEFAULT 2001; +DECLARE dd TYPE OF t1.dd DEFAULT '2001-01-01'; +DECLARE tm0 TYPE OF t1.tm0 DEFAULT '00:00:01'; +DECLARE tm3 TYPE OF t1.tm3 DEFAULT '00:00:03.333'; +DECLARE tm6 TYPE OF t1.tm6 DEFAULT '00:00:06.666666'; +DECLARE dt0 TYPE OF t1.dt0 DEFAULT '2001-01-01 00:00:01'; +DECLARE dt3 TYPE OF t1.dt3 DEFAULT '2001-01-03 00:00:01.333'; +DECLARE dt6 TYPE OF t1.dt6 DEFAULT '2001-01-06 00:00:01.666666'; +DECLARE ts0 TYPE OF t1.ts0 DEFAULT '2002-01-01 00:00:01'; +DECLARE ts3 TYPE OF t1.ts3 DEFAULT '2002-01-03 00:00:01.333'; +DECLARE ts6 TYPE OF t1.ts6 DEFAULT '2002-01-06 00:00:01.666666'; +DECLARE dc100 TYPE OF t1.dc100 DEFAULT 10; +DECLARE dc103 TYPE OF t1.dc103 DEFAULT 10.123; +DECLARE dc209 TYPE OF t1.dc209 DEFAULT 10.123456789; +CASE +WHEN command='create' THEN +CREATE TABLE t2 AS SELECT +bit6, bit7, bit8, +i1,i2,i3,i4,i8, +ff,fd, dc100, dc103, dc209, +cc,cv,cvu, +t1,t2,t3,t4, +enum1, set1, +blob1, blob2, blob3, blob4, +dd, yy, +tm0, tm3, tm6, +dt0, dt3, dt6, +ts0, ts3, ts6; +WHEN command='select' THEN +SELECT +bit6, bit7, bit8, +i1,i2,i3,i4,i8, +ff,fd, dc100, dc103, dc209, +cc,cv,cvu, +t1,t2,t3,t4, +enum1, set1, +blob1, blob2, blob3, blob4, +dd, yy, +tm0, tm3, tm6, +dt0, dt3, dt6, +ts0, ts3, ts6; +END CASE; +END; +$$ +# +# TABLE +# +CALL p1('create'); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `bit6` bit(6) DEFAULT NULL, + `bit7` bit(7) DEFAULT NULL, + `bit8` bit(8) DEFAULT NULL, + `i1` tinyint(4) DEFAULT NULL, + `i2` smallint(6) DEFAULT NULL, + `i3` mediumint(9) DEFAULT NULL, + `i4` int(11) DEFAULT NULL, + `i8` bigint(20) DEFAULT NULL, + `ff` float DEFAULT NULL, + `fd` double DEFAULT NULL, + `dc100` decimal(10,0) DEFAULT NULL, + `dc103` decimal(10,3) DEFAULT NULL, + `dc209` decimal(20,9) DEFAULT NULL, + `cc` varchar(10) DEFAULT NULL, + `cv` varchar(10) DEFAULT NULL, + `cvu` varchar(10) CHARACTER SET utf8 DEFAULT NULL, + `t1` tinytext DEFAULT NULL, + `t2` text DEFAULT NULL, + `t3` mediumtext DEFAULT NULL, + `t4` longtext DEFAULT NULL, + `enum1` varchar(1) DEFAULT NULL, + `set1` varchar(5) DEFAULT NULL, + `blob1` tinyblob DEFAULT NULL, + `blob2` blob DEFAULT NULL, + `blob3` mediumblob DEFAULT NULL, + `blob4` longblob DEFAULT NULL, + `dd` date DEFAULT NULL, + `yy` year(4) DEFAULT NULL, + `tm0` time DEFAULT NULL, + `tm3` time(3) DEFAULT NULL, + `tm6` time(6) DEFAULT NULL, + `dt0` datetime DEFAULT NULL, + `dt3` datetime(3) DEFAULT NULL, + `dt6` datetime(6) DEFAULT NULL, + `ts0` timestamp NULL DEFAULT NULL, + `ts3` timestamp(3) NULL DEFAULT NULL, + `ts6` timestamp(6) NULL DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +bit6 0 +bit7 A +bit8 ~ +i1 11 +i2 12 +i3 13 +i4 14 +i8 18 +ff 21 +fd 22 +dc100 10 +dc103 10.123 +dc209 10.123456789 +cc char +cv varchar +cvu varcharu8 +t1 text1 +t2 text2 +t3 text3 +t4 text4 +enum1 b +set1 a,c +blob1 blob1 +blob2 blob2 +blob3 blob3 +blob4 blob4 +dd 2001-01-01 +yy 2001 +tm0 00:00:01 +tm3 00:00:03.333 +tm6 00:00:06.666666 +dt0 2001-01-01 00:00:01 +dt3 2001-01-03 00:00:01.333 +dt6 2001-01-06 00:00:01.666666 +ts0 2002-01-01 00:00:01 +ts3 2002-01-03 00:00:01.333 +ts6 2002-01-06 00:00:01.666666 +DROP TABLE t2; +CALL p1('select'); +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def bit6 bit6 16 6 1 Y 32 0 63 +def bit7 bit7 16 7 1 Y 32 0 63 +def bit8 bit8 16 8 1 Y 32 0 63 +def i1 i1 1 4 2 Y 32768 0 63 +def i2 i2 2 6 2 Y 32768 0 63 +def i3 i3 9 9 2 Y 32768 0 63 +def i4 i4 3 11 2 Y 32768 0 63 +def i8 i8 8 20 2 Y 32768 0 63 +def ff ff 4 12 2 Y 32768 31 63 +def fd fd 5 22 2 Y 32768 31 63 +def dc100 dc100 246 11 2 Y 32768 0 63 +def dc103 dc103 246 12 6 Y 32768 3 63 +def dc209 dc209 246 22 12 Y 32768 9 63 +def cc cc 254 10 4 Y 0 0 8 +def cv cv 253 10 7 Y 0 0 8 +def cvu cvu 253 10 9 Y 0 0 8 +def t1 t1 252 255 5 Y 16 0 8 +def t2 t2 252 65535 5 Y 16 0 8 +def t3 t3 252 16777215 5 Y 16 0 8 +def t4 t4 252 4294967295 5 Y 16 0 8 +def enum1 enum1 254 1 1 Y 256 0 8 +def set1 set1 254 5 3 Y 2048 0 8 +def blob1 blob1 252 255 5 Y 144 0 63 +def blob2 blob2 252 65535 5 Y 144 0 63 +def blob3 blob3 252 16777215 5 Y 144 0 63 +def blob4 blob4 252 4294967295 5 Y 144 0 63 +def dd dd 10 10 10 Y 128 0 63 +def yy yy 13 4 4 Y 32864 0 63 +def tm0 tm0 11 10 8 Y 128 0 63 +def tm3 tm3 11 14 12 Y 128 3 63 +def tm6 tm6 11 17 15 Y 128 6 63 +def dt0 dt0 12 19 19 Y 128 0 63 +def dt3 dt3 12 23 23 Y 128 3 63 +def dt6 dt6 12 26 26 Y 128 6 63 +def ts0 ts0 7 19 19 Y 9376 0 63 +def ts3 ts3 7 23 23 Y 160 3 63 +def ts6 ts6 7 26 26 Y 160 6 63 +bit6 0 +bit7 A +bit8 ~ +i1 11 +i2 12 +i3 13 +i4 14 +i8 18 +ff 21 +fd 22 +dc100 10 +dc103 10.123 +dc209 10.123456789 +cc char +cv varchar +cvu varcharu8 +t1 text1 +t2 text2 +t3 text3 +t4 text4 +enum1 b +set1 a,c +blob1 blob1 +blob2 blob2 +blob3 blob3 +blob4 blob4 +dd 2001-01-01 +yy 2001 +tm0 00:00:01 +tm3 00:00:03.333 +tm6 00:00:06.666666 +dt0 2001-01-01 00:00:01 +dt3 2001-01-03 00:00:01.333 +dt6 2001-01-06 00:00:01.666666 +ts0 2002-01-01 00:00:01 +ts3 2002-01-03 00:00:01.333 +ts6 2002-01-06 00:00:01.666666 +# +# VIEW +# +ALTER TABLE t1 RENAME t0; +CREATE VIEW t1 AS SELECT * FROM t0; +CALL p1('create'); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `bit6` bit(6) DEFAULT NULL, + `bit7` bit(7) DEFAULT NULL, + `bit8` bit(8) DEFAULT NULL, + `i1` tinyint(4) DEFAULT NULL, + `i2` smallint(6) DEFAULT NULL, + `i3` mediumint(9) DEFAULT NULL, + `i4` int(11) DEFAULT NULL, + `i8` bigint(20) DEFAULT NULL, + `ff` float DEFAULT NULL, + `fd` double DEFAULT NULL, + `dc100` decimal(10,0) DEFAULT NULL, + `dc103` decimal(10,3) DEFAULT NULL, + `dc209` decimal(20,9) DEFAULT NULL, + `cc` varchar(10) DEFAULT NULL, + `cv` varchar(10) DEFAULT NULL, + `cvu` varchar(10) CHARACTER SET utf8 DEFAULT NULL, + `t1` tinytext DEFAULT NULL, + `t2` text DEFAULT NULL, + `t3` mediumtext DEFAULT NULL, + `t4` longtext DEFAULT NULL, + `enum1` varchar(1) DEFAULT NULL, + `set1` varchar(5) DEFAULT NULL, + `blob1` tinyblob DEFAULT NULL, + `blob2` blob DEFAULT NULL, + `blob3` mediumblob DEFAULT NULL, + `blob4` longblob DEFAULT NULL, + `dd` date DEFAULT NULL, + `yy` year(4) DEFAULT NULL, + `tm0` time DEFAULT NULL, + `tm3` time(3) DEFAULT NULL, + `tm6` time(6) DEFAULT NULL, + `dt0` datetime DEFAULT NULL, + `dt3` datetime(3) DEFAULT NULL, + `dt6` datetime(6) DEFAULT NULL, + `ts0` timestamp NULL DEFAULT NULL, + `ts3` timestamp(3) NULL DEFAULT NULL, + `ts6` timestamp(6) NULL DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +bit6 0 +bit7 A +bit8 ~ +i1 11 +i2 12 +i3 13 +i4 14 +i8 18 +ff 21 +fd 22 +dc100 10 +dc103 10.123 +dc209 10.123456789 +cc char +cv varchar +cvu varcharu8 +t1 text1 +t2 text2 +t3 text3 +t4 text4 +enum1 b +set1 a,c +blob1 blob1 +blob2 blob2 +blob3 blob3 +blob4 blob4 +dd 2001-01-01 +yy 2001 +tm0 00:00:01 +tm3 00:00:03.333 +tm6 00:00:06.666666 +dt0 2001-01-01 00:00:01 +dt3 2001-01-03 00:00:01.333 +dt6 2001-01-06 00:00:01.666666 +ts0 2002-01-01 00:00:01 +ts3 2002-01-03 00:00:01.333 +ts6 2002-01-06 00:00:01.666666 +DROP TABLE t2; +CALL p1('select'); +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def bit6 bit6 16 6 1 Y 32 0 63 +def bit7 bit7 16 7 1 Y 32 0 63 +def bit8 bit8 16 8 1 Y 32 0 63 +def i1 i1 1 4 2 Y 32768 0 63 +def i2 i2 2 6 2 Y 32768 0 63 +def i3 i3 9 9 2 Y 32768 0 63 +def i4 i4 3 11 2 Y 32768 0 63 +def i8 i8 8 20 2 Y 32768 0 63 +def ff ff 4 12 2 Y 32768 31 63 +def fd fd 5 22 2 Y 32768 31 63 +def dc100 dc100 246 11 2 Y 32768 0 63 +def dc103 dc103 246 12 6 Y 32768 3 63 +def dc209 dc209 246 22 12 Y 32768 9 63 +def cc cc 254 10 4 Y 0 0 8 +def cv cv 253 10 7 Y 0 0 8 +def cvu cvu 253 10 9 Y 0 0 8 +def t1 t1 252 255 5 Y 16 0 8 +def t2 t2 252 65535 5 Y 16 0 8 +def t3 t3 252 16777215 5 Y 16 0 8 +def t4 t4 252 4294967295 5 Y 16 0 8 +def enum1 enum1 254 1 1 Y 256 0 8 +def set1 set1 254 5 3 Y 2048 0 8 +def blob1 blob1 252 255 5 Y 144 0 63 +def blob2 blob2 252 65535 5 Y 144 0 63 +def blob3 blob3 252 16777215 5 Y 144 0 63 +def blob4 blob4 252 4294967295 5 Y 144 0 63 +def dd dd 10 10 10 Y 128 0 63 +def yy yy 13 4 4 Y 32864 0 63 +def tm0 tm0 11 10 8 Y 128 0 63 +def tm3 tm3 11 14 12 Y 128 3 63 +def tm6 tm6 11 17 15 Y 128 6 63 +def dt0 dt0 12 19 19 Y 128 0 63 +def dt3 dt3 12 23 23 Y 128 3 63 +def dt6 dt6 12 26 26 Y 128 6 63 +def ts0 ts0 7 19 19 Y 160 0 63 +def ts3 ts3 7 23 23 Y 160 3 63 +def ts6 ts6 7 26 26 Y 160 6 63 +bit6 0 +bit7 A +bit8 ~ +i1 11 +i2 12 +i3 13 +i4 14 +i8 18 +ff 21 +fd 22 +dc100 10 +dc103 10.123 +dc209 10.123456789 +cc char +cv varchar +cvu varcharu8 +t1 text1 +t2 text2 +t3 text3 +t4 text4 +enum1 b +set1 a,c +blob1 blob1 +blob2 blob2 +blob3 blob3 +blob4 blob4 +dd 2001-01-01 +yy 2001 +tm0 00:00:01 +tm3 00:00:03.333 +tm6 00:00:06.666666 +dt0 2001-01-01 00:00:01 +dt3 2001-01-03 00:00:01.333 +dt6 2001-01-06 00:00:01.666666 +ts0 2002-01-01 00:00:01 +ts3 2002-01-03 00:00:01.333 +ts6 2002-01-06 00:00:01.666666 +DROP VIEW t1; +DROP TABLE t0; +DROP PROCEDURE p1; +# +# VIEW with subqueries +# +CREATE TABLE t1 (a INT,b INT); +INSERT INTO t1 VALUES (10,1),(20,2),(30,3),(40,4); +SELECT AVG(a) FROM t1; +AVG(a) +25.0000 +CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1); +SELECT * FROM v1; +a b +30 1 +40 1 +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF v1.a DEFAULT 10; +DECLARE b TYPE OF v1.b DEFAULT 1; +SELECT a,b; +END; +$$ +CALL p1; +a b +10 1 +DROP PROCEDURE p1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +DECLARE a TYPE OF v1.a DEFAULT 10; +DECLARE b TYPE OF v1.b DEFAULT 1; +RETURN a+b; +END; +$$ +SELECT f1(); +f1() +11 +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE t1; +# +# TYPE OF variables + INFORMATION_SCHEMA +# +CREATE PROCEDURE p1() +BEGIN +DECLARE tables_table_name TYPE OF INFORMATION_SCHEMA.TABLES.TABLE_NAME; +DECLARE tables_table_rows TYPE OF INFORMATION_SCHEMA.TABLES.TABLE_ROWS; +DECLARE processlist_info TYPE OF INFORMATION_SCHEMA.PROCESSLIST.INFO; +DECLARE processlist_info_binary TYPE OF INFORMATION_SCHEMA.PROCESSLIST.INFO_BINARY; +CREATE TABLE t1 AS SELECT +tables_table_name, +tables_table_rows, +processlist_info, +processlist_info_binary; +END; +$$ +CALL p1(); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `tables_table_name` varchar(64) CHARACTER SET utf8 DEFAULT NULL, + `tables_table_rows` bigint(21) unsigned DEFAULT NULL, + `processlist_info` longtext CHARACTER SET utf8 DEFAULT NULL, + `processlist_info_binary` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# TYPE OF + Table structure change +# Data type for both a0 and a1 is chosen in the very beginning +# +CREATE PROCEDURE p1() +BEGIN +DECLARE a0 TYPE OF t1.a; +ALTER TABLE t1 MODIFY a VARCHAR(10); -- This does not affect a1 +BEGIN +DECLARE a1 TYPE OF t1.a; +CREATE TABLE t2 AS SELECT a0, a1; +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +END +$$ +CREATE TABLE t1 (a INT); +CALL p1; +Table Create Table +t2 CREATE TABLE `t2` ( + `a0` int(11) DEFAULT NULL, + `a1` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP PROCEDURE p1; +# +# TYPE OF in parameters +# +CREATE TABLE t1 (a VARCHAR(10)); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (b SMALLINT); +CREATE PROCEDURE p1(a TYPE OF t1.a, b TYPE OF test1.t1.b) +BEGIN +CREATE TABLE t2 AS SELECT a, b; +END; +$$ +CALL p1('test', 123); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(10) DEFAULT NULL, + `b` smallint(6) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +a b +test 123 +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE test1.t1; +DROP DATABASE test1; +DROP TABLE t1; +# +# TYPE OF in a stored function variables and arguments +# +CREATE TABLE t1 (a INT); +CREATE FUNCTION f1 (prm TYPE OF t1.a) RETURNS INT +BEGIN +DECLARE a TYPE OF t1.a DEFAULT prm; +RETURN a; +END; +$$ +SELECT f1(20); +f1(20) +20 +DROP FUNCTION f1; +DROP TABLE t1; +# +# TYPE OF in function RETURN clause is not supported yet +# +CREATE FUNCTION f1() RETURN TYPE OF t1.a +BEGIN +RETURN 0; +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 'RETURN TYPE OF t1.a +BEGIN +RETURN 0; +END' at line 1 +# +# Using TYPE OF variables as FETCH targets +# +CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3)); +INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123'); +INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123'); +CREATE TABLE t2 LIKE t1; +CREATE PROCEDURE p1() +BEGIN +DECLARE done INT DEFAULT 0; +DECLARE v_a TYPE OF t1.a; +DECLARE v_b TYPE OF t1.b; +DECLARE v_c TYPE OF t1.c; +DECLARE c CURSOR FOR SELECT a,b,c FROM t1; +DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; +OPEN c; +read_loop: LOOP +FETCH c INTO v_a, v_b, v_c; +IF done THEN +LEAVE read_loop; +END IF; +INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c); +END LOOP; +CLOSE c; +END; +$$ +CALL p1(); +SELECT * FROM t2; +a b c +1 b1 2001-01-01 10:20:30.123 +2 b2 2001-01-02 10:20:30.123 +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Using TYPE OF variables as SELECT INTO targets +# +CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3)); +INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123'); +CREATE PROCEDURE p1() +BEGIN +DECLARE v_a TYPE OF t1.a; +DECLARE v_b TYPE OF t1.b; +DECLARE v_c TYPE OF t1.c; +SELECT * FROM t1 INTO v_a, v_b, v_c; +SELECT v_a, v_b, v_c; +END; +$$ +CALL p1(); +v_a v_b v_c +1 b1 2001-01-01 10:20:30.123 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# diff --git a/mysql-test/r/sp-security-anchor-type.result b/mysql-test/r/sp-security-anchor-type.result new file mode 100644 index 0000000000000..4ee846f9f2ef1 --- /dev/null +++ b/mysql-test/r/sp-security-anchor-type.result @@ -0,0 +1,263 @@ +# +# MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +# +# Initiation: +# - creating database db1 +# - creating user user1 with access rights to db1 +# +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT, b VARCHAR(10)); +CREATE USER user1; +GRANT ALL PRIVILEGES ON test.* TO user1; +connect conn1,localhost,user1,,test; +SELECT database(); +database() +test +SELECT user(); +user() +user1@localhost +# +# Making sure that user1 does not have privileges to db1.t1 +# +SHOW CREATE TABLE db1.t1; +ERROR 42000: SHOW command denied to user 'user1'@'localhost' for table 't1' +SHOW FIELDS IN db1.t1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +# +# Trigger: using TYPE OF with a table we don't have access to +# +CREATE TABLE test.t1 (a INT, b INT); +INSERT INTO test.t1 (a,b) VALUES (10,20); +SELECT * FROM t1; +a b +10 20 +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN +DECLARE b TYPE OF db1.t1.b DEFAULT 20; +SET NEW.b = 10; +END +$$ +INSERT INTO t1 (a) VALUES (10); +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +SELECT * FROM t1; +a b +10 20 +DROP TRIGGER tr1; +DROP TABLE t1; +# +# Stored procedure: Using TYPE OF for with a table that we don't have access to +# DEFINER user1, SQL SECURITY DEFAULT +# +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 10; +SELECT a; +END; +$$ +CALL p1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +DROP PROCEDURE p1; +# +# Stored procedure: Using TYPE OF for with a table that we don't have access to +# DEFINER root, SQL SECURITY INVOKER +# +connection default; +CREATE PROCEDURE p1() +SQL SECURITY INVOKER +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 10; +SELECT a; +END; +$$ +connection conn1; +CALL p1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +DROP PROCEDURE p1; +connection default; +CREATE PROCEDURE p1() +SQL SECURITY INVOKER +BEGIN +DECLARE a ROW TYPE OF db1.t1; +SELECT a.a; +END; +$$ +connection conn1; +CALL p1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +DROP PROCEDURE p1; +# +# Stored procedure: Using TYPE OF for with a table that we don't have access to +# DEFINER root, SQL SECURITY DEFINER +# +connection default; +CREATE PROCEDURE p1() +SQL SECURITY DEFINER +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 10; +SELECT a; +END; +$$ +connection conn1; +CALL p1; +a +10 +DROP PROCEDURE p1; +connection default; +CREATE PROCEDURE p1() +SQL SECURITY DEFINER +BEGIN +DECLARE a ROW TYPE OF db1.t1; +SET a.a= 10; +SELECT a.a; +END; +$$ +connection conn1; +CALL p1; +a.a +10 +DROP PROCEDURE p1; +# +# Stored function: Using TYPE OF for with a table that we don't have access to +# DEFINER user1, SQL SECURITY DEFAULT +# +CREATE TABLE t1 (a INT); +CREATE FUNCTION f1() RETURNS INT +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 0; +RETURN OCTET_LENGTH(a); +END; +$$ +SELECT f1(); +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +DROP FUNCTION f1; +DROP TABLE t1; +# +# Stored function: Using TYPE OF for with a table that we don't have access to +# DEFINER root, SQL SECURITY INVOKER +# +connection default; +CREATE TABLE t1 (a INT); +CREATE FUNCTION f1() RETURNS INT +SQL SECURITY INVOKER +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 0; +RETURN OCTET_LENGTH(a); +END; +$$ +connection conn1; +SELECT f1(); +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' +DROP FUNCTION f1; +DROP TABLE t1; +# +# Stored function: Using TYPE OF for with a table that we don't have access to +# DEFINER root, SQL SECURITY DEFINER +# +connection default; +CREATE TABLE t1 (a INT); +CREATE FUNCTION f1() RETURNS INT +SQL SECURITY DEFINER +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 0; +RETURN OCTET_LENGTH(a); +END; +$$ +connection conn1; +SELECT f1(); +f1() +1 +DROP FUNCTION f1; +DROP TABLE t1; +connection default; +GRANT SELECT (a) ON db1.t1 TO user1; +connection conn1; +# +# Making sure that user1 has access to db1.t1.a, but not to db1.t1.b +# +SHOW CREATE TABLE db1.t1; +ERROR 42000: SHOW command denied to user 'user1'@'localhost' for table 't1' +SHOW FIELDS IN db1.t1; +Field Type Null Key Default Extra +a int(11) YES NULL +# +# Trigger: Per-column privileges +# +CREATE TABLE test.t1 (a INT, b INT); +INSERT INTO test.t1 (a,b) VALUES (10,20); +SELECT * FROM t1; +a b +10 20 +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 20; +BEGIN +SET NEW.b := 10; +END; +END +$$ +INSERT INTO t1 (a) VALUES (10); +SELECT * FROM t1; +a b +10 20 +10 10 +DROP TRIGGER tr1; +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN +DECLARE b TYPE OF db1.t1.b DEFAULT 20; +BEGIN +SET NEW.b = 10; +END; +END +$$ +INSERT INTO t1 (a) VALUES (10); +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1' +SELECT * FROM t1; +a b +10 20 +10 10 +DROP TRIGGER tr1; +DROP TABLE t1; +# +# Stored procedure: Per-column privileges +# DEFINER user1, SQL SECURITY DEFAULT +# +CREATE PROCEDURE p1() +BEGIN +DECLARE a TYPE OF db1.t1.a DEFAULT 10; +SELECT a; +END; +$$ +CALL p1; +a +10 +DROP PROCEDURE p1; +CREATE PROCEDURE p1() +BEGIN +DECLARE b TYPE OF db1.t1.b DEFAULT 10; +SELECT b; +END; +$$ +CALL p1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1' +DROP PROCEDURE p1; +CREATE PROCEDURE p1() +BEGIN +DECLARE b ROW TYPE OF db1.t1; +SET b.b=10; +SELECT b.b; +END; +$$ +CALL p1; +ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1' +DROP PROCEDURE p1; +# +# Clean up +# +disconnect conn1; +connection default; +DROP USER user1; +DROP DATABASE db1; +# +# End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index e6c8f6a5e2f70..9224756407bfe 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -2368,3 +2368,23 @@ tr1 1 2016-01-01 10:10:10.33 tr2 2 2016-01-01 10:10:10.99 drop table t1; set time_zone= @@global.time_zone; +# +# Start of 10.3 tests +# +# +# MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +# +CREATE TABLE t1 (a INT, b INT, total INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 +FOR EACH ROW +BEGIN +DECLARE va TYPE OF t1.a DEFAULT NEW.a; +DECLARE vb TYPE OF t1.b DEFAULT NEW.b; +SET NEW.total:= va + vb; +END; +$$ +INSERT INTO t1 (a,b) VALUES (10, 20); +SELECT * FROM t1; +a b total +10 20 30 +DROP TABLE t1; diff --git a/mysql-test/t/sp-anchor-row-type-cursor.test b/mysql-test/t/sp-anchor-row-type-cursor.test new file mode 100644 index 0000000000000..8d17e242a5005 --- /dev/null +++ b/mysql-test/t/sp-anchor-row-type-cursor.test @@ -0,0 +1,1073 @@ + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + +--echo # +--echo # A complete working example +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'b10'); +INSERT INTO t1 VALUES (20,'b20'); +INSERT INTO t1 VALUES (30,'b30'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE c CURSOR FOR SELECT a,b FROM t1; + BEGIN + DECLARE done INT DEFAULT 0; + DECLARE rec ROW TYPE OF c; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; + OPEN c; + read_loop: LOOP + FETCH c INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT rec.a ,rec.b FROM dual; + INSERT INTO t2 VALUES (rec.a, rec.b); + END LOOP; + CLOSE c; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SELECT * FROM t2; +DROP PROCEDURE p1; +DROP TABLE t2; +DROP TABLE t1; + + +--echo # +--echo # cursor ROW TYPE referring to a table in a non-existing database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM tes2.t1; + BEGIN + DECLARE rec ROW TYPE OF cur; + END; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +--error ER_NO_SUCH_TABLE +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # cursor ROW TYPE referring to a table in the current database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +DROP TABLE t1; +--error ER_NO_SUCH_TABLE +CALL p1(); +DROP PROCEDURE p1; + + +--echo # +--echo # cursor ROW TYPE referring to a table in an explicitly specified database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM test.t1; + BEGIN + DECLARE rec ROW TYPE OF cur; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Cursor ROW TYPE referring to a view in the current database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM v1; + BEGIN + DECLARE rec ROW TYPE OF cur; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # cursor ROW TYPE referring to a view in an explicitly specified database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM test.v1; + BEGIN + DECLARE rec ROW TYPE OF cur; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Checking that all cursor ROW TYPE fields are NULL by default +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + SELECT rec1.a, rec1.b, rec1.c, rec1.d; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A cursor ROW TYPE variable with a ROW expression as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb'); + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A cursor ROW TYPE variable with an incompatible ROW expression as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb','ccc'); + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A cursor ROW TYPE variable with a ROW variable as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW(a INT, b VARCHAR(10)) DEFAULT ROW(10,'bbb'); + DECLARE rec2 ROW TYPE OF cur DEFAULT rec1; + SELECT rec2.a, rec2.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A ROW variable using a cursor ROW TYPE variable as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(10,'bbb'); + DECLARE rec2 ROW(a INT, b VARCHAR(10)) DEFAULT rec1; + SELECT rec2.a, rec2.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning cursor ROW TYPE variables with a different column count +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + DECLARE rec2 ROW TYPE OF cur2; + SET rec2=rec1; + END; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP PROCEDURE p1; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + DECLARE rec2 ROW TYPE OF cur2; + SET rec1=rec2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning compatible cursor ROW TYPE variables (equal number of fields) +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + DECLARE rec2 ROW TYPE OF cur2; + SET rec1.a= 10; + SET rec1.b= 'bbb'; + SET rec2=rec1; + SELECT rec2.x, rec2.y; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning between incompatible cursor ROW TYPE and explicit ROW variables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + DECLARE rec2 ROW(x INT,y INT,z INT); + SET rec2.x= 10; + SET rec2.y= 20; + SET rec2.z= 30; + SET rec1= rec2; + END; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning between compatible cursor ROW TYPE and explicit ROW variables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + DECLARE rec2 ROW(x INT,y INT); + SET rec2.x= 10; + SET rec2.y= 20; + SET rec1= rec2; + SELECT rec1.a, rec1.b; + SET rec1.a= 11; + SET rec1.b= 21; + SET rec2= rec1; + SELECT rec2.x, rec2.y; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning cursor ROW TYPE from a ROW expression +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + SET rec1= ROW(10,20); + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a cursor ROW TYPE variable with a wrong field count +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec2 ROW TYPE OF cur2; + OPEN cur1; + FETCH cur1 INTO rec2; + CLOSE cur1; + END; +END; +$$ +DELIMITER ;$$ +--error ER_SP_WRONG_NO_OF_FETCH_ARGS +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a cursor ROW TYPE variable +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE done INT DEFAULT 0; + DECLARE rec ROW TYPE OF cur; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; + OPEN cur; + read_loop: LOOP + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT rec.a, rec.b, rec.c, rec.d; + INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); + END LOOP; + CLOSE cur; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SELECT * FROM t2; +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a cursor ROW TYPE variable, ROW TYPE OF cur declared inside the LOOP +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT 0; + DECLARE cur CURSOR FOR SELECT * FROM t1; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; + OPEN cur; + read_loop: LOOP + BEGIN + DECLARE rec ROW TYPE OF cur; + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT rec.a, rec.b, rec.c, rec.d; + INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); + END; + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SELECT * FROM t2; +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a cursor ROW TYPE variable with different column names +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bbb'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec2 ROW TYPE OF cur2; + OPEN cur1; + FETCH cur1 INTO rec2; + SELECT rec2.x, rec2.y; + CLOSE cur1; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a cursor ROW TYPE variable, with truncation +--echo # + +SET sql_mode=''; +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (10,'11x'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + DECLARE cur2 CURSOR FOR SELECT * FROM t2; + BEGIN + DECLARE rec2 ROW TYPE OF cur2; + OPEN cur1; + FETCH cur1 INTO rec2; + SELECT rec2.a, rec2.b; + CLOSE cur1; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +SET sql_mode=DEFAULT; + + +--echo # +--echo # cursor ROW TYPE variables are not allowed in LIMIT +--echo # +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2); +DELIMITER $$; +--error ER_WRONG_SPVAR_TYPE_IN_LIMIT +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1 DEFAULT (1,2); + SELECT * FROM t1 LIMIT rec1.a; + END; +END; +$$ +DELIMITER ;$$ +DROP TABLE t1; + + +--echo # +--echo # cursor ROW TYPE variable fields as OUT parameters +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(OUT a INT,OUT b VARCHAR(10)) +BEGIN + SET a=10; + SET b='bb'; +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + CALL p1(rec1.a, rec1.b); + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Passing the entire cursor ROW TYPE variable +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10))) +BEGIN + SELECT a.a, a.b; +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur DEFAULT ROW(10,'bb'); + CALL p1(rec1); + END; +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Passing the entire cursor ROW TYPE variable as an OUT parameter +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(OUT a ROW(a INT, b VARCHAR(10))) +BEGIN + SET a= ROW(10,'bb'); +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur; + CALL p1(rec1); + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Assigning a cursor ROW TYPE field to an OUT parameter +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1 (INOUT res INTEGER) +BEGIN + DECLARE a INT DEFAULT 10; + DECLARE cur1 CURSOR FOR SELECT a FROM DUAL; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + OPEN cur1; + FETCH cur1 INTO rec1; + CLOSE cur1; + SET res=rec1.a; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(@res); +SELECT @res; +SET @res=NULL; +DROP PROCEDURE p1; + + +--echo # +--echo # Testing Item_splocal_row_field_by_name::print +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur1 DEFAULT ROW(10,'bb'); + EXPLAIN EXTENDED SELECT rec.a, rec.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Run time error in the cursor statement +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT + 10 AS a, + CONCAT(_latin1'a' COLLATE latin1_bin, + _latin1'a' COLLATE latin1_swedish_ci) AS b; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + OPEN cur1; + FETCH cur1 INTO rec1; + CLOSE cur1; + SELECT a,b; + END; +END; +$$ +DELIMITER ;$$ +--error ER_CANT_AGGREGATE_2COLLATIONS +CALL p1(); +DROP PROCEDURE p1; + + + +--echo # +--echo # Non-existing field +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur1; + SELECT rec.c; + END; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +ALTER TABLE t1 ADD c INT; +# +# The below ALTER is needed as a workaround to call sp_cache_invalidate() +# Please remove it after fixing MDEV-12166 +# +ALTER PROCEDURE p1 COMMENT 'test'; +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing that field names are case insensitive +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur DEFAULT ROW(10,'bb'); + SELECT rec.A, rec.B; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing that cursor ROW TYPE uses temporary tables vs shadowed real tables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur DEFAULT ROW(10,'bb'); + SELECT rec.A, rec.B; + END; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +DROP TEMPORARY TABLE t1; +# +# The below ALTER is needed as a workaround to call sp_cache_invalidate() +# Please remove it after fixing MDEV-12166 +# +ALTER PROCEDURE p1 COMMENT 'test'; +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing that the structure of cursor ROW TYPE variables is determined at the DECLARE CURSOR instantiation time +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + DROP TABLE t1; + CREATE TABLE t1 (a INT, b VARCHAR(32), c INT); + BEGIN + DECLARE rec ROW TYPE OF cur; -- This has a column "c" + SET rec.c=10; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur; -- This does not have a column "c" + DROP TABLE t1; + CREATE TABLE t1 (a INT, b VARCHAR(32), c INT); + SET rec.c=10; + END; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Duplicate field nams in a cursor referenced by cursor ROW TYPE +--echo # + +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT * FROM t1, t2; + BEGIN + DECLARE rec ROW TYPE OF cur; + SELECT rec.a; + SET rec.a=10; + END; +END; +$$ +DELIMITER ;$$ +--error ER_DUP_FIELDNAME +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t2; +DROP TABLE t1; + + +--echo # +--echo # Tricky field names a cursor referenced by cursor ROW TYPE +--echo # + +SET NAMES utf8; +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 VALUES ('a'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur CURSOR FOR SELECT a, CONCAT(a,'a'), CONCAT(a,'ö') FROM t1; + BEGIN + DECLARE rec ROW TYPE OF cur; + OPEN cur; + FETCH cur INTO rec; + CLOSE cur; + SELECT rec.a, rec.`CONCAT(a,'a')`, rec.`CONCAT(a,'ö')`; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; +SET NAMES latin1; + + +--echo # +--echo # Using definitions recursively (cursor ROW TYPE variables in another cursor SELECT) +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'b1'),(20,'b2'),(30,'b3'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT a,b FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1 DEFAULT ROW(0,'b0'); + DECLARE cur2 CURSOR FOR SELECT rec1.a AS a, rec1.b AS b FROM t1; + BEGIN + DECLARE done INT DEFAULT 0; + DECLARE rec2 ROW TYPE OF cur2; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; + OPEN cur2; + read_loop: LOOP + FETCH cur2 INTO rec2; + IF done THEN + LEAVE read_loop; + END IF; + SELECT rec2.a, rec2.b; + END LOOP; + CLOSE cur2; + END; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing queries with auto-generated Items. +--echo # An instance of Item_func_conv_charset is created during the below SELECT query. +--echo # We check here that during an implicit cursor OPEN +--echo # done in sp_instr_cursor_copy_struct::exec_core() +--echo # all temporary Items are created on a proper memory root and are safely destroyed. +--echo # + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1, b VARCHAR(10) CHARACTER SET utf8); +INSERT INTO t1 VALUES (0xFF, 'a'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT CONCAT(a,b) AS c FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + OPEN cur1; + FETCH cur1 INTO rec1; + CLOSE cur1; + SELECT HEX(rec1.c); + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +--echo # +--echo # SELECT INTO + cursor ROW TYPE variable with a wrong column count +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT 10, 'b0', 'c0'; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + SELECT * FROM t1 INTO rec1; + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +--error ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # SELECT INTO + multiple cursor ROW TYPE variables +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + SELECT * FROM t1 INTO rec1, rec1; + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # SELECT INTO + cursor ROW TYPE working example +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + BEGIN + DECLARE rec1 ROW TYPE OF cur1; + SELECT * FROM t1 INTO rec1; + SELECT rec1.a, rec1.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + +--echo # +--echo # End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # diff --git a/mysql-test/t/sp-anchor-row-type-table.test b/mysql-test/t/sp-anchor-row-type-table.test new file mode 100644 index 0000000000000..97e0a1d580589 --- /dev/null +++ b/mysql-test/t/sp-anchor-row-type-table.test @@ -0,0 +1,761 @@ + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + +--echo # +--echo # Referring to a table in a non-existing database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF test2.t1; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +--error ER_NO_SUCH_TABLE +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Referring to a table in the current database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF t1; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Referring to a table in an explicitly specified database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF test.t1; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Referring to a view in the current database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF v1; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Referring to a view in an explicitly specified database +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF test.v1; + CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10)); +CREATE VIEW v1 AS SELECT * FROM t1; +CALL p1(); +DROP VIEW v1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Checking that all table ROW TYPE fields are NULL by default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + SELECT rec1.a, rec1.b, rec1.c, rec1.d; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A table ROW TYPE variable with a ROW expression as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb'); + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A table ROW TYPE variable with an incompatible ROW expression as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb','ccc'); + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A table ROW TYPE variable with a ROW variable as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW(a INT, b VARCHAR(10)) DEFAULT ROW(10,'bbb'); + DECLARE rec2 ROW TYPE OF t1 DEFAULT rec1; + SELECT rec2.a, rec2.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # A ROW variable using a table ROW TYPE variable as a default +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bbb'); + DECLARE rec2 ROW(a INT, b VARCHAR(10)) DEFAULT rec1; + SELECT rec2.a, rec2.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning table ROW TYPE variables with a different column count +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + DECLARE rec2 ROW TYPE OF t2; + SET rec2=rec1; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP PROCEDURE p1; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + DECLARE rec2 ROW TYPE OF t2; + SET rec1=rec2; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning compatible table ROW TYPE variables (equal number of fields) +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + DECLARE rec2 ROW TYPE OF t2; + SET rec1.a= 10; + SET rec1.b= 'bbb'; + SET rec2=rec1; + SELECT rec2.x, rec2.y; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning between incompatible table ROW TYPE and explicit ROW variables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + DECLARE rec2 ROW(x INT,y INT,z INT); + SET rec2.x= 10; + SET rec2.y= 20; + SET rec2.z= 30; + SET rec1= rec2; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning between compatible table ROW TYPE and explicit ROW variables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + DECLARE rec2 ROW(x INT,y INT); + SET rec2.x= 10; + SET rec2.y= 20; + SET rec1= rec2; + SELECT rec1.a, rec1.b; + SET rec1.a= 11; + SET rec1.b= 21; + SET rec2= rec1; + SELECT rec2.x, rec2.y; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Assigning table ROW TYPE from a ROW expression +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + SET rec1= ROW(10,20); + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a table ROW TYPE variable with a wrong field count +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 (a INT, b VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec2 ROW TYPE OF t2; + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + OPEN cur1; + FETCH cur1 INTO rec2; + CLOSE cur1; +END; +$$ +DELIMITER ;$$ +--error ER_SP_WRONG_NO_OF_FETCH_ARGS +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a table ROW TYPE variable +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2)); +CREATE TABLE t2 LIKE t1; +INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31); +INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32); +INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT 0; + DECLARE rec ROW TYPE OF t1; + DECLARE cur CURSOR FOR SELECT * FROM t1; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; + OPEN cur; + read_loop: LOOP + FETCH cur INTO rec; + IF done THEN + LEAVE read_loop; + END IF; + SELECT rec.a, rec.b, rec.c, rec.d; + INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d); + END LOOP; + CLOSE cur; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SELECT * FROM t2; +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + +--echo # +--echo # Fetching a cursor into a table ROW TYPE variable with different column names +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (x INT, y VARCHAR(10)); +INSERT INTO t1 VALUES (10,'bbb'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec2 ROW TYPE OF t2; + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + OPEN cur1; + FETCH cur1 INTO rec2; + SELECT rec2.x, rec2.y; + CLOSE cur1; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # Fetching a cursor into a table ROW TYPE variable, with truncation +--echo # + +SET sql_mode=''; +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (10,'11x'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec2 ROW TYPE OF t2; + DECLARE cur1 CURSOR FOR SELECT * FROM t1; + OPEN cur1; + FETCH cur1 INTO rec2; + SELECT rec2.a, rec2.b; + CLOSE cur1; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t2; +DROP TABLE t1; +DROP PROCEDURE p1; +SET sql_mode=DEFAULT; + +--echo # +--echo # table ROW TYPE variables are not allowed in LIMIT +--echo # +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,2); +DELIMITER $$; +--error ER_WRONG_SPVAR_TYPE_IN_LIMIT +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT (1,2); + SELECT * FROM t1 LIMIT rec1.a; +END; +$$ +DELIMITER ;$$ +DROP TABLE t1; + + +--echo # +--echo # table ROW TYPE variable fields as OUT parameters +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(OUT a INT,OUT b VARCHAR(10)) +BEGIN + SET a=10; + SET b='bb'; +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + CALL p1(rec1.a, rec1.b); + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Passing the entire table ROW TYPE variable +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10))) +BEGIN + SELECT a.a, a.b; +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'bb'); + CALL p1(rec1); +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Passing the entire table ROW TYPE variable as an OUT parameter +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1(OUT a ROW(a INT, b VARCHAR(10))) +BEGIN + SET a= ROW(10,'bb'); +END; +$$ +CREATE PROCEDURE p2() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + CALL p1(rec1); + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +CALL p2(); +DROP PROCEDURE p2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Assigning a table ROW TYPE field to an OUT parameter +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1 (INOUT res INTEGER) +BEGIN + DECLARE rec1 ROW TYPE OF t1 DEFAULT ROW(10,'b0'); + SET res=rec1.a; +END; +$$ +DELIMITER ;$$ +CALL p1(@res); +SELECT @res; +SET @res=NULL; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing Item_splocal_row_field_by_name::print +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); + EXPLAIN EXTENDED SELECT rec.a, rec.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +--echo # +--echo # Non-existing field +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF t1; + SELECT rec.c; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +ALTER TABLE t1 ADD c INT; +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing that field names are case insensitive +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); + SELECT rec.A, rec.B; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Testing that table ROW TYPE uses temporary tables vs shadowed real tables +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10)); +CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec ROW TYPE OF t1 DEFAULT ROW(10,'bb'); + SELECT rec.A, rec.B; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +DROP TEMPORARY TABLE t1; +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +--echo # +--echo # Testing that the structure of table ROW TYPE variables is determined at the very beginning and is not changed after ALTER +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + ALTER TABLE t1 ADD c INT; + BEGIN + DECLARE rec ROW TYPE OF t1; -- this will not have column "c" + SET rec.c=10; + END; +END; +$$ +DELIMITER ;$$ +--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # SELECT INTO + table ROW TYPE variable with a wrong column count +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + SELECT 10,'a','b' FROM t1 INTO rec1; + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +--error ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + +--echo # +--echo # SELECT INTO + multiple table ROW TYPE variables +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + SELECT 10,'a' FROM t1 INTO rec1, rec1; + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # SELECT INTO + table ROW TYPE working example +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(32)); +INSERT INTO t1 VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE rec1 ROW TYPE OF t1; + SELECT * FROM t1 INTO rec1; + SELECT rec1.a, rec1.b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + + +--echo # +--echo # MDEV-12347 Valgrind reports invalid read errors in Item_field_row::element_index_by_name +--echo # + +# An additional test for MDEV-12347, to make sure that +# Column_definition::interval creates a permanent copy of TYPELIB on +# the memory root when processing %ROWTYPE for a table with ENUM/SET column, +# rather than reuses the TYPELIB from table->field[i], which is freed in the +# end of sp_rcontext::resolve_table_rowtype_ref(). + +CREATE TABLE t1 (a INT, b ENUM('b0','b1','b12','b3')); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + BEGIN + DECLARE rec ROW TYPE OF t1; + SET rec.b='b0'; + SELECT rec.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; + + +CREATE TABLE t1 (a INT, b SET('b0','b1','b12','b3')); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + BEGIN + DECLARE rec ROW TYPE OF t1; + SET rec.b='b0'; + SELECT rec.b; + END; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP TABLE t1; +DROP PROCEDURE p1; diff --git a/mysql-test/t/sp-anchor-type.test b/mysql-test/t/sp-anchor-type.test new file mode 100644 index 0000000000000..f57342eb67ccf --- /dev/null +++ b/mysql-test/t/sp-anchor-type.test @@ -0,0 +1,677 @@ + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + +--echo # +--echo # Missing table +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1(); +DROP PROCEDURE p1; + + +--echo # +--echo # Missing column +--echo # + +CREATE TABLE t1 (b INT); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a; +END; +$$ +DELIMITER ;$$ +--error ER_BAD_FIELD_ERROR +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # One TYPE OF variable +--echo # + +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a; + SET a= 123; + SELECT a; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +--echo # +--echo # Two TYPE OF variables, with a truncation warning on assignment +--echo # + +SET sql_mode=''; +CREATE TABLE t1 (a TINYINT, b INT); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a; + DECLARE b TYPE OF t1.b; + SET a= 200; + SET b= 200; + SELECT a, b; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; +SET sql_mode=DEFAULT; + + +--echo # +--echo # TYPE OF variables for fields with various attributes +--echo # + +CREATE TABLE t1 ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + a TINYINT NOT NULL, + b INT NOT NULL, + ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE(a) +); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE id TYPE OF t1.id; + DECLARE a TYPE OF t1.a; + DECLARE b TYPE OF t1.b; + DECLARE ts TYPE OF t1.ts; + SELECT id, a, b, ts; + CREATE TABLE t2 AS SELECT id, a, b, ts; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # TYPE OF + virtual columns +--echo # + +CREATE TABLE t1 ( + a INT NOT NULL, + b VARCHAR(32), + c INT AS (a + 10) VIRTUAL, + d VARCHAR(5) AS (left(b,5)) PERSISTENT +); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE c TYPE OF t1.c; + DECLARE d TYPE OF t1.d; + SELECT c, d; + CREATE TABLE t2 AS SELECT c, d; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # TYPE OF + the ZEROFILL attribute +--echo # + +CREATE TABLE t1 ( + dz DECIMAL(10,3) ZEROFILL +); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE dzr TYPE OF t1.dz DEFAULT 10; + DECLARE dzt DECIMAL(10,3) ZEROFILL DEFAULT 10; + SELECT dzr, dzt; + CREATE TABLE t2 AS SELECT dzr,dzt; + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Temporary tables shadow real tables for TYPE OF purposes +--echo # +CREATE TABLE t1 (a VARCHAR(10)); +INSERT INTO t1 VALUES ('t1'); +CREATE TEMPORARY TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a DEFAULT 11; + CREATE TABLE t2 AS SELECT a; +END; +$$ +DELIMITER ;$$ +--echo # +--echo # Should use INT(11) as TYPE OF, as in the temporary table +--echo # +CALL p1(); +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; +SELECT * FROM t1; +DROP TEMPORARY TABLE t1; +SELECT * FROM t1; +--echo # +--echo # Should use VARCHAR(10) as TYPE OF, as in the real table +--echo # +CALL p1(); +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # "TYPE OF t1.a" searches for "t1" in the current database +--echo # + +CREATE TABLE t1 (a VARCHAR(10)); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (a INT); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a DEFAULT 11; + CREATE TABLE test.t2 AS SELECT a; +END; +$$ +DELIMITER ;$$ + +--echo # +--echo # This interprets "TYPE OF t1.a" as VARCHAR(10), as in test.t1.a +--echo # + +USE test; +CALL test.p1(); +SHOW CREATE TABLE test.t2; +DROP TABLE test.t2; + +--echo # +--echo # This interprets "TYPE OF t1.a" as INT, as in test1.t1.a +--echo # + +USE test1; +CALL test.p1(); +SHOW CREATE TABLE test.t2; +DROP TABLE test.t2; + +--echo # +--echo # Error if there is no an active database +--echo # + +DROP DATABASE test1; +--error ER_NO_DB_ERROR +CALL test.p1(); + +USE test; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # A reference to a table in a non-existing database +--echo # +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF test1.t1.a; + CREATE TABLE t1 AS SELECT a; +END; +$$ +DELIMITER ;$$ +--error ER_NO_SUCH_TABLE +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # A reference to a table in a different database +--echo # +CREATE TABLE t1(a INT); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (a VARCHAR(10)); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF t1.a; + DECLARE b TYPE OF test1.t1.a; + CREATE TABLE t2 AS SELECT a,b; +END; +$$ +DELIMITER ;$$ +CALL p1; +SHOW CREATE TABLE t2; +DROP PROCEDURE p1; +DROP TABLE t2; +DROP DATABASE test1; +DROP TABLE t1; + + +--echo # +--echo # Using a table before it appears in a TYPE OF declaration + multiple TYPE OF declarations +--echo # +CREATE TABLE t1 (a INT, b VARCHAR(10)); +INSERT INTO t1 (a,b) VALUES (10,'b10'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 (a,b) VALUES (11, 'b11'); + SELECT * FROM t1; + BEGIN + DECLARE va TYPE OF t1.a DEFAULT 30; + DECLARE vb TYPE OF t1.b DEFAULT 'b30'; + INSERT INTO t1 (a,b) VALUES (12,'b12'); + SELECT * FROM t1; + INSERT INTO t1 (a,b) VALUES (va, vb); + SELECT * FROM t1; + END; + BEGIN + DECLARE va TYPE OF t1.a DEFAULT 40; + DECLARE vb TYPE OF t1.b DEFAULT 'b40'; + INSERT INTO t1 (a,b) VALUES (va,vb); + SELECT * FROM t1; + END; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # TYPE OF variables + TABLE vs VIEW +--echo # + +CREATE TABLE t1 ( + bit6 BIT(6), + bit7 BIT(7), + bit8 BIT(8), + i1 TINYINT, + i2 SMALLINT, + i3 MEDIUMINT, + i4 INT, + i8 BIGINT, + ff FLOAT, + fd DOUBLE, + cc CHAR(10), + cv VARCHAR(10), + cvu VARCHAR(10) CHARACTER SET utf8, + t1 TINYTEXT, + t2 TEXT, + t3 MEDIUMTEXT, + t4 LONGTEXT, + enum1 ENUM('a','b','c'), + set1 SET('a','b','c'), + blob1 TINYBLOB, + blob2 BLOB, + blob3 MEDIUMBLOB, + blob4 LONGBLOB, + yy YEAR, + dd DATE, + tm0 TIME, + tm3 TIME(3), + tm6 TIME(6), + dt0 DATETIME, + dt3 DATETIME(3), + dt6 DATETIME(6), + ts0 TIMESTAMP, + ts3 TIMESTAMP(3), + ts6 TIMESTAMP(6), + dc100 DECIMAL(10,0), + dc103 DECIMAL(10,3), + dc209 DECIMAL(20,9) +); + + +DELIMITER $$; +CREATE PROCEDURE p1(command enum('create','select')) +BEGIN + DECLARE bit6 TYPE OF t1.bit6 DEFAULT 0x30; + DECLARE bit7 TYPE OF t1.bit7 DEFAULT 0x41; + DECLARE bit8 TYPE OF t1.bit8 DEFAULT 0x7E; + DECLARE i1 TYPE OF t1.i1 DEFAULT 11; + DECLARE i2 TYPE OF t1.i2 DEFAULT 12; + DECLARE i3 TYPE OF t1.i3 DEFAULT 13; + DECLARE i4 TYPE OF t1.i4 DEFAULT 14; + DECLARE i8 TYPE OF t1.i8 DEFAULT 18; + DECLARE ff TYPE OF t1.ff DEFAULT 21; + DECLARE fd TYPE OF t1.fd DEFAULT 22; + DECLARE cc TYPE OF t1.cc DEFAULT 'char'; + DECLARE cv TYPE OF t1.cv DEFAULT 'varchar'; + DECLARE cvu TYPE OF t1.cvu DEFAULT 'varcharu8'; + DECLARE t1 TYPE OF t1.t1 DEFAULT 'text1'; + DECLARE t2 TYPE OF t1.t2 DEFAULT 'text2'; + DECLARE t3 TYPE OF t1.t3 DEFAULT 'text3'; + DECLARE t4 TYPE OF t1.t4 DEFAULT 'text4'; + DECLARE enum1 TYPE OF t1.enum1 DEFAULT 'b'; + DECLARE set1 TYPE OF t1.set1 DEFAULT 'a,c'; + DECLARE blob1 TYPE OF t1.blob1 DEFAULT 'blob1'; + DECLARE blob2 TYPE OF t1.blob2 DEFAULT 'blob2'; + DECLARE blob3 TYPE OF t1.blob3 DEFAULT 'blob3'; + DECLARE blob4 TYPE OF t1.blob4 DEFAULT 'blob4'; + DECLARE yy TYPE OF t1.yy DEFAULT 2001; + DECLARE dd TYPE OF t1.dd DEFAULT '2001-01-01'; + DECLARE tm0 TYPE OF t1.tm0 DEFAULT '00:00:01'; + DECLARE tm3 TYPE OF t1.tm3 DEFAULT '00:00:03.333'; + DECLARE tm6 TYPE OF t1.tm6 DEFAULT '00:00:06.666666'; + DECLARE dt0 TYPE OF t1.dt0 DEFAULT '2001-01-01 00:00:01'; + DECLARE dt3 TYPE OF t1.dt3 DEFAULT '2001-01-03 00:00:01.333'; + DECLARE dt6 TYPE OF t1.dt6 DEFAULT '2001-01-06 00:00:01.666666'; + DECLARE ts0 TYPE OF t1.ts0 DEFAULT '2002-01-01 00:00:01'; + DECLARE ts3 TYPE OF t1.ts3 DEFAULT '2002-01-03 00:00:01.333'; + DECLARE ts6 TYPE OF t1.ts6 DEFAULT '2002-01-06 00:00:01.666666'; + DECLARE dc100 TYPE OF t1.dc100 DEFAULT 10; + DECLARE dc103 TYPE OF t1.dc103 DEFAULT 10.123; + DECLARE dc209 TYPE OF t1.dc209 DEFAULT 10.123456789; + CASE + WHEN command='create' THEN + CREATE TABLE t2 AS SELECT + bit6, bit7, bit8, + i1,i2,i3,i4,i8, + ff,fd, dc100, dc103, dc209, + cc,cv,cvu, + t1,t2,t3,t4, + enum1, set1, + blob1, blob2, blob3, blob4, + dd, yy, + tm0, tm3, tm6, + dt0, dt3, dt6, + ts0, ts3, ts6; + WHEN command='select' THEN + SELECT + bit6, bit7, bit8, + i1,i2,i3,i4,i8, + ff,fd, dc100, dc103, dc209, + cc,cv,cvu, + t1,t2,t3,t4, + enum1, set1, + blob1, blob2, blob3, blob4, + dd, yy, + tm0, tm3, tm6, + dt0, dt3, dt6, + ts0, ts3, ts6; + END CASE; +END; +$$ +DELIMITER ;$$ + +--echo # +--echo # TABLE +--echo # +CALL p1('create'); +SHOW CREATE TABLE t2; +--vertical_results +SELECT * FROM t2; +--horizontal_results +DROP TABLE t2; + +--disable_ps_protocol +--enable_metadata +--vertical_results +CALL p1('select'); +--horizontal_results +--disable_metadata +--enable_ps_protocol + +--echo # +--echo # VIEW +--echo # +ALTER TABLE t1 RENAME t0; +CREATE VIEW t1 AS SELECT * FROM t0; + +CALL p1('create'); +SHOW CREATE TABLE t2; +--vertical_results +SELECT * FROM t2; +--horizontal_results +DROP TABLE t2; + +--disable_ps_protocol +--enable_metadata +--vertical_results +CALL p1('select'); +--horizontal_results +--disable_metadata +--enable_ps_protocol + +DROP VIEW t1; +DROP TABLE t0; + +DROP PROCEDURE p1; + +--echo # +--echo # VIEW with subqueries +--echo # +CREATE TABLE t1 (a INT,b INT); +INSERT INTO t1 VALUES (10,1),(20,2),(30,3),(40,4); +SELECT AVG(a) FROM t1; +CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1); +SELECT * FROM v1; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF v1.a DEFAULT 10; + DECLARE b TYPE OF v1.b DEFAULT 1; + SELECT a,b; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; +DELIMITER $$; +CREATE FUNCTION f1() RETURNS INT +BEGIN + DECLARE a TYPE OF v1.a DEFAULT 10; + DECLARE b TYPE OF v1.b DEFAULT 1; + RETURN a+b; +END; +$$ +DELIMITER ;$$ +SELECT f1(); +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE t1; + + +--echo # +--echo # TYPE OF variables + INFORMATION_SCHEMA +--echo # +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE tables_table_name TYPE OF INFORMATION_SCHEMA.TABLES.TABLE_NAME; + DECLARE tables_table_rows TYPE OF INFORMATION_SCHEMA.TABLES.TABLE_ROWS; + DECLARE processlist_info TYPE OF INFORMATION_SCHEMA.PROCESSLIST.INFO; + DECLARE processlist_info_binary TYPE OF INFORMATION_SCHEMA.PROCESSLIST.INFO_BINARY; + CREATE TABLE t1 AS SELECT + tables_table_name, + tables_table_rows, + processlist_info, + processlist_info_binary; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # TYPE OF + Table structure change +--echo # Data type for both a0 and a1 is chosen in the very beginning +--echo # +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a0 TYPE OF t1.a; + ALTER TABLE t1 MODIFY a VARCHAR(10); -- This does not affect a1 + BEGIN + DECLARE a1 TYPE OF t1.a; + CREATE TABLE t2 AS SELECT a0, a1; + SHOW CREATE TABLE t2; + DROP TABLE t2; + END; +END +$$ +DELIMITER ;$$ +CREATE TABLE t1 (a INT); +CALL p1; +DROP TABLE t1; +DROP PROCEDURE p1; + + +--echo # +--echo # TYPE OF in parameters +--echo # +CREATE TABLE t1 (a VARCHAR(10)); +CREATE DATABASE test1; +CREATE TABLE test1.t1 (b SMALLINT); +DELIMITER $$; +CREATE PROCEDURE p1(a TYPE OF t1.a, b TYPE OF test1.t1.b) +BEGIN + CREATE TABLE t2 AS SELECT a, b; +END; +$$ +DELIMITER ;$$ +CALL p1('test', 123); +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE test1.t1; +DROP DATABASE test1; +DROP TABLE t1; + +--echo # +--echo # TYPE OF in a stored function variables and arguments +--echo # + +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE FUNCTION f1 (prm TYPE OF t1.a) RETURNS INT +BEGIN + DECLARE a TYPE OF t1.a DEFAULT prm; + RETURN a; +END; +$$ +DELIMITER ;$$ +SELECT f1(20); +DROP FUNCTION f1; +DROP TABLE t1; + + +--echo # +--echo # TYPE OF in function RETURN clause is not supported yet +--echo # +DELIMITER $$; +--error ER_PARSE_ERROR +CREATE FUNCTION f1() RETURN TYPE OF t1.a +BEGIN + RETURN 0; +END; +$$ +DELIMITER ;$$ + + +--echo # +--echo # Using TYPE OF variables as FETCH targets +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3)); +INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123'); +INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123'); +CREATE TABLE t2 LIKE t1; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE done INT DEFAULT 0; + DECLARE v_a TYPE OF t1.a; + DECLARE v_b TYPE OF t1.b; + DECLARE v_c TYPE OF t1.c; + DECLARE c CURSOR FOR SELECT a,b,c FROM t1; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; + OPEN c; + read_loop: LOOP + FETCH c INTO v_a, v_b, v_c; + IF done THEN + LEAVE read_loop; + END IF; + INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c); + END LOOP; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +SELECT * FROM t2; +DROP TABLE t2; +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Using TYPE OF variables as SELECT INTO targets +--echo # + +CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3)); +INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE v_a TYPE OF t1.a; + DECLARE v_b TYPE OF t1.b; + DECLARE v_c TYPE OF t1.c; + SELECT * FROM t1 INTO v_a, v_b, v_c; + SELECT v_a, v_b, v_c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +--echo # +--echo # End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # diff --git a/mysql-test/t/sp-security-anchor-type.test b/mysql-test/t/sp-security-anchor-type.test new file mode 100644 index 0000000000000..e61d5e3805269 --- /dev/null +++ b/mysql-test/t/sp-security-anchor-type.test @@ -0,0 +1,328 @@ +--source include/not_embedded.inc + +--echo # +--echo # MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + + +--echo # +--echo # Initiation: +--echo # - creating database db1 +--echo # - creating user user1 with access rights to db1 +--echo # + +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT, b VARCHAR(10)); + +CREATE USER user1; + +GRANT ALL PRIVILEGES ON test.* TO user1; + +connect (conn1,localhost,user1,,test); + +SELECT database(); +SELECT user(); + +--echo # +--echo # Making sure that user1 does not have privileges to db1.t1 +--echo # + +--error ER_TABLEACCESS_DENIED_ERROR +SHOW CREATE TABLE db1.t1; +--error ER_TABLEACCESS_DENIED_ERROR +SHOW FIELDS IN db1.t1; + + +--echo # +--echo # Trigger: using TYPE OF with a table we don't have access to +--echo # +CREATE TABLE test.t1 (a INT, b INT); +INSERT INTO test.t1 (a,b) VALUES (10,20); +SELECT * FROM t1; +DELIMITER $$; +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN + DECLARE b TYPE OF db1.t1.b DEFAULT 20; + SET NEW.b = 10; +END +$$ +DELIMITER ;$$ +--error ER_TABLEACCESS_DENIED_ERROR +INSERT INTO t1 (a) VALUES (10); +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + + +--echo # +--echo # Stored procedure: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER user1, SQL SECURITY DEFAULT +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 10; + SELECT a; +END; +$$ +DELIMITER ;$$ +--error ER_TABLEACCESS_DENIED_ERROR +CALL p1; +DROP PROCEDURE p1; + +#DELIMITER $$; +#CREATE PROCEDURE p1() +#BEGIN +# DECLARE a ROW TYPE OF db1.t1; +# SELECT a.a; +#END; +#$$ +#DELIMITER ;$$ +#--error ER_TABLEACCESS_DENIED_ERROR +#CALL p1; +#DROP PROCEDURE p1; + + +--echo # +--echo # Stored procedure: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER root, SQL SECURITY INVOKER +--echo # + +connection default; +DELIMITER $$; +CREATE PROCEDURE p1() +SQL SECURITY INVOKER +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 10; + SELECT a; +END; +$$ +DELIMITER ;$$ +connection conn1; +--error ER_TABLEACCESS_DENIED_ERROR +CALL p1; +DROP PROCEDURE p1; + + +connection default; +DELIMITER $$; +CREATE PROCEDURE p1() +SQL SECURITY INVOKER +BEGIN + DECLARE a ROW TYPE OF db1.t1; + SELECT a.a; +END; +$$ +DELIMITER ;$$ +connection conn1; +--error ER_TABLEACCESS_DENIED_ERROR +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # Stored procedure: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER root, SQL SECURITY DEFINER +--echo # + +connection default; +DELIMITER $$; +CREATE PROCEDURE p1() +SQL SECURITY DEFINER +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 10; + SELECT a; +END; +$$ +DELIMITER ;$$ +connection conn1; +CALL p1; +DROP PROCEDURE p1; + +connection default; +DELIMITER $$; +CREATE PROCEDURE p1() +SQL SECURITY DEFINER +BEGIN + DECLARE a ROW TYPE OF db1.t1; + SET a.a= 10; + SELECT a.a; +END; +$$ +DELIMITER ;$$ +connection conn1; +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # Stored function: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER user1, SQL SECURITY DEFAULT +--echo # + +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE FUNCTION f1() RETURNS INT +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 0; + RETURN OCTET_LENGTH(a); +END; +$$ +DELIMITER ;$$ +--error ER_TABLEACCESS_DENIED_ERROR +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE t1; + + +--echo # +--echo # Stored function: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER root, SQL SECURITY INVOKER +--echo # + +connection default; +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE FUNCTION f1() RETURNS INT +SQL SECURITY INVOKER +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 0; + RETURN OCTET_LENGTH(a); +END; +$$ +DELIMITER ;$$ +connection conn1; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE t1; + + +--echo # +--echo # Stored function: Using TYPE OF for with a table that we don't have access to +--echo # DEFINER root, SQL SECURITY DEFINER +--echo # + +connection default; +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE FUNCTION f1() RETURNS INT +SQL SECURITY DEFINER +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 0; + RETURN OCTET_LENGTH(a); +END; +$$ +DELIMITER ;$$ +connection conn1; +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE t1; + + +connection default; +GRANT SELECT (a) ON db1.t1 TO user1; +connection conn1; + +--echo # +--echo # Making sure that user1 has access to db1.t1.a, but not to db1.t1.b +--echo # + +--error ER_TABLEACCESS_DENIED_ERROR +SHOW CREATE TABLE db1.t1; +SHOW FIELDS IN db1.t1; + +--echo # +--echo # Trigger: Per-column privileges +--echo # +CREATE TABLE test.t1 (a INT, b INT); +INSERT INTO test.t1 (a,b) VALUES (10,20); +SELECT * FROM t1; +# TYPE OF reference using a column we have access to +DELIMITER $$; +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 20; + BEGIN + SET NEW.b := 10; + END; +END +$$ +DELIMITER ;$$ +INSERT INTO t1 (a) VALUES (10); +SELECT * FROM t1; +DROP TRIGGER tr1; +# TYPE OF reference using a column that we don't have access to +DELIMITER $$; +CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW +BEGIN + DECLARE b TYPE OF db1.t1.b DEFAULT 20; + BEGIN + SET NEW.b = 10; + END; +END +$$ +DELIMITER ;$$ +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO t1 (a) VALUES (10); +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + + + +--echo # +--echo # Stored procedure: Per-column privileges +--echo # DEFINER user1, SQL SECURITY DEFAULT +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a TYPE OF db1.t1.a DEFAULT 10; + SELECT a; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE b TYPE OF db1.t1.b DEFAULT 10; + SELECT b; +END; +$$ +DELIMITER ;$$ +--error ER_COLUMNACCESS_DENIED_ERROR +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE b ROW TYPE OF db1.t1; + SET b.b=10; + SELECT b.b; +END; +$$ +DELIMITER ;$$ +--error ER_COLUMNACCESS_DENIED_ERROR +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # Clean up +--echo # +disconnect conn1; +connection default; + +DROP USER user1; +DROP DATABASE db1; + +--echo # +--echo # End of MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 83b6966c67630..d6eca47c0d89c 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2676,3 +2676,26 @@ select trigger_name, action_order, created from information_schema.triggers where event_object_table = 't1' and trigger_schema='test'; drop table t1; set time_zone= @@global.time_zone; + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # +--echo # MDEV-12461 TYPE OF and ROW TYPE OF anchored data types +--echo # + +CREATE TABLE t1 (a INT, b INT, total INT); +DELIMITER $$; +CREATE TRIGGER tr1 BEFORE INSERT ON t1 +FOR EACH ROW +BEGIN + DECLARE va TYPE OF t1.a DEFAULT NEW.a; + DECLARE vb TYPE OF t1.b DEFAULT NEW.b; + SET NEW.total:= va + vb; +END; +$$ +DELIMITER ;$$ +INSERT INTO t1 (a,b) VALUES (10, 20); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/lex.h b/sql/lex.h index 2a30f7f31adef..9918683e60b70 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -423,6 +423,7 @@ static SYMBOL symbols[] = { { "NUMBER", SYM(NUMBER_SYM)}, { "NUMERIC", SYM(NUMERIC_SYM)}, { "NVARCHAR", SYM(NVARCHAR_SYM)}, + { "OF", SYM(OF_SYM)}, { "OFFSET", SYM(OFFSET_SYM)}, { "OLD_PASSWORD", SYM(OLD_PASSWORD_SYM)}, { "ON", SYM(ON)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e93c9069b4ef6..869323d03cc1a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -799,6 +799,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) String *string; TABLE_LIST *table_list; Table_ident *table; + Qualified_column_ident *qualified_column_ident; char *simple_string; const char *const_simple_string; chooser_compare_func_creator boolfunc2creator; @@ -1274,6 +1275,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NUMERIC_SYM /* SQL-2003-R */ %token NTH_VALUE_SYM /* SQL-2011 */ %token NVARCHAR_SYM +%token OF_SYM /* SQL-1992-R, Oracle-R */ %token OFFSET_SYM %token OLD_PASSWORD_SYM %token ON /* SQL-2003-R */ @@ -1592,6 +1594,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty opt_constraint constraint opt_ident + sp_decl_ident sp_block_label %type @@ -1608,6 +1611,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_ident table_ident_nodb references xid table_ident_opt_wild create_like +%type + qualified_column_ident + optionally_qualified_column_ident + %type remember_name remember_end opt_db remember_tok_start wild_and_where @@ -1883,9 +1890,10 @@ END_OF_INPUT %type case_stmt_specification %type loop_body while_body repeat_body -%type sp_decl_idents sp_handler_type sp_hcond_list +%type sp_decl_idents sp_decl_idents_init_vars +%type sp_handler_type sp_hcond_list %type sp_cond sp_hcond sqlstate signal_value opt_signal_value -%type sp_decls sp_decl sp_decl_body +%type sp_decls sp_decl sp_decl_body sp_decl_variable_list %type sp_name %type sp_param_name sp_param_name_and_type %type sp_opt_inout @@ -1909,7 +1917,7 @@ END_OF_INPUT %type condition_information; %type row_field_name row_field_definition -%type row_field_definition_list field_type_row +%type row_field_definition_list row_type_body %type opt_window_clause window_def_list window_def window_spec %type window_name @@ -2864,13 +2872,17 @@ sp_param_name_and_type: if (Lex->sp_param_fill_definition($$= $1)) MYSQL_YYABORT; } - | sp_param_name field_type_row + | sp_param_name TYPE_SYM OF_SYM qualified_column_ident + { + Lex->sphead->fill_spvar_using_type_reference($$= $1, $4); + } + | sp_param_name ROW_SYM row_type_body { $$= $1; $$->field_def.field_name= $$->name.str; Lex->sphead->fill_spvar_definition(thd, &$$->field_def); - Lex->sphead->row_fill_field_definitions(thd, $2); - $$->field_def.set_row_field_definitions($2); + Lex->sphead->row_fill_field_definitions(thd, $3); + $$->field_def.set_row_field_definitions($3); } ; @@ -2950,6 +2962,40 @@ sp_decl: DECLARE_SYM sp_decl_body { $$= $2; } ; + +qualified_column_ident: + sp_decl_ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident($1, $3))) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(thd, + $1, $3, $5))) + MYSQL_YYABORT; + } + ; + +optionally_qualified_column_ident: + sp_decl_ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident($1))) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident($1, $3))) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(thd, + $1, $3, $5))) + MYSQL_YYABORT; + } + ; + row_field_name: ident { @@ -2983,36 +3029,56 @@ row_field_definition_list: } ; -field_type_row: - ROW_SYM '(' row_field_definition_list ')' { $$= $3; } +row_type_body: + '(' row_field_definition_list ')' { $$= $2; } ; - -sp_decl_body: +sp_decl_idents_init_vars: sp_decl_idents { Lex->sp_variable_declarations_init(thd, $1); } + ; + +sp_decl_variable_list: + sp_decl_idents_init_vars type_with_opt_collate sp_opt_default { if (Lex->sp_variable_declarations_finalize(thd, $1, - &Lex->last_field[0], $4)) + &Lex->last_field[0], $3)) MYSQL_YYABORT; $$.init_using_vars($1); } - | sp_decl_idents + | sp_decl_idents_init_vars + TYPE_SYM OF_SYM optionally_qualified_column_ident + sp_opt_default { - Lex->sp_variable_declarations_init(thd, $1); + if (Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $4, $5)) + MYSQL_YYABORT; + $$.init_using_vars($1); } - field_type_row + | sp_decl_idents_init_vars + ROW_SYM TYPE_SYM OF_SYM optionally_qualified_column_ident + sp_opt_default + { + if (Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $5, $6)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + ROW_SYM row_type_body sp_opt_default { if (Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4)) MYSQL_YYABORT; $$.init_using_vars($1); } - | ident CONDITION_SYM FOR_SYM sp_cond + ; + +sp_decl_body: + sp_decl_variable_list + | sp_decl_ident CONDITION_SYM FOR_SYM sp_cond { if (Lex->spcont->declare_condition(thd, $1, $4)) MYSQL_YYABORT; @@ -3031,7 +3097,7 @@ sp_decl_body: $$.vars= $$.conds= $$.curs= 0; $$.hndlrs= 1; } - | ident CURSOR_SYM + | sp_decl_ident CURSOR_SYM { Lex->sp_block_init(thd); } @@ -3448,8 +3514,12 @@ condition_information_item_name: { $$= Condition_information_item::RETURNED_SQLSTATE; } ; +sp_decl_ident: + ident { $$= $1; } + ; + sp_decl_idents: - ident + sp_decl_ident { /* NOTE: field definition is filled in sp_decl section. */ @@ -14445,6 +14515,7 @@ keyword_sp: | NOTFOUND_SYM {} | NUMBER_SYM {} | NVARCHAR_SYM {} + | OF_SYM {} /* SQL-1999-R, Oracle-R */ | OFFSET_SYM {} | OLD_PASSWORD_SYM {} | ONE_SYM {} diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 42fc7e90fc24f..791e7a386c9ae 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -690,6 +690,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NUMERIC_SYM /* SQL-2003-R */ %token NTH_VALUE_SYM /* SQL-2011 */ %token NVARCHAR_SYM +%token OF_SYM /* SQL-1992-R, Oracle-R */ %token OFFSET_SYM %token OLD_PASSWORD_SYM %token ON /* SQL-2003-R */ @@ -14769,6 +14770,7 @@ keyword_sp_not_data_type: | NODEGROUP_SYM {} | NONE_SYM {} | NOTFOUND_SYM {} + | OF_SYM {} /* SQL-1999-R, Oracle-R */ | OFFSET_SYM {} | OLD_PASSWORD_SYM {} | ONE_SYM {}