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:
parent
1b8a0c879d
commit
f429b5a834
@ -1007,3 +1007,37 @@ Pos Instruction
|
|||||||
5 set rec1.c@0["c"] rec1.d@0["d"]
|
5 set rec1.c@0["c"] rec1.d@0["d"]
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
DROP TABLE t1;
|
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;
|
||||||
|
978
mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result
Normal file
978
mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result
Normal 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;
|
@ -764,3 +764,28 @@ DELIMITER ;$$
|
|||||||
SHOW PROCEDURE CODE p1;
|
SHOW PROCEDURE CODE p1;
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
DROP TABLE t1;
|
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;
|
||||||
|
1057
mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
Normal file
1057
mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test
Normal file
File diff suppressed because it is too large
Load Diff
25
sql/field.h
25
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,
|
This class is used during a stored routine or a trigger execution,
|
||||||
at sp_rcontext::create() time.
|
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 Qualified_column_ident *m_column_type_ref; // for %TYPE
|
||||||
class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE
|
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
|
Row_definition_list *m_row_field_definitions; // for ROW
|
||||||
public:
|
public:
|
||||||
Spvar_definition()
|
Spvar_definition()
|
||||||
:m_column_type_ref(NULL),
|
:m_column_type_ref(NULL),
|
||||||
m_table_rowtype_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)
|
Spvar_definition(THD *thd, Field *field)
|
||||||
:Column_definition(thd, field, NULL),
|
:Column_definition(thd, field, NULL),
|
||||||
m_column_type_ref(NULL),
|
m_column_type_ref(NULL),
|
||||||
m_table_rowtype_ref(NULL),
|
m_table_rowtype_ref(NULL),
|
||||||
|
m_cursor_rowtype_ref(NULL),
|
||||||
m_row_field_definitions(NULL)
|
m_row_field_definitions(NULL)
|
||||||
{ }
|
{ }
|
||||||
bool is_column_type_ref() const { return m_column_type_ref != 0; }
|
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_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
|
class Qualified_column_ident *column_type_ref() const
|
||||||
{
|
{
|
||||||
return m_column_type_ref;
|
return m_column_type_ref;
|
||||||
@ -4041,6 +4056,14 @@ public:
|
|||||||
{
|
{
|
||||||
m_table_rowtype_ref= ref;
|
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.
|
Find a ROW field by name.
|
||||||
|
105
sql/sp_head.cc
105
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
|
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;
|
int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
|
||||||
sp_variable *var= m_ctx->find_variable(m_offset);
|
sp_variable *var= m_ctx->find_variable(m_offset);
|
||||||
DBUG_ASSERT(var);
|
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;
|
rsrv+= var->name.length + 2 * m_field_name.length;
|
||||||
if (str->reserve(rsrv))
|
if (str->reserve(rsrv))
|
||||||
@ -3915,7 +3936,7 @@ sp_instr_cpush::execute(THD *thd, uint *nextp)
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("sp_instr_cpush::execute");
|
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;
|
*nextp= m_ip+1;
|
||||||
|
|
||||||
@ -3995,19 +4016,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
|
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
|
||||||
Query_arena *old_arena= thd->stmt_arena;
|
res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
/* TODO: Assert here that we either have an error or a cursor */
|
/* TODO: Assert here that we either have an error or a cursor */
|
||||||
}
|
}
|
||||||
DBUG_RETURN(res);
|
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, ¤t_arena);
|
||||||
|
row->row_create_items(thd, &defs);
|
||||||
|
thd->restore_active_arena(thd->spcont->callers_arena, ¤t_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
|
sp_instr_error class functions
|
||||||
*/
|
*/
|
||||||
|
@ -341,6 +341,8 @@ public:
|
|||||||
bool
|
bool
|
||||||
show_create_routine(THD *thd, int type);
|
show_create_routine(THD *thd, int type);
|
||||||
|
|
||||||
|
MEM_ROOT *get_main_mem_root() { return &main_mem_root; }
|
||||||
|
|
||||||
int
|
int
|
||||||
add_instr(sp_instr *instr);
|
add_instr(sp_instr *instr);
|
||||||
|
|
||||||
@ -767,6 +769,42 @@ private:
|
|||||||
}; // class sp_head : public Sql_alloc
|
}; // 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"...
|
// "Instructions"...
|
||||||
//
|
//
|
||||||
@ -924,6 +962,9 @@ public:
|
|||||||
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
|
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
|
||||||
sp_instr* instr);
|
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
|
inline uint sql_command() const
|
||||||
{
|
{
|
||||||
return (uint)m_lex->sql_command;
|
return (uint)m_lex->sql_command;
|
||||||
@ -1550,6 +1591,30 @@ private:
|
|||||||
}; // class sp_instr_copen : public sp_instr_stmt
|
}; // 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
|
class sp_instr_cclose : public sp_instr
|
||||||
{
|
{
|
||||||
sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
|
sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
|
||||||
|
@ -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)
|
if (m_cursors.elements() == m_max_cursor_index)
|
||||||
++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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -288,11 +288,14 @@ public:
|
|||||||
class sp_pcursor: public LEX_STRING
|
class sp_pcursor: public LEX_STRING
|
||||||
{
|
{
|
||||||
class sp_pcontext *m_param_context; // Formal parameters
|
class sp_pcontext *m_param_context; // Formal parameters
|
||||||
|
class sp_lex_cursor *m_lex; // The cursor statement LEX
|
||||||
public:
|
public:
|
||||||
sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx)
|
sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx,
|
||||||
:LEX_STRING(name), m_param_context(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_pcontext *param_context() const { return m_param_context; }
|
||||||
|
class sp_lex_cursor *lex() const { return m_lex; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -633,7 +636,8 @@ public:
|
|||||||
// Cursors.
|
// 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.
|
/// See comment for find_variable() above.
|
||||||
const sp_pcursor *find_cursor(const LEX_STRING name,
|
const sp_pcursor *find_cursor(const LEX_STRING name,
|
||||||
|
@ -286,6 +286,13 @@ bool sp_rcontext::init_var_items(THD *thd,
|
|||||||
item->row_create_items(thd, &defs))
|
item->row_create_items(thd, &defs))
|
||||||
return true;
|
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())
|
else if (def->is_row())
|
||||||
{
|
{
|
||||||
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
|
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,
|
bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper)
|
||||||
sp_instr_cpush *i)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We should create cursors in the callers arena, as
|
We should create cursors in the callers arena, as
|
||||||
it could be (and usually is) used in several instructions.
|
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)
|
if (c == NULL)
|
||||||
return true;
|
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),
|
result(thd_arg),
|
||||||
m_lex_keeper(lex_keeper),
|
m_lex_keeper(lex_keeper),
|
||||||
server_side_cursor(NULL),
|
server_side_cursor(NULL),
|
||||||
m_i(i),
|
|
||||||
m_fetch_count(0),
|
m_fetch_count(0),
|
||||||
m_row_count(0),
|
m_row_count(0),
|
||||||
m_found(false)
|
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)
|
int sp_cursor::close(THD *thd)
|
||||||
{
|
{
|
||||||
if (! server_side_cursor)
|
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.
|
// sp_cursor::Select_fetch_into_spvars implementation.
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -272,7 +272,7 @@ public:
|
|||||||
/// @return error flag.
|
/// @return error flag.
|
||||||
/// @retval false on success.
|
/// @retval false on success.
|
||||||
/// @retval true on error.
|
/// @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.
|
/// Pop and delete given number of sp_cursor instance from the cursor stack.
|
||||||
///
|
///
|
||||||
@ -434,7 +434,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
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()
|
virtual ~sp_cursor()
|
||||||
{ destroy(); }
|
{ destroy(); }
|
||||||
@ -443,6 +443,8 @@ public:
|
|||||||
|
|
||||||
int open(THD *thd);
|
int open(THD *thd);
|
||||||
|
|
||||||
|
int open_view_structure_only(THD *thd);
|
||||||
|
|
||||||
int close(THD *thd);
|
int close(THD *thd);
|
||||||
|
|
||||||
my_bool is_open()
|
my_bool is_open()
|
||||||
@ -459,14 +461,12 @@ public:
|
|||||||
|
|
||||||
int fetch(THD *, List<sp_variable> *vars);
|
int fetch(THD *, List<sp_variable> *vars);
|
||||||
|
|
||||||
sp_instr_cpush *get_instr()
|
bool export_structure(THD *thd, Row_definition_list *list);
|
||||||
{ return m_i; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Select_fetch_into_spvars result;
|
Select_fetch_into_spvars result;
|
||||||
sp_lex_keeper *m_lex_keeper;
|
sp_lex_keeper *m_lex_keeper;
|
||||||
Server_side_cursor *server_side_cursor;
|
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_fetch_count; // Number of FETCH commands since last OPEN
|
||||||
ulonglong m_row_count; // Number of successful FETCH since last OPEN
|
ulonglong m_row_count; // Number of successful FETCH since last OPEN
|
||||||
bool m_found; // If last FETCH fetched a row
|
bool m_found; // If last FETCH fetched a row
|
||||||
|
@ -53,6 +53,10 @@ public:
|
|||||||
virtual int open(JOIN *join __attribute__((unused)));
|
virtual int open(JOIN *join __attribute__((unused)));
|
||||||
virtual void fetch(ulong num_rows);
|
virtual void fetch(ulong num_rows);
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
bool export_structure(THD *thd, Row_definition_list *defs)
|
||||||
|
{
|
||||||
|
return table->export_structure(thd, defs);
|
||||||
|
}
|
||||||
virtual ~Materialized_cursor();
|
virtual ~Materialized_cursor();
|
||||||
|
|
||||||
void on_table_fill_finished();
|
void on_table_fill_finished();
|
||||||
|
@ -54,6 +54,11 @@ public:
|
|||||||
virtual int open(JOIN *top_level_join)= 0;
|
virtual int open(JOIN *top_level_join)= 0;
|
||||||
virtual void fetch(ulong num_rows)= 0;
|
virtual void fetch(ulong num_rows)= 0;
|
||||||
virtual void close()= 0;
|
virtual void close()= 0;
|
||||||
|
virtual bool export_structure(THD *thd, Row_definition_list *defs)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
virtual ~Server_side_cursor();
|
virtual ~Server_side_cursor();
|
||||||
|
|
||||||
static void operator delete(void *ptr, size_t size);
|
static void operator delete(void *ptr, size_t size);
|
||||||
|
@ -5297,6 +5297,9 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
|
|||||||
Qualified_column_ident *ref,
|
Qualified_column_ident *ref,
|
||||||
Item *def)
|
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();
|
uint num_vars= spcont->context_var_count();
|
||||||
|
|
||||||
if (!def && !(def= new (thd->mem_root) Item_null(thd)))
|
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;
|
bool last= i == num_vars - 1;
|
||||||
uint var_idx= spcont->var_context2runtime(i);
|
uint var_idx= spcont->var_context2runtime(i);
|
||||||
sp_variable *spvar= spcont->find_context_variable(i);
|
sp_variable *spvar= spcont->find_context_variable(i);
|
||||||
/*
|
|
||||||
When parsing a qualified identifier chain, the parser does not know yet
|
if (pcursor)
|
||||||
if it's going to be a qualified column name (for %TYPE),
|
{
|
||||||
or a qualified table name (for %ROWTYPE). So it collects the chain
|
Cursor_rowtype *ref;
|
||||||
into Qualified_column_ident.
|
if (!(ref= new (thd->mem_root) Cursor_rowtype(coffp)))
|
||||||
Now we know that it was actually a qualified table name (%ROWTYPE).
|
return true;
|
||||||
Create a new Table_ident from Qualified_column_ident,
|
spvar->field_def.set_cursor_rowtype_ref(ref);
|
||||||
shifting fields as follows:
|
sp_instr_cursor_copy_struct *instr=
|
||||||
- ref->m_column becomes table_ref->table
|
new (thd->mem_root) sp_instr_cursor_copy_struct(sphead->instructions(),
|
||||||
- ref->table becomes table_ref->db
|
spcont, pcursor->lex(),
|
||||||
*/
|
spvar->offset);
|
||||||
Table_ident *table_ref;
|
if (instr == NULL || sphead->add_instr(instr))
|
||||||
if (!(table_ref= new (thd->mem_root) Table_ident(thd,
|
return true;
|
||||||
ref->table,
|
}
|
||||||
ref->m_column,
|
else
|
||||||
false)))
|
{
|
||||||
return true;
|
/*
|
||||||
spvar->field_def.set_table_rowtype_ref(table_ref);
|
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->field_def.field_name= spvar->name.str;
|
||||||
spvar->default_value= def;
|
spvar->default_value= def;
|
||||||
/* The last instruction is responsible for freeing LEX. */
|
/* 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)
|
sp_pcontext *param_ctx)
|
||||||
{
|
{
|
||||||
uint offp;
|
uint offp;
|
||||||
@ -5501,7 +5522,7 @@ bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt,
|
|||||||
spcont->current_cursor_count());
|
spcont->current_cursor_count());
|
||||||
return i == NULL ||
|
return i == NULL ||
|
||||||
sphead->add_instr(i) ||
|
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;
|
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)
|
if (!(item= new (thd->mem_root)
|
||||||
Item_splocal_row_field_by_name(thd, a, b, spv->offset,
|
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;
|
sp_variable *spv;
|
||||||
if (spcont && (spv= spcont->find_variable(name1, false)))
|
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,
|
return sphead->set_local_variable_row_field_by_name(thd, spcont,
|
||||||
spv, name2,
|
spv, name2,
|
||||||
item, this);
|
item, this);
|
||||||
|
@ -2936,6 +2936,12 @@ public:
|
|||||||
delete_dynamic(&plugins);
|
delete_dynamic(&plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual class Query_arena *query_arena()
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void start(THD *thd);
|
void start(THD *thd);
|
||||||
|
|
||||||
const char *substatement_query(THD *thd) const;
|
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_init(THD *thd, int type);
|
||||||
bool sp_handler_declaration_finalize(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);
|
sp_pcontext *param_ctx);
|
||||||
bool sp_open_cursor(THD *thd, const LEX_STRING name,
|
bool sp_open_cursor(THD *thd, const LEX_STRING name,
|
||||||
List<sp_assignment_lex> *parameters);
|
List<sp_assignment_lex> *parameters);
|
||||||
|
@ -377,7 +377,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
|
|||||||
res|= thd->is_error();
|
res|= thd->is_error();
|
||||||
if (unlikely(res))
|
if (unlikely(res))
|
||||||
result->abort_result_set();
|
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,
|
If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
|
||||||
|
@ -783,6 +783,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
|
|||||||
Item_param *item_param;
|
Item_param *item_param;
|
||||||
Key_part_spec *key_part;
|
Key_part_spec *key_part;
|
||||||
LEX *lex;
|
LEX *lex;
|
||||||
|
class sp_lex_cursor *sp_cursor_stmt;
|
||||||
LEX_STRING *lex_str_ptr;
|
LEX_STRING *lex_str_ptr;
|
||||||
LEX_USER *lex_user;
|
LEX_USER *lex_user;
|
||||||
List<Condition_information_item> *cond_info_list;
|
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
|
expr_list opt_udf_expr_list udf_expr_list when_list
|
||||||
ident_list ident_list_arg opt_expr_list
|
ident_list ident_list_arg opt_expr_list
|
||||||
|
|
||||||
|
%type <sp_cursor_stmt>
|
||||||
|
sp_cursor_stmt_lex
|
||||||
|
sp_cursor_stmt
|
||||||
|
|
||||||
%type <var_type>
|
%type <var_type>
|
||||||
option_type opt_var_type opt_var_ident_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 <num> sp_decl_idents sp_handler_type sp_hcond_list
|
||||||
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
|
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
|
||||||
%type <spblock> sp_decls sp_decl sp_decl_body
|
%type <spblock> sp_decls sp_decl sp_decl_body
|
||||||
%type <lex> sp_cursor_stmt
|
|
||||||
%type <spname> sp_name
|
%type <spname> sp_name
|
||||||
%type <spvar> sp_param_name sp_param_name_and_type
|
%type <spvar> sp_param_name sp_param_name_and_type
|
||||||
%type <spvar_mode> sp_opt_inout
|
%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
|
select
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
DBUG_ASSERT(Lex == $1);
|
||||||
|
if ($1->stmt_finalize(thd) ||
|
||||||
DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
|
$1->sphead->restore_lex(thd))
|
||||||
|
|
||||||
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))
|
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
|
$$= $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -199,6 +199,7 @@ void ORAerror(THD *thd, const char *s)
|
|||||||
Key_part_spec *key_part;
|
Key_part_spec *key_part;
|
||||||
LEX *lex;
|
LEX *lex;
|
||||||
sp_assignment_lex *assignment_lex;
|
sp_assignment_lex *assignment_lex;
|
||||||
|
class sp_lex_cursor *sp_cursor_stmt;
|
||||||
LEX_STRING *lex_str_ptr;
|
LEX_STRING *lex_str_ptr;
|
||||||
LEX_USER *lex_user;
|
LEX_USER *lex_user;
|
||||||
List<Condition_information_item> *cond_info_list;
|
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
|
ident_list ident_list_arg opt_expr_list
|
||||||
decode_when_list
|
decode_when_list
|
||||||
|
|
||||||
|
%type <sp_cursor_stmt>
|
||||||
|
sp_cursor_stmt_lex
|
||||||
|
sp_cursor_stmt
|
||||||
|
|
||||||
%type <assignment_lex>
|
%type <assignment_lex>
|
||||||
assignment_source_lex
|
assignment_source_lex
|
||||||
assignment_source_expr
|
assignment_source_expr
|
||||||
@ -1333,7 +1338,6 @@ END_OF_INPUT
|
|||||||
%type <sp_instr_addr> sp_instr_addr
|
%type <sp_instr_addr> sp_instr_addr
|
||||||
%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
|
%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
|
||||||
%type <num> opt_exception_clause exception_handlers
|
%type <num> opt_exception_clause exception_handlers
|
||||||
%type <lex> sp_cursor_stmt remember_lex
|
|
||||||
%type <spname> sp_name
|
%type <spname> sp_name
|
||||||
%type <spvar> sp_param_name sp_param_name_and_type
|
%type <spvar> sp_param_name sp_param_name_and_type
|
||||||
%type <for_loop> sp_for_loop_index_and_bounds
|
%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
|
select
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
DBUG_ASSERT(Lex == $1);
|
||||||
|
if ($1->stmt_finalize(thd) ||
|
||||||
DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
|
$1->sphead->restore_lex(thd))
|
||||||
|
|
||||||
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))
|
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
|
$$= $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -3303,12 +3312,6 @@ sp_proc_stmt_goto:
|
|||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
remember_lex:
|
|
||||||
{
|
|
||||||
$$= thd->lex;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
assignment_source_lex:
|
assignment_source_lex:
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(Lex->sphead);
|
DBUG_ASSERT(Lex->sphead);
|
||||||
|
22
sql/table.cc
22
sql/table.cc
@ -8213,3 +8213,25 @@ Field *TABLE::find_field_by_name(const char *str) const
|
|||||||
}
|
}
|
||||||
return NULL;
|
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;
|
||||||
|
}
|
||||||
|
@ -1441,6 +1441,7 @@ public:
|
|||||||
TMP_TABLE_PARAM *tmp_table_param,
|
TMP_TABLE_PARAM *tmp_table_param,
|
||||||
bool with_cleanup);
|
bool with_cleanup);
|
||||||
Field *find_field_by_name(const char *str) const;
|
Field *find_field_by_name(const char *str) const;
|
||||||
|
bool export_structure(THD *thd, class Row_definition_list *defs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user