1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-21 21:22:27 +03:00
Files
mariadb/mysql-test/suite/compat/oracle/t/sp-code.test
Alexander Barkov f429b5a834 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.
2017-04-05 15:02:59 +04:00

792 lines
12 KiB
Plaintext

-- source include/have_debug.inc
SET sql_mode=ORACLE;
--echo #
--echo # Testing exceptions in the top-level blocks
--echo #
--echo # No HANDLER declarations, no exceptions
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
AS
BEGIN
RETURN 10;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1();
DROP FUNCTION f1;
--echo # No HANDLER declarations, no code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 ()
IS
BEGIN
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo # No HANDLER declarations, no code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
EXCEPTION
WHEN 1002 THEN v:=225;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # No HANDLER declarations, some code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=224;
EXCEPTION
WHEN 1002 THEN v:=225;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, no code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
EXIT HANDLER FOR 1000
BEGIN
v:=123;
END;
BEGIN
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, no code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
EXIT HANDLER FOR 1000
BEGIN
v:=123;
END;
BEGIN
EXCEPTION
WHEN 1002 THEN v:=225;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, some code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
EXIT HANDLER FOR 1000
BEGIN
v:=123;
END;
BEGIN
v:=223;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, some code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT VARCHAR2(20))
IS
EXIT HANDLER FOR 1000
BEGIN
v:=123;
END;
CONTINUE HANDLER FOR 1001
BEGIN
SET v=223;
END;
BEGIN
v:= 1;
EXCEPTION
WHEN 1002 THEN SET v=225;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Testing EXCEPTIONS in internal blocks
--echo #
--echo # No HANDLER declarations, no code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
BEGIN
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # No HANDLER declarations, no code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
BEGIN
EXCEPTION
WHEN 20002 THEN v:=335;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # No HANDLER declarations, some code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
BEGIN
v:=223;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # No HANDLER declarations, some code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
BEGIN
v:=223;
EXCEPTION
WHEN 20002 THEN v:=335;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, no code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
DECLARE
EXIT HANDLER FOR 1000
BEGIN
v:=323;
END;
BEGIN
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, no code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
DECLARE
EXIT HANDLER FOR 1000
BEGIN
v:=323;
END;
BEGIN
EXCEPTION
WHEN 20002 THEN v:=335;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, some code, no exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
DECLARE
EXIT HANDLER FOR 1000
BEGIN
v:=323;
END;
BEGIN
v:= 324;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Some HANDLER declarations, some code, some exceptions
DELIMITER /;
CREATE PROCEDURE p1 (v IN OUT INT)
IS
BEGIN
v:=123;
DECLARE
EXIT HANDLER FOR 1000
BEGIN
v:=323;
END;
BEGIN
v:= 324;
EXCEPTION WHEN 2002 THEN v:= 325;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
SET @v=10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo #
--echo # Testing EXIT statement
--echo #
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
i:= i + 1;
EXIT WHEN i >=5;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
BEGIN
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN i:= 1000;
END;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE PROCEDURE p1(a IN OUT INT)
IS
i INT := 0;
BEGIN
LOOP
LOOP
BEGIN
i:= i + 1;
IF i >=5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN a:=1000;
END;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
a:= i;
EXCEPTION
WHEN OTHERS THEN a:=11;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
--echo # Testing RETURN in procedures
DELIMITER /;
CREATE PROCEDURE p1 (a IN OUT INT)
AS
BEGIN
IF a < 10 THEN
BEGIN
a:= a + 1;
RETURN;
END;
END IF;
a:= 200;
EXCEPTION
WHEN OTHERS THEN
BEGIN
a:= 100;
RETURN;
END;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo # Testing FOR loop statement
DELIMITER /;
CREATE FUNCTION f1 (a INT, b INT) RETURN INT
AS
total INT := 0;
BEGIN
FOR i IN 1 .. a
LOOP
total:= total + i;
IF i = b THEN
EXIT;
END IF;
END LOOP;
RETURN total;
END
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3, 100) FROM DUAL;
SELECT f1(3, 2) FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 (a INT, b INT) RETURN INT
AS
total INT := 0;
BEGIN
FOR i IN REVERSE a..1
LOOP
total:= total + i;
IF i = b THEN
EXIT;
END IF;
END LOOP;
RETURN total;
END
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3, 100) FROM DUAL;
SELECT f1(3, 2) FROM DUAL;
DROP FUNCTION f1;
--echo # Testing labeled FOR LOOP statement
DELIMITER /;
CREATE FUNCTION f1 (a INT, limita INT, b INT, limitb INT) RETURN INT
AS
total INT := 0;
BEGIN
<<la>>
FOR ia IN 1 .. a
LOOP
total:= total + 1000;
<<lb>>
FOR ib IN 1 .. b
LOOP
total:= total + 1;
EXIT lb WHEN ib = limitb;
EXIT la WHEN ia = limita;
END LOOP lb;
END LOOP la;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(2, 1, 2, 2) FROM DUAL;
SELECT f1(2, 2, 2, 2) FROM DUAL;
SELECT f1(2, 3, 2, 3) FROM DUAL;
DROP FUNCTION f1;
--echo # Testing labeled ITERATE in a labeled FOR LOOP
DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
FOR j IN 1 .. 2
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<lj>>
FOR j IN 1 .. 2
LOOP
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;
--echo # Testing CONTINUE statement
DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
FOR i IN 1 .. a
LOOP
CONTINUE WHEN i=5;
total:= total + 1;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
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 #
--echo #
--echo # MDEV-10914 ROW data type for stored routine variables
--echo #
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
AS
a ROW(a INT, b INT);
BEGIN
a.b:= 200;
RETURN a.b;
END;
$$
DELIMITER ;$$
SHOW FUNCTION CODE f1;
SELECT f1();
DROP FUNCTION f1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec:= ROW(10,20.123456,30.123,'test');
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
BEGIN
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec1:= ROW(10,20.123456,30.123,'test');
rec2:= rec1;
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) := rec1;
BEGIN
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #
--echo #
--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
rec1.c:= 10e2;
rec1.d:= 10.12;
rec1.c:= rec1.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
CURSOR cur1 IS SELECT * FROM t1;
CURSOR cur2 IS SELECT * FROM t1;
BEGIN
DECLARE
rec1,rec2 cur1%ROWTYPE;
rec3 cur2%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
END;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
DROP TABLE t1;