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.
This commit is contained in:
Alexander Barkov 2017-03-08 23:20:39 +04:00
parent 1b8a0c879d
commit f429b5a834
20 changed files with 2447 additions and 85 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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);
@ -4139,6 +4148,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
*/

View File

@ -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 */

View File

@ -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));
}

View File

@ -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,

View File

@ -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.
///////////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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,6 +5310,22 @@ 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);
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),
@ -5325,6 +5344,7 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
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);

View File

@ -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);

View File

@ -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,

View File

@ -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:
sp_cursor_stmt_lex:
{
Lex->sphead->reset_lex(thd);
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
{
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;
}
;

View File

@ -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:
sp_cursor_stmt_lex:
{
Lex->sphead->reset_lex(thd);
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
{
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);

View File

@ -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;
}

View File

@ -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);
};