mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-10597 Cursors with parameters
This commit is contained in:
@ -827,3 +827,63 @@ SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
|
|||||||
f1(3) f1(4) f1(5) f1(6)
|
f1(3) f1(4) f1(5) f1(6)
|
||||||
3 4 4 5
|
3 4 4 5
|
||||||
DROP FUNCTION f1;
|
DROP FUNCTION f1;
|
||||||
|
#
|
||||||
|
# Start of MDEV-10597 Cursors with parameters
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(arg_value_a VARCHAR, arg_value_b VARCHAR,
|
||||||
|
arg_pattern_a VARCHAR, arg_pattern_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (p_value_a VARCHAR,
|
||||||
|
p_value_b VARCHAR,
|
||||||
|
p_pattern_a VARCHAR,
|
||||||
|
p_pattern_b VARCHAR,
|
||||||
|
p_limit_a INT,
|
||||||
|
p_limit_b INT,
|
||||||
|
p_unused TEXT) IS
|
||||||
|
(SELECT p_value_a, p_value_b FROM DUAL
|
||||||
|
WHERE p_value_a LIKE p_pattern_a LIMIT p_limit_a)
|
||||||
|
UNION
|
||||||
|
(SELECT p_value_b, p_value_a FROM DUAL
|
||||||
|
WHERE p_value_b LIKE p_pattern_b LIMIT p_limit_b);
|
||||||
|
BEGIN
|
||||||
|
OPEN c(arg_value_a, (SELECT arg_value_b),
|
||||||
|
arg_pattern_a, arg_pattern_b, 100, 101, 'x');
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('aaa','bbb','aaa','bbb');
|
||||||
|
v_a v_b
|
||||||
|
aaa bbb
|
||||||
|
v_a v_b
|
||||||
|
bbb aaa
|
||||||
|
SHOW PROCEDURE CODE p1;
|
||||||
|
Pos Instruction
|
||||||
|
0 set v_a@4 NULL
|
||||||
|
1 set v_b@5 NULL
|
||||||
|
2 cpush c@0
|
||||||
|
3 set p_value_a@6 arg_value_a@0
|
||||||
|
4 set p_value_b@7 (select arg_value_b@1)
|
||||||
|
5 set p_pattern_a@8 arg_pattern_a@2
|
||||||
|
6 set p_pattern_b@9 arg_pattern_b@3
|
||||||
|
7 set p_limit_a@10 100
|
||||||
|
8 set p_limit_b@11 101
|
||||||
|
9 set p_unused@12 'x'
|
||||||
|
10 copen c@0
|
||||||
|
11 cfetch c@0 v_a@4 v_b@5
|
||||||
|
12 jump_if_not 14(0) "c"%NOTFOUND
|
||||||
|
13 jump 16
|
||||||
|
14 stmt 0 "SELECT v_a, v_b"
|
||||||
|
15 jump 11
|
||||||
|
16 cclose c@0
|
||||||
|
17 cpop 1
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# End of MDEV-10597 Cursors with parameters
|
||||||
|
#
|
||||||
|
@ -289,3 +289,466 @@ a
|
|||||||
30
|
30
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# End of MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# MDEV-10597 Cursors with parameters
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# OPEN with a wrong number of parameters
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
CREATE PROCEDURE p1(a_a INT,a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
DBMS_OUTPUT.PUT_LINE('Fetched a record a='||TO_CHAR(v_a)||' b='||v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
ERROR 42000: Incorrect parameter count to cursor 'c'
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# Cursor parameters are not visible outside of the cursor
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
p_a:=10;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
ERROR HY000: Unknown system variable 'p_a'
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
p_a:=10;
|
||||||
|
OPEN c(a_a);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
ERROR HY000: Unknown system variable 'p_a'
|
||||||
|
#
|
||||||
|
# Cursor parameter shadowing a local variable
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
INSERT INTO t1 VALUES (1);
|
||||||
|
CREATE PROCEDURE p1(a INT)
|
||||||
|
AS
|
||||||
|
v_a INT:=NULL;
|
||||||
|
p_a INT:=NULL;
|
||||||
|
CURSOR c (p_a VARCHAR2) IS SELECT a FROM t1 WHERE p_a IS NOT NULL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a);
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
IF c%NOTFOUND THEN
|
||||||
|
BEGIN
|
||||||
|
SELECT 'No records found' AS msg;
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
END IF;
|
||||||
|
CLOSE c;
|
||||||
|
SELECT 'Fetched a record a='||v_a AS msg;
|
||||||
|
INSERT INTO t1 VALUES (v_a);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1);
|
||||||
|
msg
|
||||||
|
Fetched a record a=1
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
1
|
||||||
|
CALL p1(NULL);
|
||||||
|
msg
|
||||||
|
No records found
|
||||||
|
SELECT * FROM t1;
|
||||||
|
a
|
||||||
|
1
|
||||||
|
1
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# Parameters in SELECT list
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT p_a,p_b FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 0..1
|
||||||
|
LOOP
|
||||||
|
OPEN c(a_a + i,a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1,'b1');
|
||||||
|
msg
|
||||||
|
Fetched a record a=1 b=b1
|
||||||
|
msg
|
||||||
|
Fetched a record a=2 b=b1
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Parameters in SELECT list + UNION
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS
|
||||||
|
SELECT p_a,p_b FROM DUAL
|
||||||
|
UNION ALL
|
||||||
|
SELECT p_a+1,p_b||'b' FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a,a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1,'b1');
|
||||||
|
msg
|
||||||
|
Fetched a record a=1 b=b1
|
||||||
|
msg
|
||||||
|
Fetched a record a=2 b=b1b
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Parameters in SELECT list + type conversion + warnings
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT p_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('1b');
|
||||||
|
msg
|
||||||
|
Fetched a record a=1
|
||||||
|
Warnings:
|
||||||
|
Warning 1265 Data truncated for column 'p_a' at row 1
|
||||||
|
CALL p1('b1');
|
||||||
|
msg
|
||||||
|
Fetched a record a=0
|
||||||
|
Warnings:
|
||||||
|
Warning 1366 Incorrect integer value: 'b1' for column 'p_a' at row 1
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# One parameter in SELECT list + subselect
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
CURSOR c (p_a VARCHAR) IS
|
||||||
|
SELECT p_a FROM DUAL UNION SELECT REVERSE(p_a) FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT a_a));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('ab');
|
||||||
|
v_a
|
||||||
|
ab
|
||||||
|
v_a
|
||||||
|
ba
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Two parameters in SELECT list + subselect
|
||||||
|
#
|
||||||
|
SET sql_mode=ORACLE;
|
||||||
|
CREATE PROCEDURE p1()
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (p_a VARCHAR, p_b VARCHAR) IS
|
||||||
|
SELECT p_a, p_b FROM DUAL
|
||||||
|
UNION
|
||||||
|
SELECT p_b, p_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT 'aaa'),(SELECT 'bbb'));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1();
|
||||||
|
v_a v_b
|
||||||
|
aaa bbb
|
||||||
|
v_a v_b
|
||||||
|
bbb aaa
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Two parameters in SELECT list + two parameters in WHERE + subselects
|
||||||
|
#
|
||||||
|
SET sql_mode=ORACLE;
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (value_a VARCHAR, value_b VARCHAR,
|
||||||
|
pattern_a VARCHAR, pattern_b VARCHAR) IS
|
||||||
|
SELECT value_a, value_b FROM DUAL WHERE value_a LIKE pattern_a
|
||||||
|
UNION
|
||||||
|
SELECT value_b, value_a FROM DUAL WHERE value_b LIKE pattern_b;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT 'aaa'),(SELECT 'bbb'),(SELECT a_a),(SELECT a_b));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('%','%');
|
||||||
|
v_a v_b
|
||||||
|
aaa bbb
|
||||||
|
v_a v_b
|
||||||
|
bbb aaa
|
||||||
|
CALL p1('aaa','xxx');
|
||||||
|
v_a v_b
|
||||||
|
aaa bbb
|
||||||
|
CALL p1('xxx','bbb');
|
||||||
|
v_a v_b
|
||||||
|
bbb aaa
|
||||||
|
CALL p1('xxx','xxx');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Parameters in SELECT list + stored function
|
||||||
|
#
|
||||||
|
CREATE FUNCTION f1 (a VARCHAR) RETURN VARCHAR
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
RETURN a || 'y';
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_sel_a VARCHAR, p_cmp_a VARCHAR) IS
|
||||||
|
SELECT p_sel_a, p_cmp_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(f1(a_a), f1(a_a));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('x');
|
||||||
|
v_a
|
||||||
|
xy
|
||||||
|
CALL p1(f1(COALESCE(NULL, f1('x'))));
|
||||||
|
v_a
|
||||||
|
xyyy
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
#
|
||||||
|
# One parameter in WHERE clause
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'11');
|
||||||
|
INSERT INTO t1 VALUES (1,'12');
|
||||||
|
INSERT INTO t1 VALUES (2,'21');
|
||||||
|
INSERT INTO t1 VALUES (2,'22');
|
||||||
|
INSERT INTO t1 VALUES (3,'31');
|
||||||
|
INSERT INTO t1 VALUES (3,'32');
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT) IS SELECT a,b FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1);
|
||||||
|
SELECT * FROM t2;
|
||||||
|
a b
|
||||||
|
1 11
|
||||||
|
1 12
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Two parameters in WHERE clause
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'11');
|
||||||
|
INSERT INTO t1 VALUES (1,'12');
|
||||||
|
INSERT INTO t1 VALUES (2,'21');
|
||||||
|
INSERT INTO t1 VALUES (2,'22');
|
||||||
|
INSERT INTO t1 VALUES (3,'31');
|
||||||
|
INSERT INTO t1 VALUES (3,'32');
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT a,b FROM t1 WHERE a=p_a AND b=p_b;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a, a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1,'11');
|
||||||
|
SELECT * FROM t2;
|
||||||
|
a b
|
||||||
|
1 11
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# Parameters in WHERE and HAVING clauses
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (name VARCHAR(10), value INT);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('bin',1);
|
||||||
|
INSERT INTO t1 VALUES ('bin',1);
|
||||||
|
INSERT INTO t1 VALUES ('bot',1);
|
||||||
|
CREATE PROCEDURE p1 (arg_name_limit VARCHAR, arg_total_limit INT)
|
||||||
|
AS
|
||||||
|
v_name VARCHAR(10);
|
||||||
|
v_total INT;
|
||||||
|
-- +0 is needed to work around the bug MDEV-11081
|
||||||
|
CURSOR c(p_v INT) IS
|
||||||
|
SELECT name, SUM(value + p_v) + 0 AS total FROM t1
|
||||||
|
WHERE name LIKE arg_name_limit
|
||||||
|
GROUP BY name HAVING total>=arg_total_limit;
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 0..1
|
||||||
|
LOOP
|
||||||
|
OPEN c(i);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_name, v_total;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_name, v_total;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1('%', 2);
|
||||||
|
v_name v_total
|
||||||
|
bin 2
|
||||||
|
v_name v_total
|
||||||
|
but 3
|
||||||
|
v_name v_total
|
||||||
|
bin 4
|
||||||
|
v_name v_total
|
||||||
|
bot 2
|
||||||
|
v_name v_total
|
||||||
|
but 6
|
||||||
|
CALL p1('b_t', 0);
|
||||||
|
v_name v_total
|
||||||
|
bot 1
|
||||||
|
v_name v_total
|
||||||
|
but 3
|
||||||
|
v_name v_total
|
||||||
|
bot 2
|
||||||
|
v_name v_total
|
||||||
|
but 6
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# One parameter in LIMIT clause
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'b1');
|
||||||
|
INSERT INTO t1 VALUES (2,'b2');
|
||||||
|
INSERT INTO t1 VALUES (3,'b3');
|
||||||
|
INSERT INTO t1 VALUES (4,'b4');
|
||||||
|
INSERT INTO t1 VALUES (5,'b5');
|
||||||
|
INSERT INTO t1 VALUES (6,'b6');
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT) IS SELECT a,b FROM t1 ORDER BY a LIMIT p_a;
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
DROP TABLE t2;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CALL p1(1);
|
||||||
|
a b
|
||||||
|
1 b1
|
||||||
|
CALL p1(3);
|
||||||
|
a b
|
||||||
|
1 b1
|
||||||
|
2 b2
|
||||||
|
3 b3
|
||||||
|
CALL p1(6);
|
||||||
|
a b
|
||||||
|
1 b1
|
||||||
|
2 b2
|
||||||
|
3 b3
|
||||||
|
4 b4
|
||||||
|
5 b5
|
||||||
|
6 b6
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
#
|
||||||
|
# End of MDEV-10597 Cursors with parameters
|
||||||
|
#
|
||||||
|
@ -615,3 +615,45 @@ SHOW FUNCTION CODE f1;
|
|||||||
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
|
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
|
||||||
DROP FUNCTION f1;
|
DROP FUNCTION f1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Start of MDEV-10597 Cursors with parameters
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(arg_value_a VARCHAR, arg_value_b VARCHAR,
|
||||||
|
arg_pattern_a VARCHAR, arg_pattern_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (p_value_a VARCHAR,
|
||||||
|
p_value_b VARCHAR,
|
||||||
|
p_pattern_a VARCHAR,
|
||||||
|
p_pattern_b VARCHAR,
|
||||||
|
p_limit_a INT,
|
||||||
|
p_limit_b INT,
|
||||||
|
p_unused TEXT) IS
|
||||||
|
(SELECT p_value_a, p_value_b FROM DUAL
|
||||||
|
WHERE p_value_a LIKE p_pattern_a LIMIT p_limit_a)
|
||||||
|
UNION
|
||||||
|
(SELECT p_value_b, p_value_a FROM DUAL
|
||||||
|
WHERE p_value_b LIKE p_pattern_b LIMIT p_limit_b);
|
||||||
|
BEGIN
|
||||||
|
OPEN c(arg_value_a, (SELECT arg_value_b),
|
||||||
|
arg_pattern_a, arg_pattern_b, 100, 101, 'x');
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('aaa','bbb','aaa','bbb');
|
||||||
|
SHOW PROCEDURE CODE p1;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of MDEV-10597 Cursors with parameters
|
||||||
|
--echo #
|
||||||
|
@ -294,3 +294,459 @@ DELIMITER ;$$
|
|||||||
CALL p1();
|
CALL p1();
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10597 Cursors with parameters
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # OPEN with a wrong number of parameters
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
DELIMITER $$;
|
||||||
|
--error ER_WRONG_PARAMCOUNT_TO_CURSOR
|
||||||
|
CREATE PROCEDURE p1(a_a INT,a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
DBMS_OUTPUT.PUT_LINE('Fetched a record a='||TO_CHAR(v_a)||' b='||v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Cursor parameters are not visible outside of the cursor
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
--error ER_UNKNOWN_SYSTEM_VARIABLE
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
p_a:=10;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
--error ER_UNKNOWN_SYSTEM_VARIABLE
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
p_a:=10;
|
||||||
|
OPEN c(a_a);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Cursor parameter shadowing a local variable
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
INSERT INTO t1 VALUES (1);
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a INT)
|
||||||
|
AS
|
||||||
|
v_a INT:=NULL;
|
||||||
|
p_a INT:=NULL;
|
||||||
|
CURSOR c (p_a VARCHAR2) IS SELECT a FROM t1 WHERE p_a IS NOT NULL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a);
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
IF c%NOTFOUND THEN
|
||||||
|
BEGIN
|
||||||
|
SELECT 'No records found' AS msg;
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
END IF;
|
||||||
|
CLOSE c;
|
||||||
|
SELECT 'Fetched a record a='||v_a AS msg;
|
||||||
|
INSERT INTO t1 VALUES (v_a);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
CALL p1(NULL);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Parameters in SELECT list
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT p_a,p_b FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 0..1
|
||||||
|
LOOP
|
||||||
|
OPEN c(a_a + i,a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1,'b1');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Parameters in SELECT list + UNION
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS
|
||||||
|
SELECT p_a,p_b FROM DUAL
|
||||||
|
UNION ALL
|
||||||
|
SELECT p_a+1,p_b||'b' FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a,a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1,'b1');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Parameters in SELECT list + type conversion + warnings
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
CURSOR c (p_a INT) IS SELECT p_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT 'Fetched a record a=' || v_a AS msg;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('1b');
|
||||||
|
CALL p1('b1');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # One parameter in SELECT list + subselect
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
CURSOR c (p_a VARCHAR) IS
|
||||||
|
SELECT p_a FROM DUAL UNION SELECT REVERSE(p_a) FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT a_a));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('ab');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Two parameters in SELECT list + subselect
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET sql_mode=ORACLE;
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1()
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (p_a VARCHAR, p_b VARCHAR) IS
|
||||||
|
SELECT p_a, p_b FROM DUAL
|
||||||
|
UNION
|
||||||
|
SELECT p_b, p_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT 'aaa'),(SELECT 'bbb'));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1();
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Two parameters in SELECT list + two parameters in WHERE + subselects
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET sql_mode=ORACLE;
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(20);
|
||||||
|
CURSOR c (value_a VARCHAR, value_b VARCHAR,
|
||||||
|
pattern_a VARCHAR, pattern_b VARCHAR) IS
|
||||||
|
SELECT value_a, value_b FROM DUAL WHERE value_a LIKE pattern_a
|
||||||
|
UNION
|
||||||
|
SELECT value_b, value_a FROM DUAL WHERE value_b LIKE pattern_b;
|
||||||
|
BEGIN
|
||||||
|
OPEN c((SELECT 'aaa'),(SELECT 'bbb'),(SELECT a_a),(SELECT a_b));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a, v_b;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('%','%');
|
||||||
|
CALL p1('aaa','xxx');
|
||||||
|
CALL p1('xxx','bbb');
|
||||||
|
CALL p1('xxx','xxx');
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Parameters in SELECT list + stored function
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE FUNCTION f1 (a VARCHAR) RETURN VARCHAR
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
RETURN a || 'y';
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
CREATE PROCEDURE p1(a_a VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a VARCHAR(10);
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_sel_a VARCHAR, p_cmp_a VARCHAR) IS
|
||||||
|
SELECT p_sel_a, p_cmp_a FROM DUAL;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(f1(a_a), f1(a_a));
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_a;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('x');
|
||||||
|
# A complex expression
|
||||||
|
CALL p1(f1(COALESCE(NULL, f1('x'))));
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP FUNCTION f1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # One parameter in WHERE clause
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'11');
|
||||||
|
INSERT INTO t1 VALUES (1,'12');
|
||||||
|
INSERT INTO t1 VALUES (2,'21');
|
||||||
|
INSERT INTO t1 VALUES (2,'22');
|
||||||
|
INSERT INTO t1 VALUES (3,'31');
|
||||||
|
INSERT INTO t1 VALUES (3,'32');
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT) IS SELECT a,b FROM t1 WHERE a=p_a;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1);
|
||||||
|
SELECT * FROM t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Two parameters in WHERE clause
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'11');
|
||||||
|
INSERT INTO t1 VALUES (1,'12');
|
||||||
|
INSERT INTO t1 VALUES (2,'21');
|
||||||
|
INSERT INTO t1 VALUES (2,'22');
|
||||||
|
INSERT INTO t1 VALUES (3,'31');
|
||||||
|
INSERT INTO t1 VALUES (3,'32');
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT a,b FROM t1 WHERE a=p_a AND b=p_b;
|
||||||
|
BEGIN
|
||||||
|
OPEN c(a_a, a_b);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1,'11');
|
||||||
|
SELECT * FROM t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Parameters in WHERE and HAVING clauses
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t1 (name VARCHAR(10), value INT);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('but',1);
|
||||||
|
INSERT INTO t1 VALUES ('bin',1);
|
||||||
|
INSERT INTO t1 VALUES ('bin',1);
|
||||||
|
INSERT INTO t1 VALUES ('bot',1);
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1 (arg_name_limit VARCHAR, arg_total_limit INT)
|
||||||
|
AS
|
||||||
|
v_name VARCHAR(10);
|
||||||
|
v_total INT;
|
||||||
|
-- +0 is needed to work around the bug MDEV-11081
|
||||||
|
CURSOR c(p_v INT) IS
|
||||||
|
SELECT name, SUM(value + p_v) + 0 AS total FROM t1
|
||||||
|
WHERE name LIKE arg_name_limit
|
||||||
|
GROUP BY name HAVING total>=arg_total_limit;
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 0..1
|
||||||
|
LOOP
|
||||||
|
OPEN c(i);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_name, v_total;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
SELECT v_name, v_total;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1('%', 2);
|
||||||
|
CALL p1('b_t', 0);
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # One parameter in LIMIT clause
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
||||||
|
INSERT INTO t1 VALUES (1,'b1');
|
||||||
|
INSERT INTO t1 VALUES (2,'b2');
|
||||||
|
INSERT INTO t1 VALUES (3,'b3');
|
||||||
|
INSERT INTO t1 VALUES (4,'b4');
|
||||||
|
INSERT INTO t1 VALUES (5,'b5');
|
||||||
|
INSERT INTO t1 VALUES (6,'b6');
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p1(a_a INT)
|
||||||
|
AS
|
||||||
|
v_a INT;
|
||||||
|
v_b VARCHAR(10);
|
||||||
|
CURSOR c (p_a INT) IS SELECT a,b FROM t1 ORDER BY a LIMIT p_a;
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE t2 (a INT, b VARCHAR(10));
|
||||||
|
OPEN c(a_a);
|
||||||
|
LOOP
|
||||||
|
FETCH c INTO v_a, v_b;
|
||||||
|
EXIT WHEN c%NOTFOUND;
|
||||||
|
INSERT INTO t2 VALUES (v_a,v_b);
|
||||||
|
END LOOP;
|
||||||
|
CLOSE c;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
DROP TABLE t2;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
CALL p1(1);
|
||||||
|
CALL p1(3);
|
||||||
|
CALL p1(6);
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of MDEV-10597 Cursors with parameters
|
||||||
|
--echo #
|
||||||
|
@ -7457,3 +7457,6 @@ ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
|
|||||||
eng "Illegal parameter data types %s and %s for operation '%s'"
|
eng "Illegal parameter data types %s and %s for operation '%s'"
|
||||||
ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
|
ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
|
||||||
eng "Illegal parameter data type %s for operation '%s'"
|
eng "Illegal parameter data type %s for operation '%s'"
|
||||||
|
ER_WRONG_PARAMCOUNT_TO_CURSOR 42000
|
||||||
|
eng "Incorrect parameter count to cursor '%-.192s'"
|
||||||
|
rus "Некорректное количество параметров для курсора '%-.192s'"
|
||||||
|
@ -2066,30 +2066,16 @@ bool
|
|||||||
sp_head::reset_lex(THD *thd)
|
sp_head::reset_lex(THD *thd)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("sp_head::reset_lex");
|
DBUG_ENTER("sp_head::reset_lex");
|
||||||
LEX *sublex;
|
|
||||||
LEX *oldlex= thd->lex;
|
LEX *oldlex= thd->lex;
|
||||||
|
|
||||||
sublex= new (thd->mem_root)st_lex_local;
|
sp_lex_local *sublex= new (thd->mem_root) sp_lex_local(thd, oldlex);
|
||||||
if (sublex == 0)
|
if (sublex == 0)
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
thd->lex= sublex;
|
thd->set_local_lex(sublex);
|
||||||
|
|
||||||
(void)m_lex.push_front(oldlex);
|
(void)m_lex.push_front(oldlex);
|
||||||
|
|
||||||
/* Reset most stuff. */
|
|
||||||
lex_start(thd);
|
|
||||||
|
|
||||||
/* And keep the SP stuff too */
|
|
||||||
sublex->sphead= oldlex->sphead;
|
|
||||||
sublex->spcont= oldlex->spcont;
|
|
||||||
/* And trigger related stuff too */
|
|
||||||
sublex->trg_chistics= oldlex->trg_chistics;
|
|
||||||
sublex->trg_table_fields.empty();
|
|
||||||
sublex->sp_lex_in_use= FALSE;
|
|
||||||
|
|
||||||
/* Reset part of parser state which needs this. */
|
|
||||||
thd->m_parser_state->m_yacc.reset_before_substatement();
|
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2098,6 +2084,8 @@ sp_head::reset_lex(THD *thd)
|
|||||||
Restore lex during parsing, after we have parsed a sub statement.
|
Restore lex during parsing, after we have parsed a sub statement.
|
||||||
|
|
||||||
@param thd Thread handle
|
@param thd Thread handle
|
||||||
|
@param oldlex The upper level lex we're near to restore to
|
||||||
|
@param sublex The local lex we're near to restore from
|
||||||
|
|
||||||
@return
|
@return
|
||||||
@retval TRUE failure
|
@retval TRUE failure
|
||||||
@ -2105,23 +2093,17 @@ sp_head::reset_lex(THD *thd)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sp_head::restore_lex(THD *thd)
|
sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("sp_head::restore_lex");
|
DBUG_ENTER("sp_head::merge_lex");
|
||||||
LEX *sublex= thd->lex;
|
|
||||||
LEX *oldlex;
|
|
||||||
|
|
||||||
sublex->set_trg_event_type_for_tables();
|
sublex->set_trg_event_type_for_tables();
|
||||||
|
|
||||||
oldlex= (LEX *)m_lex.pop();
|
|
||||||
if (! oldlex)
|
|
||||||
DBUG_RETURN(FALSE); // Nothing to restore
|
|
||||||
|
|
||||||
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
||||||
|
|
||||||
/* If this substatement is unsafe, the entire routine is too. */
|
/* If this substatement is unsafe, the entire routine is too. */
|
||||||
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags: 0x%x",
|
DBUG_PRINT("info", ("sublex->get_stmt_unsafe_flags: 0x%x",
|
||||||
thd->lex->get_stmt_unsafe_flags()));
|
sublex->get_stmt_unsafe_flags()));
|
||||||
unsafe_flags|= sublex->get_stmt_unsafe_flags();
|
unsafe_flags|= sublex->get_stmt_unsafe_flags();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2143,13 +2125,6 @@ sp_head::restore_lex(THD *thd)
|
|||||||
/* Merge lists of PS parameters. */
|
/* Merge lists of PS parameters. */
|
||||||
oldlex->param_list.append(&sublex->param_list);
|
oldlex->param_list.append(&sublex->param_list);
|
||||||
|
|
||||||
if (! sublex->sp_lex_in_use)
|
|
||||||
{
|
|
||||||
sublex->sphead= NULL;
|
|
||||||
lex_end(sublex);
|
|
||||||
delete sublex;
|
|
||||||
}
|
|
||||||
thd->lex= oldlex;
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4197,3 +4172,59 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Helper action for a SET statement.
|
||||||
|
Used to push a SP local variable into the assignment list.
|
||||||
|
|
||||||
|
@param var_type the SP local variable
|
||||||
|
@param val the value being assigned to the variable
|
||||||
|
|
||||||
|
@return TRUE if error, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
|
||||||
|
sp_variable *spv, Item *val, LEX *lex)
|
||||||
|
{
|
||||||
|
Item *it;
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
it= val;
|
||||||
|
else if (spv->default_value)
|
||||||
|
it= spv->default_value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it= new (thd->mem_root) Item_null(thd);
|
||||||
|
if (it == NULL)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp_instr_set *sp_set= new (thd->mem_root)
|
||||||
|
sp_instr_set(instructions(), spcont,
|
||||||
|
spv->offset, it, spv->sql_type(),
|
||||||
|
lex, true);
|
||||||
|
|
||||||
|
return sp_set == NULL || add_instr(sp_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
|
||||||
|
sp_pcontext *param_spcont,
|
||||||
|
List<sp_assignment_lex> *parameters)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The caller must make sure that the number of formal parameters matches
|
||||||
|
the number of actual parameters.
|
||||||
|
*/
|
||||||
|
DBUG_ASSERT((param_spcont ? param_spcont->context_var_count() : 0) ==
|
||||||
|
(parameters ? parameters->elements : 0));
|
||||||
|
|
||||||
|
if (parameters &&
|
||||||
|
add_set_cursor_param_variables(thd, param_spcont, parameters))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
sp_instr_copen *i= new (thd->mem_root)
|
||||||
|
sp_instr_copen(instructions(), spcont, offset);
|
||||||
|
return i == NULL || add_instr(i);
|
||||||
|
}
|
||||||
|
111
sql/sp_head.h
111
sql/sp_head.h
@ -353,6 +353,85 @@ public:
|
|||||||
spcont->last_label());
|
spcont->last_label());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool set_local_variable(THD *thd, sp_pcontext *spcont,
|
||||||
|
sp_variable *spv, Item *val, LEX *lex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
Generate a code to set a single cursor parameter variable.
|
||||||
|
@param thd - current thd, for mem_root allocations.
|
||||||
|
@param param_spcont - the context of the parameter block
|
||||||
|
@param idx - the index of the parameter
|
||||||
|
@param prm - the actual parameter (contains information about
|
||||||
|
the assignment source expression Item,
|
||||||
|
its free list, and its LEX)
|
||||||
|
*/
|
||||||
|
bool add_set_cursor_param_variable(THD *thd,
|
||||||
|
sp_pcontext *param_spcont, uint idx,
|
||||||
|
sp_assignment_lex *prm)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(idx < param_spcont->context_var_count());
|
||||||
|
sp_variable *spvar= param_spcont->find_context_variable(idx);
|
||||||
|
/*
|
||||||
|
add_instr() gets free_list from m_thd->free_list.
|
||||||
|
Initialize it before the set_local_variable() call.
|
||||||
|
*/
|
||||||
|
DBUG_ASSERT(m_thd->free_list == NULL);
|
||||||
|
m_thd->free_list= prm->get_free_list();
|
||||||
|
if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm))
|
||||||
|
return true;
|
||||||
|
/*
|
||||||
|
Safety:
|
||||||
|
The item and its free_list are now fully owned by the sp_instr_set
|
||||||
|
instance, created by set_local_variable(). The sp_instr_set instance
|
||||||
|
is now responsible for freeing the item and the free_list.
|
||||||
|
Reset the "item" and the "free_list" members of "prm",
|
||||||
|
to avoid double pointers to the same objects from "prm" and
|
||||||
|
from the sp_instr_set instance.
|
||||||
|
*/
|
||||||
|
prm->set_item_and_free_list(NULL, NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate a code to set all cursor parameter variables.
|
||||||
|
This method is called only when parameters exists,
|
||||||
|
and the number of formal parameters matches the number of actual
|
||||||
|
parameters. See also comments to add_open_cursor().
|
||||||
|
*/
|
||||||
|
bool add_set_cursor_param_variables(THD *thd, sp_pcontext *param_spcont,
|
||||||
|
List<sp_assignment_lex> *parameters)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(param_spcont->context_var_count() == parameters->elements);
|
||||||
|
sp_assignment_lex *prm;
|
||||||
|
List_iterator<sp_assignment_lex> li(*parameters);
|
||||||
|
for (uint idx= 0; (prm= li++); idx++)
|
||||||
|
{
|
||||||
|
if (add_set_cursor_param_variable(thd, param_spcont, idx, prm))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Generate a code for an "OPEN cursor" statement.
|
||||||
|
@param thd - current thd, for mem_root allocations
|
||||||
|
@param spcont - the context of the cursor
|
||||||
|
@param offset - the offset of the cursor
|
||||||
|
@param param_spcont - the context of the cursor parameter block
|
||||||
|
@param parameters - the list of the OPEN actual parameters
|
||||||
|
|
||||||
|
The caller must make sure that the number of local variables
|
||||||
|
in "param_spcont" (formal parameters) matches the number of list elements
|
||||||
|
in "parameters" (actual parameters).
|
||||||
|
NULL in either of them means 0 parameters.
|
||||||
|
*/
|
||||||
|
bool add_open_cursor(THD *thd, sp_pcontext *spcont,
|
||||||
|
uint offset,
|
||||||
|
sp_pcontext *param_spcont,
|
||||||
|
List<sp_assignment_lex> *parameters);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns true if any substatement in the routine directly
|
Returns true if any substatement in the routine directly
|
||||||
@ -385,6 +464,20 @@ public:
|
|||||||
bool
|
bool
|
||||||
reset_lex(THD *thd);
|
reset_lex(THD *thd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Merge two LEX instances.
|
||||||
|
@param oldlex - the upper level LEX we're going to restore to.
|
||||||
|
@param sublex - the local lex that have just parsed some substatement.
|
||||||
|
@returns - false on success, true on error (e.g. failed to
|
||||||
|
merge the routine list or the table list).
|
||||||
|
This method is shared by:
|
||||||
|
- restore_lex(), when the old LEX is popped by sp_head::m_lex.pop()
|
||||||
|
- THD::restore_from_local_lex_to_old_lex(), when the old LEX
|
||||||
|
is stored in the caller's local variable.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
merge_lex(THD *thd, LEX *oldlex, LEX *sublex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Restores lex in 'thd' from our copy, but keeps some status from the
|
Restores lex in 'thd' from our copy, but keeps some status from the
|
||||||
one in 'thd', like ptr, tables, fields, etc.
|
one in 'thd', like ptr, tables, fields, etc.
|
||||||
@ -392,7 +485,23 @@ public:
|
|||||||
@todo Conflicting comment in sp_head.cc
|
@todo Conflicting comment in sp_head.cc
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
restore_lex(THD *thd);
|
restore_lex(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("sp_head::restore_lex");
|
||||||
|
LEX *oldlex= (LEX *) m_lex.pop();
|
||||||
|
if (!oldlex)
|
||||||
|
DBUG_RETURN(false); // Nothing to restore
|
||||||
|
LEX *sublex= thd->lex;
|
||||||
|
if (thd->restore_from_local_lex_to_old_lex(oldlex))// This restores thd->lex
|
||||||
|
DBUG_RETURN(true);
|
||||||
|
if (!sublex->sp_lex_in_use)
|
||||||
|
{
|
||||||
|
sublex->sphead= NULL;
|
||||||
|
lex_end(sublex);
|
||||||
|
delete sublex;
|
||||||
|
}
|
||||||
|
DBUG_RETURN(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// Put the instruction on the backpatch list, associated with the label.
|
/// Put the instruction on the backpatch list, associated with the label.
|
||||||
int
|
int
|
||||||
|
@ -498,18 +498,18 @@ sp_pcontext::find_handler(const Sql_condition_identity &value) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool sp_pcontext::add_cursor(const LEX_STRING name)
|
bool sp_pcontext::add_cursor(const LEX_STRING name, sp_pcontext *param_ctx)
|
||||||
{
|
{
|
||||||
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(name);
|
return m_cursors.append(sp_pcursor(name, param_ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool sp_pcontext::find_cursor(const LEX_STRING name,
|
const sp_pcursor *sp_pcontext::find_cursor(const LEX_STRING name,
|
||||||
uint *poff,
|
uint *poff,
|
||||||
bool current_scope_only) const
|
bool current_scope_only) const
|
||||||
{
|
{
|
||||||
uint i= m_cursors.elements();
|
uint i= m_cursors.elements();
|
||||||
|
|
||||||
@ -522,13 +522,13 @@ bool sp_pcontext::find_cursor(const LEX_STRING name,
|
|||||||
(const uchar *) n.str, n.length) == 0)
|
(const uchar *) n.str, n.length) == 0)
|
||||||
{
|
{
|
||||||
*poff= m_cursor_offset + i;
|
*poff= m_cursor_offset + i;
|
||||||
return true;
|
return &m_cursors.at(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!current_scope_only && m_parent) ?
|
return (!current_scope_only && m_parent) ?
|
||||||
m_parent->find_cursor(name, poff, false) :
|
m_parent->find_cursor(name, poff, false) :
|
||||||
false;
|
NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -551,7 +551,7 @@ void sp_pcontext::retrieve_field_definitions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
|
const sp_pcursor *sp_pcontext::find_cursor(uint offset) const
|
||||||
{
|
{
|
||||||
if (m_cursor_offset <= offset &&
|
if (m_cursor_offset <= offset &&
|
||||||
offset < m_cursor_offset + m_cursors.elements())
|
offset < m_cursor_offset + m_cursors.elements())
|
||||||
|
@ -247,6 +247,36 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
class sp_pcursor.
|
||||||
|
Stores information about a cursor:
|
||||||
|
- Cursor's name in LEX_STRING.
|
||||||
|
- Cursor's formal parameter descriptions.
|
||||||
|
|
||||||
|
Formal parameter descriptions reside in a separate context block,
|
||||||
|
pointed by the "m_param_context" member.
|
||||||
|
|
||||||
|
m_param_context can be NULL. This means a cursor with no parameters.
|
||||||
|
Otherwise, the number of variables in m_param_context means
|
||||||
|
the number of cursor's formal parameters.
|
||||||
|
|
||||||
|
Note, m_param_context can be not NULL, but have no variables.
|
||||||
|
This is also means a cursor with no parameters (similar to NULL).
|
||||||
|
*/
|
||||||
|
class sp_pcursor: public LEX_STRING
|
||||||
|
{
|
||||||
|
class sp_pcontext *m_param_context; // Formal parameters
|
||||||
|
public:
|
||||||
|
sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx)
|
||||||
|
:LEX_STRING(name), m_param_context(param_ctx)
|
||||||
|
{ }
|
||||||
|
class sp_pcontext *param_context() const { return m_param_context; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// This class represents 'DECLARE HANDLER' statement.
|
/// This class represents 'DECLARE HANDLER' statement.
|
||||||
@ -383,6 +413,13 @@ public:
|
|||||||
uint context_var_count() const
|
uint context_var_count() const
|
||||||
{ return m_vars.elements(); }
|
{ return m_vars.elements(); }
|
||||||
|
|
||||||
|
/// return the i-th variable on the current context
|
||||||
|
sp_variable *find_context_variable(uint i) const
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(i < m_vars.elements());
|
||||||
|
return m_vars.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
/// @return map index in this parsing context to runtime offset.
|
/// @return map index in this parsing context to runtime offset.
|
||||||
uint var_context2runtime(uint i) const
|
uint var_context2runtime(uint i) const
|
||||||
{ return m_var_offset + i; }
|
{ return m_var_offset + i; }
|
||||||
@ -558,14 +595,14 @@ public:
|
|||||||
// Cursors.
|
// Cursors.
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool add_cursor(const LEX_STRING name);
|
bool add_cursor(const LEX_STRING name, sp_pcontext *param_ctx);
|
||||||
|
|
||||||
/// See comment for find_variable() above.
|
/// See comment for find_variable() above.
|
||||||
bool find_cursor(const LEX_STRING name,
|
const sp_pcursor *find_cursor(const LEX_STRING name,
|
||||||
uint *poff, bool current_scope_only) const;
|
uint *poff, bool current_scope_only) const;
|
||||||
|
|
||||||
/// Find cursor by offset (for debugging only).
|
/// Find cursor by offset (for SHOW {PROCEDURE|FUNCTION} CODE only).
|
||||||
const LEX_STRING *find_cursor(uint offset) const;
|
const sp_pcursor *find_cursor(uint offset) const;
|
||||||
|
|
||||||
uint max_cursor_index() const
|
uint max_cursor_index() const
|
||||||
{ return m_max_cursor_index + m_cursors.elements(); }
|
{ return m_max_cursor_index + m_cursors.elements(); }
|
||||||
@ -641,7 +678,7 @@ private:
|
|||||||
Dynamic_array<sp_condition *> m_conditions;
|
Dynamic_array<sp_condition *> m_conditions;
|
||||||
|
|
||||||
/// Stack of cursors.
|
/// Stack of cursors.
|
||||||
Dynamic_array<LEX_STRING> m_cursors;
|
Dynamic_array<sp_pcursor> m_cursors;
|
||||||
|
|
||||||
/// Stack of SQL-handlers.
|
/// Stack of SQL-handlers.
|
||||||
Dynamic_array<sp_handler *> m_handlers;
|
Dynamic_array<sp_handler *> m_handlers;
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
#include <mysys_err.h>
|
#include <mysys_err.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "sp_head.h"
|
||||||
#include "sp_rcontext.h"
|
#include "sp_rcontext.h"
|
||||||
#include "sp_cache.h"
|
#include "sp_cache.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
@ -1323,7 +1324,17 @@ void THD::init(void)
|
|||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool THD::restore_from_local_lex_to_old_lex(LEX *oldlex)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(lex->sphead);
|
||||||
|
if (lex->sphead->merge_lex(this, oldlex, lex))
|
||||||
|
return true;
|
||||||
|
lex= oldlex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Updates some status variables to be used by update_global_user_stats */
|
/* Updates some status variables to be used by update_global_user_stats */
|
||||||
|
|
||||||
void THD::update_stats(void)
|
void THD::update_stats(void)
|
||||||
|
@ -4358,6 +4358,31 @@ public:
|
|||||||
current_linfo= 0;
|
current_linfo= 0;
|
||||||
mysql_mutex_unlock(&LOCK_thread_count);
|
mysql_mutex_unlock(&LOCK_thread_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Switch to a sublex, to parse a substatement or an expression.
|
||||||
|
*/
|
||||||
|
void set_local_lex(sp_lex_local *sublex)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(lex->sphead);
|
||||||
|
lex= sublex;
|
||||||
|
/* Reset part of parser state which needs this. */
|
||||||
|
m_parser_state->m_yacc.reset_before_substatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Switch back from a sublex (currently pointed by this->lex) to the old lex.
|
||||||
|
Sublex is merged to "oldlex" and this->lex is set to "oldlex".
|
||||||
|
|
||||||
|
This method is called after parsing a substatement or an expression.
|
||||||
|
set_local_lex() must be previously called.
|
||||||
|
@param oldlex - The old lex which was active before set_local_lex().
|
||||||
|
@returns - false on success, true on error (failed to merge LEX's).
|
||||||
|
|
||||||
|
See also sp_head::merge_lex().
|
||||||
|
*/
|
||||||
|
bool restore_from_local_lex_to_old_lex(LEX *oldlex);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void add_to_active_threads(THD *thd)
|
inline void add_to_active_threads(THD *thd)
|
||||||
|
236
sql/sql_lex.cc
236
sql/sql_lex.cc
@ -652,116 +652,121 @@ void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lex_start(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("lex_start");
|
||||||
|
thd->lex->start(thd);
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is called before every query that is to be parsed.
|
This is called before every query that is to be parsed.
|
||||||
Because of this, it's critical to not do too much things here.
|
Because of this, it's critical to not do too much things here.
|
||||||
(We already do too much here)
|
(We already do too much here)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void lex_start(THD *thd)
|
void LEX::start(THD *thd_arg)
|
||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
DBUG_ENTER("LEX::start");
|
||||||
DBUG_ENTER("lex_start");
|
|
||||||
|
|
||||||
lex->thd= lex->unit.thd= thd;
|
thd= unit.thd= thd_arg;
|
||||||
|
|
||||||
DBUG_ASSERT(!lex->explain);
|
DBUG_ASSERT(!explain);
|
||||||
|
|
||||||
lex->context_stack.empty();
|
context_stack.empty();
|
||||||
lex->unit.init_query();
|
unit.init_query();
|
||||||
lex->unit.init_select();
|
unit.init_select();
|
||||||
lex->select_lex.linkage= UNSPECIFIED_TYPE;
|
select_lex.linkage= UNSPECIFIED_TYPE;
|
||||||
/* 'parent_lex' is used in init_query() so it must be before it. */
|
/* 'parent_lex' is used in init_query() so it must be before it. */
|
||||||
lex->select_lex.parent_lex= lex;
|
select_lex.parent_lex= this;
|
||||||
lex->select_lex.init_query();
|
select_lex.init_query();
|
||||||
lex->curr_with_clause= 0;
|
curr_with_clause= 0;
|
||||||
lex->with_clauses_list= 0;
|
with_clauses_list= 0;
|
||||||
lex->with_clauses_list_last_next= &lex->with_clauses_list;
|
with_clauses_list_last_next= &with_clauses_list;
|
||||||
lex->value_list.empty();
|
value_list.empty();
|
||||||
lex->update_list.empty();
|
update_list.empty();
|
||||||
lex->set_var_list.empty();
|
set_var_list.empty();
|
||||||
lex->param_list.empty();
|
param_list.empty();
|
||||||
lex->view_list.empty();
|
view_list.empty();
|
||||||
lex->with_column_list.empty();
|
with_column_list.empty();
|
||||||
lex->with_persistent_for_clause= FALSE;
|
with_persistent_for_clause= FALSE;
|
||||||
lex->column_list= NULL;
|
column_list= NULL;
|
||||||
lex->index_list= NULL;
|
index_list= NULL;
|
||||||
lex->prepared_stmt_params.empty();
|
prepared_stmt_params.empty();
|
||||||
lex->auxiliary_table_list.empty();
|
auxiliary_table_list.empty();
|
||||||
lex->unit.next= lex->unit.master=
|
unit.next= unit.master= unit.link_next= unit.return_to= 0;
|
||||||
lex->unit.link_next= lex->unit.return_to= 0;
|
unit.prev= unit.link_prev= 0;
|
||||||
lex->unit.prev= lex->unit.link_prev= 0;
|
unit.slave= current_select= all_selects_list= &select_lex;
|
||||||
lex->unit.slave= lex->current_select=
|
select_lex.master= &unit;
|
||||||
lex->all_selects_list= &lex->select_lex;
|
select_lex.prev= &unit.slave;
|
||||||
lex->select_lex.master= &lex->unit;
|
select_lex.link_next= select_lex.slave= select_lex.next= 0;
|
||||||
lex->select_lex.prev= &lex->unit.slave;
|
select_lex.link_prev= (st_select_lex_node**)&(all_selects_list);
|
||||||
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
|
select_lex.options= 0;
|
||||||
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
|
select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
|
||||||
lex->select_lex.options= 0;
|
select_lex.init_order();
|
||||||
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
|
select_lex.group_list.empty();
|
||||||
lex->select_lex.init_order();
|
if (select_lex.group_list_ptrs)
|
||||||
lex->select_lex.group_list.empty();
|
select_lex.group_list_ptrs->clear();
|
||||||
if (lex->select_lex.group_list_ptrs)
|
describe= 0;
|
||||||
lex->select_lex.group_list_ptrs->clear();
|
analyze_stmt= 0;
|
||||||
lex->describe= 0;
|
explain_json= false;
|
||||||
lex->analyze_stmt= 0;
|
subqueries= FALSE;
|
||||||
lex->explain_json= false;
|
context_analysis_only= 0;
|
||||||
lex->subqueries= FALSE;
|
derived_tables= 0;
|
||||||
lex->context_analysis_only= 0;
|
safe_to_cache_query= 1;
|
||||||
lex->derived_tables= 0;
|
parsing_options.reset();
|
||||||
lex->safe_to_cache_query= 1;
|
empty_field_list_on_rset= 0;
|
||||||
lex->parsing_options.reset();
|
select_lex.select_number= 1;
|
||||||
lex->empty_field_list_on_rset= 0;
|
part_info= 0;
|
||||||
lex->select_lex.select_number= 1;
|
select_lex.in_sum_expr=0;
|
||||||
lex->part_info= 0;
|
select_lex.ftfunc_list_alloc.empty();
|
||||||
lex->select_lex.in_sum_expr=0;
|
select_lex.ftfunc_list= &select_lex.ftfunc_list_alloc;
|
||||||
lex->select_lex.ftfunc_list_alloc.empty();
|
select_lex.group_list.empty();
|
||||||
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
|
select_lex.order_list.empty();
|
||||||
lex->select_lex.group_list.empty();
|
select_lex.gorder_list.empty();
|
||||||
lex->select_lex.order_list.empty();
|
m_sql_cmd= NULL;
|
||||||
lex->select_lex.gorder_list.empty();
|
duplicates= DUP_ERROR;
|
||||||
lex->m_sql_cmd= NULL;
|
ignore= 0;
|
||||||
lex->duplicates= DUP_ERROR;
|
spname= NULL;
|
||||||
lex->ignore= 0;
|
spcont= NULL;
|
||||||
lex->spname= NULL;
|
proc_list.first= 0;
|
||||||
lex->spcont= NULL;
|
escape_used= FALSE;
|
||||||
lex->proc_list.first= 0;
|
query_tables= 0;
|
||||||
lex->escape_used= FALSE;
|
reset_query_tables_list(FALSE);
|
||||||
lex->query_tables= 0;
|
expr_allows_subselect= TRUE;
|
||||||
lex->reset_query_tables_list(FALSE);
|
use_only_table_context= FALSE;
|
||||||
lex->expr_allows_subselect= TRUE;
|
parse_vcol_expr= FALSE;
|
||||||
lex->use_only_table_context= FALSE;
|
check_exists= FALSE;
|
||||||
lex->parse_vcol_expr= FALSE;
|
create_info.lex_start();
|
||||||
lex->check_exists= FALSE;
|
verbose= 0;
|
||||||
lex->create_info.lex_start();
|
|
||||||
lex->verbose= 0;
|
|
||||||
|
|
||||||
lex->name= null_lex_str;
|
name= null_lex_str;
|
||||||
lex->event_parse_data= NULL;
|
event_parse_data= NULL;
|
||||||
lex->profile_options= PROFILE_NONE;
|
profile_options= PROFILE_NONE;
|
||||||
lex->nest_level=0 ;
|
nest_level=0 ;
|
||||||
lex->select_lex.nest_level_base= &lex->unit;
|
select_lex.nest_level_base= &unit;
|
||||||
lex->allow_sum_func= 0;
|
allow_sum_func= 0;
|
||||||
lex->in_sum_func= NULL;
|
in_sum_func= NULL;
|
||||||
|
|
||||||
lex->used_tables= 0;
|
used_tables= 0;
|
||||||
lex->only_view= FALSE;
|
only_view= FALSE;
|
||||||
lex->reset_slave_info.all= false;
|
reset_slave_info.all= false;
|
||||||
lex->limit_rows_examined= 0;
|
limit_rows_examined= 0;
|
||||||
lex->limit_rows_examined_cnt= ULONGLONG_MAX;
|
limit_rows_examined_cnt= ULONGLONG_MAX;
|
||||||
lex->var_list.empty();
|
var_list.empty();
|
||||||
lex->stmt_var_list.empty();
|
stmt_var_list.empty();
|
||||||
lex->proc_list.elements=0;
|
proc_list.elements=0;
|
||||||
|
|
||||||
lex->save_group_list.empty();
|
save_group_list.empty();
|
||||||
lex->save_order_list.empty();
|
save_order_list.empty();
|
||||||
lex->win_ref= NULL;
|
win_ref= NULL;
|
||||||
lex->win_frame= NULL;
|
win_frame= NULL;
|
||||||
lex->frame_top_bound= NULL;
|
frame_top_bound= NULL;
|
||||||
lex->frame_bottom_bound= NULL;
|
frame_bottom_bound= NULL;
|
||||||
lex->win_spec= NULL;
|
win_spec= NULL;
|
||||||
|
|
||||||
lex->is_lex_started= TRUE;
|
is_lex_started= TRUE;
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5355,7 +5360,8 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
|
|||||||
if (!inc)
|
if (!inc)
|
||||||
return true;
|
return true;
|
||||||
Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
|
Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
|
||||||
if (!expr || set_local_variable(loop.m_index, expr))
|
if (!expr ||
|
||||||
|
sphead->set_local_variable(thd, spcont, loop.m_index, expr, this))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -5379,7 +5385,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, LEX *cursor_stmt,
|
||||||
|
sp_pcontext *param_ctx)
|
||||||
{
|
{
|
||||||
uint offp;
|
uint offp;
|
||||||
sp_instr_cpush *i;
|
sp_instr_cpush *i;
|
||||||
@ -5392,7 +5399,38 @@ bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt)
|
|||||||
i= new (thd->mem_root)
|
i= new (thd->mem_root)
|
||||||
sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt,
|
sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt,
|
||||||
spcont->current_cursor_count());
|
spcont->current_cursor_count());
|
||||||
return i == NULL || sphead->add_instr(i) || spcont->add_cursor(name);
|
return i == NULL ||
|
||||||
|
sphead->add_instr(i) ||
|
||||||
|
spcont->add_cursor(name, param_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate an SP code for an "OPEN cursor_name" statement.
|
||||||
|
@param thd
|
||||||
|
@param name - Name of the cursor
|
||||||
|
@param parameters - Cursor parameters, e.g. OPEN c(1,2,3)
|
||||||
|
@returns - false on success, true on error
|
||||||
|
*/
|
||||||
|
bool LEX::sp_open_cursor(THD *thd, const LEX_STRING name,
|
||||||
|
List<sp_assignment_lex> *parameters)
|
||||||
|
{
|
||||||
|
uint offset;
|
||||||
|
const sp_pcursor *pcursor;
|
||||||
|
if (!(pcursor= spcont->find_cursor(name, &offset, false)))
|
||||||
|
{
|
||||||
|
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name.str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((pcursor->param_context() ?
|
||||||
|
pcursor->param_context()->context_var_count() : 0) !=
|
||||||
|
(parameters ? parameters->elements : 0))
|
||||||
|
{
|
||||||
|
my_error(ER_WRONG_PARAMCOUNT_TO_CURSOR, MYF(0), name.str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return sphead->add_open_cursor(thd, spcont, offset,
|
||||||
|
pcursor->param_context(), parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -5947,7 +5985,7 @@ bool LEX::set_variable(struct sys_var_with_base *variable, Item *item)
|
|||||||
sp_variable *spv= spcont->find_variable(variable->base_name, false);
|
sp_variable *spv= spcont->find_variable(variable->base_name, false);
|
||||||
DBUG_ASSERT(spv);
|
DBUG_ASSERT(spv);
|
||||||
/* It is a local variable. */
|
/* It is a local variable. */
|
||||||
return set_local_variable(spv, item);
|
return sphead->set_local_variable(thd, spcont, spv, item, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ class sp_name;
|
|||||||
class sp_instr;
|
class sp_instr;
|
||||||
class sp_pcontext;
|
class sp_pcontext;
|
||||||
class sp_variable;
|
class sp_variable;
|
||||||
|
class sp_assignment_lex;
|
||||||
class st_alter_tablespace;
|
class st_alter_tablespace;
|
||||||
class partition_info;
|
class partition_info;
|
||||||
class Event_parse_data;
|
class Event_parse_data;
|
||||||
@ -765,7 +766,7 @@ public:
|
|||||||
|
|
||||||
void set_unique_exclude();
|
void set_unique_exclude();
|
||||||
|
|
||||||
friend void lex_start(THD *thd);
|
friend struct LEX;
|
||||||
friend int subselect_union_engine::exec();
|
friend int subselect_union_engine::exec();
|
||||||
|
|
||||||
List<Item> *get_column_types(bool for_cursor);
|
List<Item> *get_column_types(bool for_cursor);
|
||||||
@ -1072,7 +1073,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
ha_rows get_limit();
|
ha_rows get_limit();
|
||||||
|
|
||||||
friend void lex_start(THD *thd);
|
friend struct LEX;
|
||||||
st_select_lex() : group_list_ptrs(NULL), braces(0), automatic_brackets(0),
|
st_select_lex() : group_list_ptrs(NULL), braces(0), automatic_brackets(0),
|
||||||
n_sum_items(0), n_child_sum_items(0)
|
n_sum_items(0), n_child_sum_items(0)
|
||||||
{}
|
{}
|
||||||
@ -2920,6 +2921,8 @@ public:
|
|||||||
delete_dynamic(&plugins);
|
delete_dynamic(&plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void start(THD *thd);
|
||||||
|
|
||||||
inline bool is_ps_or_view_context_analysis()
|
inline bool is_ps_or_view_context_analysis()
|
||||||
{
|
{
|
||||||
return (context_analysis_only &
|
return (context_analysis_only &
|
||||||
@ -3112,8 +3115,10 @@ 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, LEX *cursor_stmt,
|
||||||
bool set_local_variable(sp_variable *spv, Item *val);
|
sp_pcontext *param_ctx);
|
||||||
|
bool sp_open_cursor(THD *thd, const LEX_STRING name,
|
||||||
|
List<sp_assignment_lex> *parameters);
|
||||||
Item_splocal *create_item_for_sp_var(LEX_STRING name, sp_variable *spvar,
|
Item_splocal *create_item_for_sp_var(LEX_STRING name, sp_variable *spvar,
|
||||||
const char *start_in_q,
|
const char *start_in_q,
|
||||||
const char *end_in_q);
|
const char *end_in_q);
|
||||||
@ -3507,6 +3512,92 @@ struct st_lex_local: public LEX, public Sql_alloc
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An st_lex_local extension with automatic initialization for SP purposes.
|
||||||
|
Used to parse sub-expressions and SP sub-statements.
|
||||||
|
|
||||||
|
This class is reused for:
|
||||||
|
1. sp_head::reset_lex() based constructs
|
||||||
|
- SP variable assignments (e.g. SET x=10;)
|
||||||
|
- FOR loop conditions and index variable increments
|
||||||
|
- Cursor statements
|
||||||
|
- SP statements
|
||||||
|
- SP function RETURN statements
|
||||||
|
- CASE statements
|
||||||
|
- REPEAT..UNTIL expressions
|
||||||
|
- WHILE expressions
|
||||||
|
- EXIT..WHEN and CONTINUE..WHEN statements
|
||||||
|
2. sp_assignment_lex based constructs:
|
||||||
|
- CURSOR parameter assignments
|
||||||
|
*/
|
||||||
|
class sp_lex_local: public st_lex_local
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sp_lex_local(THD *thd, const LEX *oldlex)
|
||||||
|
{
|
||||||
|
/* Reset most stuff. */
|
||||||
|
start(thd);
|
||||||
|
/* Keep the parent SP stuff */
|
||||||
|
sphead= oldlex->sphead;
|
||||||
|
spcont= oldlex->spcont;
|
||||||
|
/* Keep the parent trigger stuff too */
|
||||||
|
trg_chistics= oldlex->trg_chistics;
|
||||||
|
trg_table_fields.empty();
|
||||||
|
sp_lex_in_use= false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An assignment specific LEX, which additionally has an Item (an expression)
|
||||||
|
and an associated with the Item free_list, which is usually freed
|
||||||
|
after the expression is calculated.
|
||||||
|
|
||||||
|
Note, consider changing some of sp_lex_local to sp_assignment_lex,
|
||||||
|
as the latter allows to use a simpler grammar in sql_yacc.yy (IMO).
|
||||||
|
|
||||||
|
If the expression is simple (e.g. does not have function calls),
|
||||||
|
then m_item and m_free_list point to the same Item.
|
||||||
|
|
||||||
|
If the expressions is complex (e.g. have function calls),
|
||||||
|
then m_item points to the leftmost Item, while m_free_list points
|
||||||
|
to the rightmost item.
|
||||||
|
For example:
|
||||||
|
f1(COALESCE(f2(10), f2(20)))
|
||||||
|
- m_item points to Item_func_sp for f1 (the leftmost Item)
|
||||||
|
- m_free_list points to Item_int for 20 (the rightmost Item)
|
||||||
|
|
||||||
|
Note, we could avoid storing m_item at all, as we can always reach
|
||||||
|
the leftmost item from the rightmost item by iterating through m_free_list.
|
||||||
|
But with a separate m_item the code should be faster.
|
||||||
|
*/
|
||||||
|
class sp_assignment_lex: public sp_lex_local
|
||||||
|
{
|
||||||
|
Item *m_item; // The expression
|
||||||
|
Item *m_free_list; // The associated free_list (sub-expressions)
|
||||||
|
public:
|
||||||
|
sp_assignment_lex(THD *thd, LEX *oldlex)
|
||||||
|
:sp_lex_local(thd, oldlex),
|
||||||
|
m_item(NULL),
|
||||||
|
m_free_list(NULL)
|
||||||
|
{ }
|
||||||
|
void set_item_and_free_list(Item *item, Item *free_list)
|
||||||
|
{
|
||||||
|
m_item= item;
|
||||||
|
m_free_list= free_list;
|
||||||
|
}
|
||||||
|
Item *get_item() const
|
||||||
|
{
|
||||||
|
return m_item;
|
||||||
|
}
|
||||||
|
Item *get_free_list() const
|
||||||
|
{
|
||||||
|
return m_free_list;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
extern void lex_init(void);
|
extern void lex_init(void);
|
||||||
extern void lex_free(void);
|
extern void lex_free(void);
|
||||||
extern void lex_start(THD *thd);
|
extern void lex_start(THD *thd);
|
||||||
|
@ -309,42 +309,6 @@ LEX::set_system_variable(struct sys_var_with_base *tmp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Helper action for a SET statement.
|
|
||||||
Used to push a SP local variable into the assignment list.
|
|
||||||
|
|
||||||
@param var_type the SP local variable
|
|
||||||
@param val the value being assigned to the variable
|
|
||||||
|
|
||||||
@return TRUE if error, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool
|
|
||||||
LEX::set_local_variable(sp_variable *spv, Item *val)
|
|
||||||
{
|
|
||||||
Item *it;
|
|
||||||
sp_instr_set *sp_set;
|
|
||||||
|
|
||||||
if (val)
|
|
||||||
it= val;
|
|
||||||
else if (spv->default_value)
|
|
||||||
it= spv->default_value;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it= new (thd->mem_root) Item_null(thd);
|
|
||||||
if (it == NULL)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp_set= new (thd->mem_root)
|
|
||||||
sp_instr_set(sphead->instructions(), spcont,
|
|
||||||
spv->offset, it, spv->sql_type(),
|
|
||||||
this, true);
|
|
||||||
|
|
||||||
return (sp_set == NULL || sphead->add_instr(sp_set));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Helper action for a SET statement.
|
Helper action for a SET statement.
|
||||||
Used to SET a field of NEW row.
|
Used to SET a field of NEW row.
|
||||||
@ -3001,7 +2965,7 @@ sp_decl_body:
|
|||||||
}
|
}
|
||||||
| ident CURSOR_SYM FOR_SYM sp_cursor_stmt
|
| ident CURSOR_SYM FOR_SYM sp_cursor_stmt
|
||||||
{
|
{
|
||||||
if (Lex->sp_declare_cursor(thd, $1, $4))
|
if (Lex->sp_declare_cursor(thd, $1, $4, NULL))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
$$.vars= $$.conds= $$.hndlrs= 0;
|
$$.vars= $$.conds= $$.hndlrs= 0;
|
||||||
$$.curs= 1;
|
$$.curs= 1;
|
||||||
@ -3570,17 +3534,7 @@ sp_proc_stmt_iterate:
|
|||||||
sp_proc_stmt_open:
|
sp_proc_stmt_open:
|
||||||
OPEN_SYM ident
|
OPEN_SYM ident
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
if (Lex->sp_open_cursor(thd, $2, NULL))
|
||||||
sp_head *sp= lex->sphead;
|
|
||||||
uint offset;
|
|
||||||
sp_instr_copen *i;
|
|
||||||
|
|
||||||
if (! lex->spcont->find_cursor($2, &offset, false))
|
|
||||||
my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str));
|
|
||||||
i= new (thd->mem_root)
|
|
||||||
sp_instr_copen(sp->instructions(), lex->spcont, offset);
|
|
||||||
if (i == NULL ||
|
|
||||||
sp->add_instr(i))
|
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -195,11 +195,13 @@ void ORAerror(THD *thd, const char *s)
|
|||||||
Item_param *item_param;
|
Item_param *item_param;
|
||||||
Key_part_spec *key_part;
|
Key_part_spec *key_part;
|
||||||
LEX *lex;
|
LEX *lex;
|
||||||
|
sp_assignment_lex *assignment_lex;
|
||||||
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;
|
||||||
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
|
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
|
||||||
List<Item> *item_list;
|
List<Item> *item_list;
|
||||||
|
List<sp_assignment_lex> *sp_assignment_lex_list;
|
||||||
List<Statement_information_item> *stmt_info_list;
|
List<Statement_information_item> *stmt_info_list;
|
||||||
List<String> *string_list;
|
List<String> *string_list;
|
||||||
List<LEX_STRING> *lex_str_list;
|
List<LEX_STRING> *lex_str_list;
|
||||||
@ -266,10 +268,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||||||
%parse-param { THD *thd }
|
%parse-param { THD *thd }
|
||||||
%lex-param { THD *thd }
|
%lex-param { THD *thd }
|
||||||
/*
|
/*
|
||||||
Currently there are 103 shift/reduce conflicts.
|
Currently there are 102 shift/reduce conflicts.
|
||||||
We should not introduce new conflicts any more.
|
We should not introduce new conflicts any more.
|
||||||
*/
|
*/
|
||||||
%expect 103
|
%expect 102
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Comments for TOKENS.
|
Comments for TOKENS.
|
||||||
@ -1131,6 +1133,14 @@ 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 <assignment_lex>
|
||||||
|
assignment_source_lex
|
||||||
|
assignment_source_expr
|
||||||
|
|
||||||
|
%type <sp_assignment_lex_list>
|
||||||
|
cursor_actual_parameters
|
||||||
|
opt_parenthesized_cursor_actual_parameters
|
||||||
|
|
||||||
%type <var_type>
|
%type <var_type>
|
||||||
option_type opt_var_type opt_var_ident_type
|
option_type opt_var_type opt_var_ident_type
|
||||||
|
|
||||||
@ -1302,7 +1312,7 @@ 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
|
%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
|
||||||
@ -2438,15 +2448,29 @@ sp_decl_body:
|
|||||||
$$.vars= $$.conds= $$.curs= 0;
|
$$.vars= $$.conds= $$.curs= 0;
|
||||||
$$.hndlrs= 1;
|
$$.hndlrs= 1;
|
||||||
}
|
}
|
||||||
| CURSOR_SYM ident_directly_assignable IS sp_cursor_stmt
|
| CURSOR_SYM ident_directly_assignable
|
||||||
{
|
{
|
||||||
if (Lex->sp_declare_cursor(thd, $2, $4))
|
Lex->sp_block_init(thd);
|
||||||
|
}
|
||||||
|
opt_parenthesized_cursor_formal_parameters
|
||||||
|
IS sp_cursor_stmt
|
||||||
|
{
|
||||||
|
sp_pcontext *param_ctx= Lex->spcont;
|
||||||
|
if (Lex->sp_block_finalize(thd))
|
||||||
|
MYSQL_YYABORT;
|
||||||
|
if (Lex->sp_declare_cursor(thd, $2, $6, param_ctx))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
$$.vars= $$.conds= $$.hndlrs= 0;
|
$$.vars= $$.conds= $$.hndlrs= 0;
|
||||||
$$.curs= 1;
|
$$.curs= 1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
opt_parenthesized_cursor_formal_parameters:
|
||||||
|
/* Empty */
|
||||||
|
| '(' sp_fdparams ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
sp_cursor_stmt:
|
sp_cursor_stmt:
|
||||||
{
|
{
|
||||||
Lex->sphead->reset_lex(thd);
|
Lex->sphead->reset_lex(thd);
|
||||||
@ -3095,20 +3119,60 @@ sp_proc_stmt_iterate:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
sp_proc_stmt_open:
|
remember_lex:
|
||||||
OPEN_SYM ident
|
|
||||||
{
|
{
|
||||||
LEX *lex= Lex;
|
$$= thd->lex;
|
||||||
sp_head *sp= lex->sphead;
|
}
|
||||||
uint offset;
|
;
|
||||||
sp_instr_copen *i;
|
|
||||||
|
|
||||||
if (! lex->spcont->find_cursor($2, &offset, false))
|
assignment_source_lex:
|
||||||
my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str));
|
{
|
||||||
i= new (thd->mem_root)
|
DBUG_ASSERT(Lex->sphead);
|
||||||
sp_instr_copen(sp->instructions(), lex->spcont, offset);
|
if (!($$= new (thd->mem_root) sp_assignment_lex(thd, thd->lex)))
|
||||||
if (i == NULL ||
|
MYSQL_YYABORT;
|
||||||
sp->add_instr(i))
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
assignment_source_expr:
|
||||||
|
remember_lex assignment_source_lex
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(thd->free_list == NULL);
|
||||||
|
thd->set_local_lex($2); // This changes thd->lex to $2
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
{
|
||||||
|
DBUG_ASSERT($2 == thd->lex);
|
||||||
|
if (thd->restore_from_local_lex_to_old_lex($1)) // Restores thd->lex
|
||||||
|
MYSQL_YYABORT;
|
||||||
|
$$= $2;
|
||||||
|
$$->set_item_and_free_list($4, thd->free_list);
|
||||||
|
thd->free_list= NULL;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
cursor_actual_parameters:
|
||||||
|
assignment_source_expr
|
||||||
|
{
|
||||||
|
if (!($$= new (thd->mem_root) List<sp_assignment_lex>))
|
||||||
|
MYSQL_YYABORT;
|
||||||
|
$$->push_back($1, thd->mem_root);
|
||||||
|
}
|
||||||
|
| cursor_actual_parameters ',' assignment_source_expr
|
||||||
|
{
|
||||||
|
$$= $1;
|
||||||
|
$$->push_back($3, thd->mem_root);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_parenthesized_cursor_actual_parameters:
|
||||||
|
/* Empty */ { $$= NULL; }
|
||||||
|
| '(' cursor_actual_parameters ')' { $$= $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
sp_proc_stmt_open:
|
||||||
|
OPEN_SYM ident opt_parenthesized_cursor_actual_parameters
|
||||||
|
{
|
||||||
|
if (Lex->sp_open_cursor(thd, $2, $3))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
Reference in New Issue
Block a user