summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-03-08 23:20:39 +0400
committerAlexander Barkov <bar@mariadb.org>2017-04-05 15:02:59 +0400
commitf429b5a834439e4f0c76e893487e33027d76b74b (patch)
treea06ec92dad05a2820ecd8a9afe73696b103536a3
parent1b8a0c879d80733e3c684080b8c7719c35642e0d (diff)
downloadmariadb-git-f429b5a834439e4f0c76e893487e33027d76b74b.tar.gz
MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
Implementing cursor%ROWTYPE variables, according to the task description. This patch includes a refactoring in how sp_instr_cpush and sp_instr_copen work. This is needed to implement MDEV-10598 later easier, to allow variable declarations go after cursor declarations (which is currently not allowed). Before this patch, sp_instr_cpush worked as a Query_arena associated with the cursor. sp_instr_copen::execute() switched to the sp_instr_cpush's Query_arena when executing the cursor SELECT statement. Now the Query_arena associated with the cursor is stored inside an instance of a new class sp_lex_cursor (a LEX descendand) that contains the cursor SELECT statement. This simplifies the implementation, because: - It's easier to follow the code when everything related to execution of the cursor SELECT statement is stored inside the same sp_lex_cursor object (rather than distributed between LEX and sp_instr_cpush). - It's easier to link an sp_instr_cursor_copy_struct to sp_lex_cursor rather than to sp_instr_cpush. - Also, it allows to perform sp_instr_cursor_copy_struct::exec_core() without having a pointer to sp_instr_cpush, using a pointer to sp_lex_cursor instead. This will be important for MDEV-10598, because sp_instr_cpush will happen *after* sp_instr_cursor_copy_struct. After MDEV-10598 is done, this declaration: DECLARE CURSOR cur IS SELECT * FROM t1; rec cur%ROWTYPE; BEGIN OPEN cur; FETCH cur INTO rec; CLOSE cur; END; will generate about this code: +-----+--------------------------+ | Pos | Instruction | +-----+--------------------------+ | 0 | cursor_copy_struct rec@0 | Points to sp_cursor_lex through m_lex_keeper | 1 | set rec@0 NULL | | 2 | cpush cur@0 | Points to sp_cursor_lex through m_lex_keeper | 3 | copen cur@0 | Points to sp_cursor_lex through m_cursor | 4 | cfetch cur@0 rec@0 | | 5 | cclose cur@0 | | 6 | cpop 1 | +-----+--------------------------+ Notice, "cursor_copy_struct" and "set" will go before "cpush". Instructions at positions 0, 2, 3 point to the same sp_cursor_lex instance.
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-code.result34
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result978
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-code.test25
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test1057
-rw-r--r--sql/field.h25
-rw-r--r--sql/sp_head.cc105
-rw-r--r--sql/sp_head.h65
-rw-r--r--sql/sp_pcontext.cc5
-rw-r--r--sql/sp_pcontext.h10
-rw-r--r--sql/sp_rcontext.cc35
-rw-r--r--sql/sp_rcontext.h10
-rw-r--r--sql/sql_cursor.cc4
-rw-r--r--sql/sql_cursor.h5
-rw-r--r--sql/sql_lex.cc67
-rw-r--r--sql/sql_lex.h9
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_yacc.yy32
-rw-r--r--sql/sql_yacc_ora.yy37
-rw-r--r--sql/table.cc22
-rw-r--r--sql/table.h1
20 files changed, 2445 insertions, 83 deletions
diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result
index 9fc8574cbc8..5f21da6eb16 100644
--- a/mysql-test/suite/compat/oracle/r/sp-code.result
+++ b/mysql-test/suite/compat/oracle/r/sp-code.result
@@ -1007,3 +1007,37 @@ Pos Instruction
5 set rec1.c@0["c"] rec1.d@0["d"]
DROP PROCEDURE p1;
DROP TABLE t1;
+#
+# MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1,rec2 cur1%ROWTYPE;
+rec3 cur2%ROWTYPE;
+BEGIN
+rec1.a:= 10;
+rec1.b:= 'bbb';
+END;
+END;
+$$
+SHOW PROCEDURE CODE p1;
+Pos Instruction
+0 cpush cur1@0
+1 cpush cur2@1
+2 cursor_copy_struct rec1@0
+3 set rec1@0 NULL
+4 cursor_copy_struct rec2@1
+5 set rec2@1 NULL
+6 cursor_copy_struct rec3@2
+7 set rec3@2 NULL
+8 set rec1.a@0["a"] 10
+9 set rec1.b@0["b"] 'bbb'
+10 jump 11
+11 cpop 2
+DROP PROCEDURE p1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result b/mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result
new file mode 100644
index 00000000000..683b02c3b4f
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result
@@ -0,0 +1,978 @@
+SET sql_mode=ORACLE;
+#
+# MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+#
+#
+# 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 AS
+CURSOR c IS SELECT a,b FROM t1;
+BEGIN
+DECLARE
+rec c%ROWTYPE;
+BEGIN
+OPEN c;
+LOOP
+FETCH c INTO rec;
+EXIT WHEN c%NOTFOUND;
+SELECT 'rec=(' || rec.a ||','|| rec.b||')' AS c FROM dual;
+INSERT INTO t2 VALUES (rec.a, rec.b);
+END LOOP;
+CLOSE c;
+END;
+END;
+$$
+CALL p1();
+c
+rec=(10,b10)
+c
+rec=(20,b20)
+c
+rec=(30,b30)
+SELECT * FROM t2;
+a b
+10 b10
+20 b20
+30 b30
+DROP PROCEDURE p1;
+DROP TABLE t2;
+DROP TABLE t1;
+#
+# cursor%ROWTYPE referring to a table in a non-existing database
+#
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM tes2.t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+NULL;
+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%ROWTYPE referring to a table in the current database
+#
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+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
+)
+DROP TABLE t1;
+CALL p1();
+ERROR 42S02: Table 'test.t1' doesn't exist
+DROP PROCEDURE p1;
+#
+# cursor%ROWTYPE referring to a table in an explicitly specified database
+#
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM test.t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+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
+)
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# Cursor%ROWTYPE referring to a view in the current database
+#
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM v1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+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
+)
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# cursor%ROWTYPE referring to a view in an explicitly specified database
+#
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM test.v1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+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
+)
+DROP VIEW v1;
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# Checking that all cursor%ROWTYPE fields are NULL by default
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+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%ROWTYPE variable with a ROW expression as a default
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE := ROW(10,'bbb');
+BEGIN
+SELECT rec1.a, rec1.b;
+END;
+END;
+$$
+CALL p1();
+rec1.a rec1.b
+10 bbb
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# A cursor%ROWTYPE variable with an incompatible ROW expression as a default
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE := ROW(10,'bbb','ccc');
+BEGIN
+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%ROWTYPE variable with a ROW variable as a default
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
+rec2 cur%ROWTYPE := rec1;
+BEGIN
+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%ROWTYPE variable as a default
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE := ROW(10,'bbb');
+rec2 ROW(a INT, b VARCHAR(10)):= rec1;
+BEGIN
+SELECT rec2.a, rec2.b;
+END;
+END;
+$$
+CALL p1();
+rec2.a rec2.b
+10 bbb
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# Assigning cursor%ROWTYPE 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()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+rec2 cur2%ROWTYPE;
+BEGIN
+rec2:=rec1;
+END;
+END;
+$$
+CALL p1();
+ERROR 21000: Operand should contain 2 column(s)
+DROP PROCEDURE p1;
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+rec2 cur2%ROWTYPE;
+BEGIN
+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%ROWTYPE variables (equal number of fields)
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (x INT, y VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+rec2 cur2%ROWTYPE;
+BEGIN
+rec1.a:= 10;
+rec1.b:= 'bbb';
+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%ROWTYPE and explicit ROW variables
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+rec2 ROW(x INT,y INT,z INT);
+BEGIN
+rec2.x:= 10;
+rec2.y:= 20;
+rec2.z:= 30;
+rec1:= rec2;
+END;
+END;
+$$
+CALL p1();
+ERROR 21000: Operand should contain 2 column(s)
+DROP TABLE t1;
+DROP PROCEDURE p1;
+#
+# Assigning between compatible cursor%ROWTYPE and explicit ROW variables
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+rec2 ROW(x INT,y INT);
+BEGIN
+rec2.x:= 10;
+rec2.y:= 20;
+rec1:= rec2;
+SELECT rec1.a, rec1.b;
+rec1.a:= 11;
+rec1.b:= 21;
+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%ROWTYPE from a ROW expression
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+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%ROWTYPE 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()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec2 cur2%ROWTYPE;
+BEGIN
+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%ROWTYPE 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()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+OPEN cur;
+LOOP
+FETCH cur INTO rec;
+EXIT WHEN cur%NOTFOUND;
+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%ROWTYPE variable, cur%ROWTYPE 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()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+OPEN cur;
+LOOP
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+FETCH cur INTO rec;
+EXIT WHEN cur%NOTFOUND;
+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%ROWTYPE 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()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec2 cur2%ROWTYPE;
+BEGIN
+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%ROWTYPE variable, with truncation
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE TABLE t2 (a INT, b INT);
+INSERT INTO t1 VALUES (10,'11x');
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+DECLARE
+rec2 cur2%ROWTYPE;
+BEGIN
+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;
+#
+# cursor%ROWTYPE variables are not allowed in LIMIT
+#
+CREATE TABLE t1 (a INT, b INT);
+INSERT INTO t1 VALUES (1,2);
+CREATE PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE:=(1,2);
+BEGIN
+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%ROWTYPE variable fields as OUT parameters
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
+AS
+BEGIN
+a:=10;
+b:='bb';
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+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%ROWTYPE variable
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+SELECT a.a, a.b;
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur%ROWTYPE:= ROW(10,'bb');
+BEGIN
+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%ROWTYPE variable as an OUT parameter
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+a:= ROW(10,'bb');
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec1 cur%ROWTYPE;
+BEGIN
+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%ROWTYPE field to an OUT parameter
+#
+CREATE PROCEDURE p1 (res IN OUT INTEGER)
+AS
+a INT:=10;
+CURSOR cur1 IS SELECT a FROM DUAL;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+OPEN cur1;
+FETCH cur1 INTO rec1;
+CLOSE cur1;
+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
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur1%ROWTYPE:=ROW(10,'bb');
+BEGIN
+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
+AS
+CURSOR cur1 IS SELECT
+10 AS a,
+CONCAT(_latin1'a' COLLATE latin1_bin,
+_latin1'a' COLLATE latin1_swedish_ci) AS b;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+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 '||'
+DROP PROCEDURE p1;
+#
+# Non-existing field
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+CREATE PROCEDURE p1
+AS
+CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur1%ROWTYPE;
+BEGIN
+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
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE:=ROW(10,'bb');
+BEGIN
+SELECT rec.A, rec.B;
+END;
+END;
+$$
+CALL p1();
+rec.A rec.B
+10 bb
+DROP PROCEDURE p1;
+DROP TABLE t1;
+#
+# Testing that cursor%ROWTYPE 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
+AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE:=ROW(10,'bb');
+BEGIN
+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%ROWTYPE variables is determined at the CURSOR instantiation time
+#
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+CREATE PROCEDURE p1 AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DROP TABLE t1;
+CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+DECLARE
+rec cur%ROWTYPE; -- This has a column "c"
+ BEGIN
+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 AS
+CURSOR cur IS SELECT * FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE; -- This does not have a column "c"
+ BEGIN
+DROP TABLE t1;
+CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+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 %ROWTYPE
+#
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+CREATE PROCEDURE p1 AS
+CURSOR cur IS SELECT * FROM t1, t2;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+SELECT rec.a;
+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 %ROWTYPE
+#
+SET NAMES utf8;
+CREATE TABLE t1 (a VARCHAR(10));
+INSERT INTO t1 VALUES ('a');
+CREATE PROCEDURE p1 AS
+CURSOR cur IS SELECT a, CONCAT(a,'a'), CONCAT(a,'ö') FROM t1;
+BEGIN
+DECLARE
+rec cur%ROWTYPE;
+BEGIN
+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%ROWTYPE 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 AS
+CURSOR cur1 IS SELECT a,b FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE:=ROW(0,'b0');
+CURSOR cur2 IS SELECT rec1.a AS a, rec1.b AS b FROM t1;
+BEGIN
+DECLARE
+rec2 cur2%ROWTYPE;
+BEGIN
+OPEN cur2;
+LOOP
+FETCH cur2 INTO rec2;
+EXIT WHEN cur2%NOTFOUND;
+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
+AS
+CURSOR cur1 IS SELECT CONCAT(a,b) AS c FROM t1;
+BEGIN
+DECLARE
+rec1 cur1%ROWTYPE;
+BEGIN
+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;
diff --git a/mysql-test/suite/compat/oracle/t/sp-code.test b/mysql-test/suite/compat/oracle/t/sp-code.test
index 53ffeb7bc01..bc766e868fd 100644
--- a/mysql-test/suite/compat/oracle/t/sp-code.test
+++ b/mysql-test/suite/compat/oracle/t/sp-code.test
@@ -764,3 +764,28 @@ DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1,rec2 cur1%ROWTYPE;
+ rec3 cur2%ROWTYPE;
+ BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ END;
+END;
+$$
+DELIMITER ;$$
+SHOW PROCEDURE CODE p1;
+DROP PROCEDURE p1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test b/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
new file mode 100644
index 00000000000..b873ba8c11b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
@@ -0,0 +1,1057 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
+--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 AS
+ CURSOR c IS SELECT a,b FROM t1;
+BEGIN
+ DECLARE
+ rec c%ROWTYPE;
+ BEGIN
+ OPEN c;
+ LOOP
+ FETCH c INTO rec;
+ EXIT WHEN c%NOTFOUND;
+ SELECT 'rec=(' || rec.a ||','|| rec.b||')' AS c 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%ROWTYPE referring to a table in a non-existing database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM tes2.t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ NULL;
+ 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%ROWTYPE referring to a table in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE referring to a table in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM test.t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE referring to a view in the current database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM v1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE referring to a view in an explicitly specified database
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM test.v1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE fields are NULL by default
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ SELECT rec1.a, rec1.b, rec1.c, rec1.d;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with a ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb');
+ BEGIN
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with an incompatible ROW expression as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb','ccc');
+ BEGIN
+ SELECT rec1.a, rec1.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A cursor%ROWTYPE variable with a ROW variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
+ rec2 cur%ROWTYPE := rec1;
+ BEGIN
+ SELECT rec2.a, rec2.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # A ROW variable using a cursor%ROWTYPE variable as a default
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE := ROW(10,'bbb');
+ rec2 ROW(a INT, b VARCHAR(10)):= rec1;
+ BEGIN
+ SELECT rec2.a, rec2.b;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning cursor%ROWTYPE 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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ rec2:=rec1;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP PROCEDURE p1;
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE 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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ rec1.a:= 10;
+ rec1.b:= 'bbb';
+ 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%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 ROW(x INT,y INT,z INT);
+ BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec2.z:= 30;
+ rec1:= rec2;
+ END;
+END;
+$$
+DELIMITER ;$$
+--error ER_OPERAND_COLUMNS
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning between compatible cursor%ROWTYPE and explicit ROW variables
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ rec2 ROW(x INT,y INT);
+ BEGIN
+ rec2.x:= 10;
+ rec2.y:= 20;
+ rec1:= rec2;
+ SELECT rec1.a, rec1.b;
+ rec1.a:= 11;
+ rec1.b:= 21;
+ rec2:= rec1;
+ SELECT rec2.x, rec2.y;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP TABLE t1;
+DROP PROCEDURE p1;
+
+
+--echo #
+--echo # Assigning cursor%ROWTYPE from a ROW expression
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE 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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE 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()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ OPEN cur;
+ LOOP
+ FETCH cur INTO rec;
+ EXIT WHEN cur%NOTFOUND;
+ 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%ROWTYPE variable, cur%ROWTYPE 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()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ OPEN cur;
+ LOOP
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ FETCH cur INTO rec;
+ EXIT WHEN cur%NOTFOUND;
+ 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%ROWTYPE 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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE variable, with truncation
+--echo #
+
+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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+ CURSOR cur2 IS SELECT * FROM t2;
+BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ 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;
+
+
+--echo #
+--echo # cursor%ROWTYPE 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()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE:=(1,2);
+ BEGIN
+ SELECT * FROM t1 LIMIT rec1.a;
+ END;
+END;
+$$
+DELIMITER ;$$
+DROP TABLE t1;
+
+
+--echo #
+--echo # cursor%ROWTYPE variable fields as OUT parameters
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
+AS
+BEGIN
+ a:=10;
+ b:='bb';
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE variable
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ SELECT a.a, a.b;
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur%ROWTYPE:= ROW(10,'bb');
+ BEGIN
+ CALL p1(rec1);
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p2();
+DROP PROCEDURE p2;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Passing the entire cursor%ROWTYPE variable as an OUT parameter
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+DELIMITER $$;
+CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
+AS
+BEGIN
+ a:= ROW(10,'bb');
+END;
+$$
+CREATE PROCEDURE p2()
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE field to an OUT parameter
+--echo #
+
+DELIMITER $$;
+CREATE PROCEDURE p1 (res IN OUT INTEGER)
+AS
+ a INT:=10;
+ CURSOR cur1 IS SELECT a FROM DUAL;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ 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
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur1%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ 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
+AS
+ CURSOR cur1 IS SELECT
+ 10 AS a,
+ CONCAT(_latin1'a' COLLATE latin1_bin,
+ _latin1'a' COLLATE latin1_swedish_ci) AS b;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ 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
+AS
+ CURSOR cur1 IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur1%ROWTYPE;
+ BEGIN
+ 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
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ SELECT rec.A, rec.B;
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
+
+
+--echo #
+--echo # Testing that cursor%ROWTYPE 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
+AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE:=ROW(10,'bb');
+ BEGIN
+ 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%ROWTYPE variables is determined at the CURSOR instantiation time
+--echo #
+
+CREATE TABLE t1 (a INT, b VARCHAR(32));
+INSERT INTO t1 VALUES (10,'b10');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DROP TABLE t1;
+ CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+ DECLARE
+ rec cur%ROWTYPE; -- This has a column "c"
+ BEGIN
+ 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 AS
+ CURSOR cur IS SELECT * FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE; -- This does not have a column "c"
+ BEGIN
+ DROP TABLE t1;
+ CREATE TABLE t1 (a INT, b VARCHAR(32), c INT);
+ 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 %ROWTYPE
+--echo #
+
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT * FROM t1, t2;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ SELECT rec.a;
+ 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 %ROWTYPE
+--echo #
+
+SET NAMES utf8;
+CREATE TABLE t1 (a VARCHAR(10));
+INSERT INTO t1 VALUES ('a');
+DELIMITER $$;
+CREATE PROCEDURE p1 AS
+ CURSOR cur IS SELECT a, CONCAT(a,'a'), CONCAT(a,'ö') FROM t1;
+BEGIN
+ DECLARE
+ rec cur%ROWTYPE;
+ BEGIN
+ 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%ROWTYPE 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 AS
+ CURSOR cur1 IS SELECT a,b FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE:=ROW(0,'b0');
+ CURSOR cur2 IS SELECT rec1.a AS a, rec1.b AS b FROM t1;
+ BEGIN
+ DECLARE
+ rec2 cur2%ROWTYPE;
+ BEGIN
+ OPEN cur2;
+ LOOP
+ FETCH cur2 INTO rec2;
+ EXIT WHEN cur2%NOTFOUND;
+ 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
+AS
+ CURSOR cur1 IS SELECT CONCAT(a,b) AS c FROM t1;
+BEGIN
+ DECLARE
+ rec1 cur1%ROWTYPE;
+ BEGIN
+ OPEN cur1;
+ FETCH cur1 INTO rec1;
+ CLOSE cur1;
+ SELECT HEX(rec1.c);
+ END;
+END;
+$$
+DELIMITER ;$$
+CALL p1();
+DROP PROCEDURE p1;
+DROP TABLE t1;
diff --git a/sql/field.h b/sql/field.h
index e110e1f8146..3b96dd546cb 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -3987,6 +3987,16 @@ public:
};
+class Cursor_rowtype: public Sql_alloc
+{
+public:
+ uint m_cursor;
+ Cursor_rowtype(uint cursor)
+ :m_cursor(cursor)
+ { }
+};
+
+
/**
This class is used during a stored routine or a trigger execution,
at sp_rcontext::create() time.
@@ -4010,20 +4020,25 @@ class Spvar_definition: public Column_definition
{
class Qualified_column_ident *m_column_type_ref; // for %TYPE
class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE
+ class Cursor_rowtype *m_cursor_rowtype_ref; // for cursor%ROWTYPE
Row_definition_list *m_row_field_definitions; // for ROW
public:
Spvar_definition()
:m_column_type_ref(NULL),
m_table_rowtype_ref(NULL),
- m_row_field_definitions(NULL) { }
+ m_cursor_rowtype_ref(NULL),
+ m_row_field_definitions(NULL)
+ { }
Spvar_definition(THD *thd, Field *field)
:Column_definition(thd, field, NULL),
m_column_type_ref(NULL),
m_table_rowtype_ref(NULL),
+ m_cursor_rowtype_ref(NULL),
m_row_field_definitions(NULL)
{ }
bool is_column_type_ref() const { return m_column_type_ref != 0; }
bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; }
+ bool is_cursor_rowtype_ref() const { return m_cursor_rowtype_ref != NULL; }
class Qualified_column_ident *column_type_ref() const
{
return m_column_type_ref;
@@ -4041,6 +4056,14 @@ public:
{
m_table_rowtype_ref= ref;
}
+ class Cursor_rowtype *cursor_rowtype_ref() const
+ {
+ return m_cursor_rowtype_ref;
+ }
+ void set_cursor_rowtype_ref(class Cursor_rowtype *ref)
+ {
+ m_cursor_rowtype_ref= ref;
+ }
/*
Find a ROW field by name.
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index a40ad52676c..2b5b1db5ddf 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -3152,6 +3152,26 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
}
+int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables,
+ sp_instr *instr)
+{
+ Query_arena *old_arena= thd->stmt_arena;
+ /*
+ Get the Query_arena from the cursor statement LEX, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each cursor operation,
+ e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
+ */
+ thd->stmt_arena= m_lex->query_arena();
+ int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr);
+ if (thd->stmt_arena->free_list)
+ cleanup_items(thd->stmt_arena->free_list);
+ thd->stmt_arena= old_arena;
+ return res;
+}
+
+
/*
sp_instr class functions
*/
@@ -3455,7 +3475,8 @@ sp_instr_set_row_field_by_name::print(String *str)
int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
sp_variable *var= m_ctx->find_variable(m_offset);
DBUG_ASSERT(var);
- DBUG_ASSERT(var->field_def.is_table_rowtype_ref());
+ DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
+ var->field_def.is_cursor_rowtype_ref());
rsrv+= var->name.length + 2 * m_field_name.length;
if (str->reserve(rsrv))
@@ -3915,7 +3936,7 @@ sp_instr_cpush::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpush::execute");
- int ret= thd->spcont->push_cursor(thd, &m_lex_keeper, this);
+ int ret= thd->spcont->push_cursor(thd, &m_lex_keeper);
*nextp= m_ip+1;
@@ -3995,19 +4016,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
else
{
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
- Query_arena *old_arena= thd->stmt_arena;
-
- /*
- Get the Query_arena from the cpush instruction, which contains
- the free_list of the query, so new items (if any) are stored in
- the right free_list, and we can cleanup after each open.
- */
- thd->stmt_arena= c->get_instr();
- res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
- /* Cleanup the query's items */
- if (thd->stmt_arena->free_list)
- cleanup_items(thd->stmt_arena->free_list);
- thd->stmt_arena= old_arena;
+ res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
/* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
@@ -4140,6 +4149,72 @@ sp_instr_cfetch::print(String *str)
/*
+ sp_instr_cursor_copy_struct class functions
+*/
+
+int
+sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cusrot_copy_struct::exec_core");
+ int ret= 0;
+ Item_field_row *row= (Item_field_row*) thd->spcont->get_item(m_var);
+ DBUG_ASSERT(row->type_handler() == &type_handler_row);
+
+ /*
+ Copy structure only once. If the cursor%ROWTYPE variable is declared
+ inside a LOOP block, it gets its structure on the first loop interation
+ and remembers the structure for all consequent loop iterations.
+ It we recreated the structure on every iteration, we would get
+ potential memory leaks, and it would be less efficient.
+ */
+ if (!row->arguments())
+ {
+ sp_cursor tmp(thd, &m_lex_keeper);
+ if (!(ret= tmp.open_view_structure_only(thd)))
+ {
+ Row_definition_list defs;
+ if (!(ret= tmp.export_structure(thd, &defs)))
+ {
+ /*
+ Create row elements on the caller arena.
+ It's the same arena that was used during sp_rcontext::create().
+ This puts cursor%ROWTYPE elements on the same mem_root
+ where explicit ROW elements and table%ROWTYPE reside.
+ */
+ Query_arena current_arena;
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
+ row->row_create_items(thd, &defs);
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
+ }
+ tmp.close(thd);
+ }
+ }
+ *nextp= m_ip + 1;
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::execute");
+ int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
+ DBUG_RETURN(ret);
+}
+
+
+void
+sp_instr_cursor_copy_struct::print(String *str)
+{
+ sp_variable *var= m_ctx->find_variable(m_var);
+ str->append(STRING_WITH_LEN("cursor_copy_struct "));
+ str->append(var->name.str, var->name.length);
+ str->append('@');
+ str->append_ulonglong(m_var);
+}
+
+
+/*
sp_instr_error class functions
*/
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 92f17884285..37ef4b6825f 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -341,6 +341,8 @@ public:
bool
show_create_routine(THD *thd, int type);
+ MEM_ROOT *get_main_mem_root() { return &main_mem_root; }
+
int
add_instr(sp_instr *instr);
@@ -767,6 +769,42 @@ private:
}; // class sp_head : public Sql_alloc
+class sp_lex_cursor: public sp_lex_local, public Query_arena
+{
+public:
+ sp_lex_cursor(THD *thd, const LEX *oldlex, MEM_ROOT *mem_root_arg)
+ :sp_lex_local(thd, oldlex),
+ Query_arena(mem_root_arg, STMT_INITIALIZED_FOR_SP)
+ { }
+ sp_lex_cursor(THD *thd, const LEX *oldlex)
+ :sp_lex_local(thd, oldlex),
+ Query_arena(thd->lex->sphead->get_main_mem_root(), STMT_INITIALIZED_FOR_SP)
+ { }
+ ~sp_lex_cursor() { free_items(); }
+ void cleanup_stmt() { }
+ Query_arena *query_arena() { return this; }
+ bool validate()
+ {
+ DBUG_ASSERT(sql_command == SQLCOM_SELECT);
+ if (result)
+ {
+ my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0));
+ return true;
+ }
+ return false;
+ }
+ bool stmt_finalize(THD *thd)
+ {
+ if (validate())
+ return true;
+ sp_lex_in_use= true;
+ free_list= thd->free_list;
+ thd->free_list= NULL;
+ return false;
+ }
+};
+
+
//
// "Instructions"...
//
@@ -924,6 +962,9 @@ public:
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_instr* instr);
+ int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
+ sp_instr *instr);
+
inline uint sql_command() const
{
return (uint)m_lex->sql_command;
@@ -1550,6 +1591,30 @@ private:
}; // class sp_instr_copen : public sp_instr_stmt
+/**
+ Initialize the structure of a cursor%ROWTYPE variable
+ from the LEX containing the cursor SELECT statement.
+*/
+class sp_instr_cursor_copy_struct: public sp_instr
+{
+ /**< Prevent use of these */
+ sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &);
+ void operator=(sp_instr_cursor_copy_struct &);
+ sp_lex_keeper m_lex_keeper;
+ uint m_var;
+public:
+ sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx,
+ sp_lex_cursor *lex, uint voffs)
+ : sp_instr(ip, ctx), m_lex_keeper(lex, FALSE), m_var(voffs)
+ {}
+ virtual ~sp_instr_cursor_copy_struct()
+ {}
+ virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+ virtual void print(String *str);
+};
+
+
class sp_instr_cclose : public sp_instr
{
sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 332f43db846..3a3ad0713b5 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -553,12 +553,13 @@ sp_pcontext::find_handler(const Sql_condition_identity &value) const
}
-bool sp_pcontext::add_cursor(const LEX_STRING name, sp_pcontext *param_ctx)
+bool sp_pcontext::add_cursor(const LEX_STRING name, sp_pcontext *param_ctx,
+ sp_lex_cursor *lex)
{
if (m_cursors.elements() == m_max_cursor_index)
++m_max_cursor_index;
- return m_cursors.append(sp_pcursor(name, param_ctx));
+ return m_cursors.append(sp_pcursor(name, param_ctx, lex));
}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 85d0afa8feb..6d8f5e1baf8 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -288,11 +288,14 @@ public:
class sp_pcursor: public LEX_STRING
{
class sp_pcontext *m_param_context; // Formal parameters
+ class sp_lex_cursor *m_lex; // The cursor statement LEX
public:
- sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx)
- :LEX_STRING(name), m_param_context(param_ctx)
+ sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx,
+ class sp_lex_cursor *lex)
+ :LEX_STRING(name), m_param_context(param_ctx), m_lex(lex)
{ }
class sp_pcontext *param_context() const { return m_param_context; }
+ class sp_lex_cursor *lex() const { return m_lex; }
};
@@ -633,7 +636,8 @@ public:
// Cursors.
/////////////////////////////////////////////////////////////////////////
- bool add_cursor(const LEX_STRING name, sp_pcontext *param_ctx);
+ bool add_cursor(const LEX_STRING name, sp_pcontext *param_ctx,
+ class sp_lex_cursor *lex);
/// See comment for find_variable() above.
const sp_pcursor *find_cursor(const LEX_STRING name,
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index b631ad71e49..d60d7f5118f 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -286,6 +286,13 @@ bool sp_rcontext::init_var_items(THD *thd,
item->row_create_items(thd, &defs))
return true;
}
+ else if (def->is_cursor_rowtype_ref())
+ {
+ Row_definition_list defs;
+ Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
+ if (!(m_var_items[idx]= item))
+ return true;
+ }
else if (def->is_row())
{
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
@@ -341,14 +348,13 @@ bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
}
-bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper,
- sp_instr_cpush *i)
+bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper)
{
/*
We should create cursors in the callers arena, as
it could be (and usually is) used in several instructions.
*/
- sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper, i);
+ sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper);
if (c == NULL)
return true;
@@ -687,11 +693,10 @@ bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
///////////////////////////////////////////////////////////////////////////
-sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i):
+sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper):
result(thd_arg),
m_lex_keeper(lex_keeper),
server_side_cursor(NULL),
- m_i(i),
m_fetch_count(0),
m_row_count(0),
m_found(false)
@@ -731,6 +736,21 @@ int sp_cursor::open(THD *thd)
}
+int sp_cursor::open_view_structure_only(THD *thd)
+{
+ int res;
+ int thd_no_errors_save= thd->no_errors;
+ Item *limit_rows_examined= thd->lex->limit_rows_examined;
+ if (!(thd->lex->limit_rows_examined= new (thd->mem_root) Item_uint(thd, 0)))
+ return -1;
+ thd->no_errors= true; // Suppress ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+ res= open(thd);
+ thd->no_errors= thd_no_errors_save;
+ thd->lex->limit_rows_examined= limit_rows_examined;
+ return res;
+}
+
+
int sp_cursor::close(THD *thd)
{
if (! server_side_cursor)
@@ -802,6 +822,11 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
}
+bool sp_cursor::export_structure(THD *thd, Row_definition_list *list)
+{
+ return server_side_cursor->export_structure(thd, list);
+}
+
///////////////////////////////////////////////////////////////////////////
// sp_cursor::Select_fetch_into_spvars implementation.
///////////////////////////////////////////////////////////////////////////
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 1c47b1caff0..98464518787 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -272,7 +272,7 @@ public:
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
- bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+ bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper);
/// Pop and delete given number of sp_cursor instance from the cursor stack.
///
@@ -434,7 +434,7 @@ private:
};
public:
- sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+ sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper);
virtual ~sp_cursor()
{ destroy(); }
@@ -443,6 +443,8 @@ public:
int open(THD *thd);
+ int open_view_structure_only(THD *thd);
+
int close(THD *thd);
my_bool is_open()
@@ -459,14 +461,12 @@ public:
int fetch(THD *, List<sp_variable> *vars);
- sp_instr_cpush *get_instr()
- { return m_i; }
+ bool export_structure(THD *thd, Row_definition_list *list);
private:
Select_fetch_into_spvars result;
sp_lex_keeper *m_lex_keeper;
Server_side_cursor *server_side_cursor;
- sp_instr_cpush *m_i; // My push instruction
ulonglong m_fetch_count; // Number of FETCH commands since last OPEN
ulonglong m_row_count; // Number of successful FETCH since last OPEN
bool m_found; // If last FETCH fetched a row
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 60d80387a10..40eb7046108 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -53,6 +53,10 @@ public:
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
virtual void close();
+ bool export_structure(THD *thd, Row_definition_list *defs)
+ {
+ return table->export_structure(thd, defs);
+ }
virtual ~Materialized_cursor();
void on_table_fill_finished();
diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h
index bff47d654b3..6fa72a2005d 100644
--- a/sql/sql_cursor.h
+++ b/sql/sql_cursor.h
@@ -54,6 +54,11 @@ public:
virtual int open(JOIN *top_level_join)= 0;
virtual void fetch(ulong num_rows)= 0;
virtual void close()= 0;
+ virtual bool export_structure(THD *thd, Row_definition_list *defs)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
virtual ~Server_side_cursor();
static void operator delete(void *ptr, size_t size);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 8f258f50902..ca35535973e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5297,6 +5297,9 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
Qualified_column_ident *ref,
Item *def)
{
+ uint coffp;
+ const sp_pcursor *pcursor= ref->table.str && ref->db.str ? NULL :
+ spcont->find_cursor(ref->m_column, &coffp, false);
uint num_vars= spcont->context_var_count();
if (!def && !(def= new (thd->mem_root) Item_null(thd)))
@@ -5307,24 +5310,41 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
bool last= i == num_vars - 1;
uint var_idx= spcont->var_context2runtime(i);
sp_variable *spvar= spcont->find_context_variable(i);
- /*
- When parsing a qualified identifier chain, the parser does not know yet
- if it's going to be a qualified column name (for %TYPE),
- or a qualified table name (for %ROWTYPE). So it collects the chain
- into Qualified_column_ident.
- Now we know that it was actually a qualified table name (%ROWTYPE).
- Create a new Table_ident from Qualified_column_ident,
- shifting fields as follows:
- - ref->m_column becomes table_ref->table
- - ref->table becomes table_ref->db
- */
- Table_ident *table_ref;
- if (!(table_ref= new (thd->mem_root) Table_ident(thd,
- ref->table,
- ref->m_column,
- false)))
- return true;
- spvar->field_def.set_table_rowtype_ref(table_ref);
+
+ if (pcursor)
+ {
+ Cursor_rowtype *ref;
+ if (!(ref= new (thd->mem_root) Cursor_rowtype(coffp)))
+ return true;
+ spvar->field_def.set_cursor_rowtype_ref(ref);
+ sp_instr_cursor_copy_struct *instr=
+ new (thd->mem_root) sp_instr_cursor_copy_struct(sphead->instructions(),
+ spcont, pcursor->lex(),
+ spvar->offset);
+ if (instr == NULL || sphead->add_instr(instr))
+ return true;
+ }
+ else
+ {
+ /*
+ When parsing a qualified identifier chain, the parser does not know yet
+ if it's going to be a qualified column name (for %TYPE),
+ or a qualified table name (for %ROWTYPE). So it collects the chain
+ into Qualified_column_ident.
+ Now we know that it was actually a qualified table name (%ROWTYPE).
+ Create a new Table_ident from Qualified_column_ident,
+ shifting fields as follows:
+ - ref->m_column becomes table_ref->table
+ - ref->table becomes table_ref->db
+ */
+ Table_ident *table_ref;
+ if (!(table_ref= new (thd->mem_root) Table_ident(thd,
+ ref->table,
+ ref->m_column,
+ false)))
+ return true;
+ spvar->field_def.set_table_rowtype_ref(table_ref);
+ }
spvar->field_def.field_name= spvar->name.str;
spvar->default_value= def;
/* The last instruction is responsible for freeing LEX. */
@@ -5485,7 +5505,8 @@ bool LEX::sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop)
/***************************************************************************/
-bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt,
+bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name,
+ sp_lex_cursor *cursor_stmt,
sp_pcontext *param_ctx)
{
uint offp;
@@ -5501,7 +5522,7 @@ bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt,
spcont->current_cursor_count());
return i == NULL ||
sphead->add_instr(i) ||
- spcont->add_cursor(name, param_ctx);
+ spcont->add_cursor(name, param_ctx, cursor_stmt);
}
@@ -6178,7 +6199,8 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
}
Item_splocal *item;
- if (spv->field_def.is_table_rowtype_ref())
+ if (spv->field_def.is_table_rowtype_ref() ||
+ spv->field_def.is_cursor_rowtype_ref())
{
if (!(item= new (thd->mem_root)
Item_splocal_row_field_by_name(thd, a, b, spv->offset,
@@ -6406,7 +6428,8 @@ bool LEX::set_variable(const LEX_STRING &name1,
sp_variable *spv;
if (spcont && (spv= spcont->find_variable(name1, false)))
{
- if (spv->field_def.is_table_rowtype_ref())
+ if (spv->field_def.is_table_rowtype_ref() ||
+ spv->field_def.is_cursor_rowtype_ref())
return sphead->set_local_variable_row_field_by_name(thd, spcont,
spv, name2,
item, this);
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 8edebaf6028..3cdf05377dd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2936,6 +2936,12 @@ public:
delete_dynamic(&plugins);
}
+ virtual class Query_arena *query_arena()
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
+
void start(THD *thd);
const char *substatement_query(THD *thd) const;
@@ -3155,7 +3161,8 @@ public:
bool sp_handler_declaration_init(THD *thd, int type);
bool sp_handler_declaration_finalize(THD *thd, int type);
- bool sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt,
+ bool sp_declare_cursor(THD *thd, const LEX_STRING name,
+ class sp_lex_cursor *cursor_stmt,
sp_pcontext *param_ctx);
bool sp_open_cursor(THD *thd, const LEX_STRING name,
List<sp_assignment_lex> *parameters);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index d3f2a674664..5ca19e34bac 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -377,7 +377,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
res|= thd->is_error();
if (unlikely(res))
result->abort_result_set();
- if (thd->killed == ABORT_QUERY)
+ if (thd->killed == ABORT_QUERY && !thd->no_errors)
{
/*
If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 93fdff57d38..45af801272a 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -783,6 +783,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Item_param *item_param;
Key_part_spec *key_part;
LEX *lex;
+ class sp_lex_cursor *sp_cursor_stmt;
LEX_STRING *lex_str_ptr;
LEX_USER *lex_user;
List<Condition_information_item> *cond_info_list;
@@ -1710,6 +1711,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
expr_list opt_udf_expr_list udf_expr_list when_list
ident_list ident_list_arg opt_expr_list
+%type <sp_cursor_stmt>
+ sp_cursor_stmt_lex
+ sp_cursor_stmt
+
%type <var_type>
option_type opt_var_type opt_var_ident_type
@@ -1871,7 +1876,6 @@ END_OF_INPUT
%type <num> sp_decl_idents sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
%type <spblock> sp_decls sp_decl sp_decl_body
-%type <lex> sp_cursor_stmt
%type <spname> sp_name
%type <spvar> sp_param_name sp_param_name_and_type
%type <spvar_mode> sp_opt_inout
@@ -3024,22 +3028,28 @@ sp_decl_body:
}
;
+
+sp_cursor_stmt_lex:
+ {
+ DBUG_ASSERT(thd->lex->sphead);
+ if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex)))
+ MYSQL_YYABORT;
+ }
+ ;
+
sp_cursor_stmt:
+ sp_cursor_stmt_lex
{
- Lex->sphead->reset_lex(thd);
+ DBUG_ASSERT(thd->free_list == NULL);
+ Lex->sphead->reset_lex(thd, $1);
}
select
{
- LEX *lex= Lex;
-
- DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
-
- if (lex->result)
- my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0)));
- lex->sp_lex_in_use= TRUE;
- $$= lex;
- if (lex->sphead->restore_lex(thd))
+ DBUG_ASSERT(Lex == $1);
+ if ($1->stmt_finalize(thd) ||
+ $1->sphead->restore_lex(thd))
MYSQL_YYABORT;
+ $$= $1;
}
;
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index c72fa8ca0e5..2360b735674 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -199,6 +199,7 @@ void ORAerror(THD *thd, const char *s)
Key_part_spec *key_part;
LEX *lex;
sp_assignment_lex *assignment_lex;
+ class sp_lex_cursor *sp_cursor_stmt;
LEX_STRING *lex_str_ptr;
LEX_USER *lex_user;
List<Condition_information_item> *cond_info_list;
@@ -1150,6 +1151,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
ident_list ident_list_arg opt_expr_list
decode_when_list
+%type <sp_cursor_stmt>
+ sp_cursor_stmt_lex
+ sp_cursor_stmt
+
%type <assignment_lex>
assignment_source_lex
assignment_source_expr
@@ -1333,7 +1338,6 @@ END_OF_INPUT
%type <sp_instr_addr> sp_instr_addr
%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
%type <num> opt_exception_clause exception_handlers
-%type <lex> sp_cursor_stmt remember_lex
%type <spname> sp_name
%type <spvar> sp_param_name sp_param_name_and_type
%type <for_loop> sp_for_loop_index_and_bounds
@@ -2609,22 +2613,27 @@ opt_parenthesized_cursor_formal_parameters:
;
+sp_cursor_stmt_lex:
+ {
+ DBUG_ASSERT(thd->lex->sphead);
+ if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex)))
+ MYSQL_YYABORT;
+ }
+ ;
+
sp_cursor_stmt:
+ sp_cursor_stmt_lex
{
- Lex->sphead->reset_lex(thd);
+ DBUG_ASSERT(thd->free_list == NULL);
+ Lex->sphead->reset_lex(thd, $1);
}
select
{
- LEX *lex= Lex;
-
- DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
-
- if (lex->result)
- my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0)));
- lex->sp_lex_in_use= TRUE;
- $$= lex;
- if (lex->sphead->restore_lex(thd))
+ DBUG_ASSERT(Lex == $1);
+ if ($1->stmt_finalize(thd) ||
+ $1->sphead->restore_lex(thd))
MYSQL_YYABORT;
+ $$= $1;
}
;
@@ -3303,12 +3312,6 @@ sp_proc_stmt_goto:
;
-remember_lex:
- {
- $$= thd->lex;
- }
- ;
-
assignment_source_lex:
{
DBUG_ASSERT(Lex->sphead);
diff --git a/sql/table.cc b/sql/table.cc
index 38cda5cc209..ee592f120d3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -8213,3 +8213,25 @@ Field *TABLE::find_field_by_name(const char *str) const
}
return NULL;
}
+
+
+bool TABLE::export_structure(THD *thd, Row_definition_list *defs)
+{
+ for (Field **src= field; *src; src++)
+ {
+ uint offs;
+ if (defs->find_row_field_by_name(src[0]->field_name, &offs))
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name);
+ return true;
+ }
+ Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src);
+ if (!def)
+ return true;
+ def->flags&= (uint) ~NOT_NULL_FLAG;
+ if ((def->sp_prepare_create_field(thd, thd->mem_root)) ||
+ (defs->push_back(def, thd->mem_root)))
+ return true;
+ }
+ return false;
+}
diff --git a/sql/table.h b/sql/table.h
index 22dfeaaa596..4885196a817 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1441,6 +1441,7 @@ public:
TMP_TABLE_PARAM *tmp_table_param,
bool with_cleanup);
Field *find_field_by_name(const char *str) const;
+ bool export_structure(THD *thd, class Row_definition_list *defs);
};