summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-goto.result834
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-goto.test872
-rw-r--r--sql/lex.h1
-rw-r--r--sql/sp_head.cc135
-rw-r--r--sql/sp_head.h29
-rw-r--r--sql/sp_pcontext.cc61
-rw-r--r--sql/sp_pcontext.h55
-rw-r--r--sql/sql_lex.cc48
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_yacc.yy2
-rw-r--r--sql/sql_yacc_ora.yy94
11 files changed, 2092 insertions, 41 deletions
diff --git a/mysql-test/suite/compat/oracle/r/sp-goto.result b/mysql-test/suite/compat/oracle/r/sp-goto.result
new file mode 100644
index 00000000000..259be7a34e6
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-goto.result
@@ -0,0 +1,834 @@
+set sql_mode=oracle;
+#
+# MDEV-10697 sql_mode=ORACLE: GOTO statement
+#
+# matrice of tests in procedure
+# |--------------------------------------------------------
+# | | Same | Outside | to sub | No |
+# | | block | one block | block | matching |
+# | | | | | label |
+# |--------------------------------------------------------
+# | Forward jump | F1 | F3 | F5 | F7 |
+# |--------------------------------------------------------
+# | Backward jump | F2 | F4 | F6 | F8 |
+# |--------------------------------------------------------
+# Jump from handler to outside handling code block : F9
+# Jump from handler to handling code block : F10 (forbidden)
+# Jump inside handler : F21
+# Jump between handler : F22 (forbidden)
+# Jump from cascaded block with handler : F11
+# Duplicate label in same block : F12 (forbidden)
+# Duplicate label in different block : F13
+# Jump outside unlabeled block : F14
+# Jump inside/outside labeled block : F15
+# Jump from if / else : F16
+# Jump with cursors : F17
+# Jump outside case : F18
+# Jump inside/outside case block : F19
+# Jump outside labeled loop : F20
+# Jump (continue) labeled loop : F23
+# Two consecutive label : P24
+# Two consecutive label (backward and forward jump) : P25
+# Two consecutive label, continue to wrong label : P26
+# Consecutive goto label and block label : P27
+# Test in function
+# backward jump : func1
+# forward jump : func2
+# Test in trigger
+# forward jump : trg1
+#
+# Forward jump in same block
+#
+CREATE or replace procedure f1(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+goto lab1;
+<<lab1>>
+goto lab2;
+p2:='b';
+<<lab2>>
+return ;
+END;
+$$
+call f1(@wp1);
+select 'f1',@wp1;
+f1 @wp1
+f1 a
+DROP PROCEDURE f1;
+#
+# Backward jump in same block
+#
+CREATE or replace procedure f2(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+<<lab1>>
+if (p2='b') then
+return ;
+end if;
+p2:='b';
+goto lab1;
+END;
+$$
+call f2(@wp1);
+select 'f2',@wp1;
+f2 @wp1
+f2 b
+DROP PROCEDURE f2;
+#
+# Forward jump outside one block
+#
+CREATE or replace procedure f3(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+if (p2='a') then
+goto lab1;
+end if;
+p2:='c';
+<<lab1>>
+return ;
+END;
+$$
+call f3(@wp1);
+select 'f3',@wp1;
+f3 @wp1
+f3 a
+DROP PROCEDURE f3;
+#
+# Backward jump outside one block
+#
+CREATE or replace procedure f4(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+<<lab1>>
+if (p2='a') then
+p2:=p2||'b';
+goto lab1;
+end if;
+if (p2='ab') then
+p2:=p2||'c';
+end if;
+return ;
+END;
+$$
+call f4(@wp1);
+select 'f4',@wp1;
+f4 @wp1
+f4 abc
+DROP PROCEDURE f4;
+#
+# Forward jump inside sub block
+CREATE or replace procedure f5(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+goto lab5 ;
+if (p2='a') then
+<<lab5>>
+p2:=p2||'b';
+end if;
+return ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: lab5
+#
+# Backward jump inside sub block
+CREATE or replace procedure f6(p2 IN OUT VARCHAR)
+AS
+BEGIN
+p2:='a';
+if (p2='a') then
+<<lab6>>
+p2:=p2||'b';
+return ;
+end if;
+goto lab6 ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: lab6
+#
+# Backward jump - missing label
+CREATE or replace procedure f7(p2 IN OUT VARCHAR)
+AS
+BEGIN
+<<lab>>
+goto lab7 ;
+return ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: lab7
+#
+# Forward jump - missing label
+CREATE or replace procedure f8(p2 IN OUT VARCHAR)
+AS
+BEGIN
+goto lab8 ;
+<<lab>>
+return ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: lab8
+#
+# Jump from handler to procedure code
+#
+CREATE or replace procedure f9(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+<<lab9>>
+if lim=-1 then
+res:=res||' -- goto end limit -1 --';
+goto lab9_end;
+end if;
+begin
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+res:=res||'--- too_many_rows cought ---';
+lim:=0;
+goto lab9;
+end;
+WHEN NO_DATA_FOUND THEN
+begin
+res:=res||'--- no_data_found cought ---';
+lim:=-1;
+goto lab9;
+end;
+end;
+res:=res||'error';
+<<lab9_end>>
+return ;
+END;
+$$
+SET @res='';
+CALL f9(2, @res);
+SELECT 'f9',@res;
+f9 @res
+f9 --- too_many_rows cought ------ no_data_found cought --- -- goto end limit -1 --
+CALL f9(0, @res);
+SELECT 'f9',@res;
+f9 @res
+f9 --- no_data_found cought --- -- goto end limit -1 --
+DROP PROCEDURE f9;
+#
+# Jump from handler to handling bloc
+CREATE or replace procedure f10(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+begin
+<<lab10>>
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+res:='--- too_many_rows cought ---';
+goto lab10;
+end;
+WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
+end;
+return ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: lab10
+#
+# Jump from cascaded block with handler
+#
+CREATE or replace procedure f11(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+<<lab11a>>
+begin
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+res:=res||'--- too_many_rows cought 1 ---';
+goto lab11b;
+end;
+WHEN NO_DATA_FOUND THEN
+begin
+res:=res||'--- no_data_found cought 1 ---';
+lim:=2;
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+res:=res||'--- too_many_rows cought 2 ---';
+goto lab11a;
+end;
+WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought 2 ---';
+end;
+end;
+set res:=res||' error ';
+<<lab11b>>
+return ;
+END;
+$$
+SET @res='';
+CALL f11(0, @res);
+SELECT 'f11',@res;
+f11 @res
+f11 --- no_data_found cought 1 ------ too_many_rows cought 2 ------ too_many_rows cought 1 ---
+DROP PROCEDURE f11;
+#
+# Jump inside handler
+#
+CREATE or replace procedure f21(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+begin
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+<<retry>>
+lim:=lim-1;
+loop
+begin
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+begin
+lim:=lim-1;
+goto retry;
+end;
+end;
+exit ;
+end loop;
+end;
+end;
+res:=lim;
+return ;
+END;
+$$
+SET @res='';
+CALL f21(10, @res);
+SELECT 'f21',@res;
+f21 @res
+f21 1
+drop procedure f21;
+#
+# Jump beetween handler
+CREATE or replace procedure f22(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+res:='ok';
+begin
+SELECT a INTO a FROM information_schema.tables LIMIT lim;
+EXCEPTION
+WHEN TOO_MANY_ROWS THEN
+goto nodata ;
+WHEN NO_DATA_FOUND THEN
+begin
+<<nodata>>
+res:='error';
+end;
+end;
+return ;
+END;
+$$
+ERROR 42000: GOTO with no matching label: nodata
+#
+# Duplicate label in same bloc
+CREATE or replace procedure f12(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+<<lab12>>
+res:='error';
+<<lab12>>
+return ;
+END;
+$$
+ERROR 42000: Redefining label lab12
+#
+# Duplicate label in different block
+#
+CREATE or replace procedure f13(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+a:=0;
+<<lab13>>
+a:=a+1;
+begin
+<<lab13>>
+a:=a+1;
+if (a<10) then
+goto lab13;
+end if;
+end;
+res:=a;
+if (a=10) then
+goto lab13;
+end if;
+return ;
+END;
+$$
+SET @res='';
+CALL f13(0, @res);
+SELECT 'f13',@res;
+f13 @res
+f13 12
+DROP PROCEDURE f13;
+#
+# Jump outside unlabeled block
+#
+CREATE or replace procedure f14(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+a:=0;
+loop
+a:=a+1;
+if (a<10) then
+continue;
+end if;
+if (a>=lim) then
+goto lab14;
+end if;
+if (a>=20) then
+exit;
+end if;
+end loop;
+<<lab14>>
+res:=a;
+return ;
+END;
+$$
+SET @res='';
+CALL f14(15, @res);
+SELECT 'f14',@res;
+f14 @res
+f14 15
+CALL f14(8, @res);
+SELECT 'f14',@res;
+f14 @res
+f14 10
+CALL f14(25, @res);
+SELECT 'f14',@res;
+f14 @res
+f14 20
+DROP PROCEDURE f14;
+#
+# Jump inside/outside labeled block
+#
+CREATE or replace procedure f15(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+a:=0;
+<<looplabel>> loop
+<<beginlooplabel>>
+a:=a+1;
+if (a<10) then
+continue looplabel;
+end if;
+if (a>=lim) then
+goto lab15;
+end if;
+if (a>=20) then
+exit looplabel;
+end if;
+goto beginlooplabel;
+end loop;
+<<lab15>>
+res:=a;
+return ;
+END;
+$$
+SET @res='';
+CALL f15(15, @res);
+SELECT 'f15',@res;
+f15 @res
+f15 15
+CALL f15(8, @res);
+SELECT 'f15',@res;
+f15 @res
+f15 10
+CALL f15(25, @res);
+SELECT 'f15',@res;
+f15 @res
+f15 20
+DROP PROCEDURE f15;
+#
+# Jump from if / else
+#
+CREATE or replace procedure f16(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+if (lim<10) then
+goto lab16_1;
+else
+goto lab16_2;
+end if;
+<<lab16_1>>
+res:='if lab16_1';
+goto lab16_3;
+<<lab16_2>>
+res:='else lab16_2';
+goto lab16_3;
+res:='error lab16_3';
+<<lab16_3>>
+return ;
+END;
+$$
+SET @res='';
+CALL f16(15, @res);
+SELECT 'f16',@res;
+f16 @res
+f16 else lab16_2
+CALL f16(8, @res);
+SELECT 'f16',@res;
+f16 @res
+f16 if lab16_1
+DROP PROCEDURE f16;
+#
+# Jump with cursors
+#
+CREATE or replace procedure f17(lim INT, res OUT VARCHAR)
+AS
+v_a INT;
+v_b VARCHAR(10);
+CURSOR cur1 IS SELECT 1 FROM dual where 1=2;
+BEGIN
+OPEN cur1;
+LOOP
+FETCH cur1 INTO v_a;
+EXIT WHEN cur1%NOTFOUND;
+END LOOP;
+CLOSE cur1;
+<<lab17>>
+lim:=lim-1;
+begin
+declare
+CURSOR cur1 IS SELECT 1 FROM dual;
+CURSOR cur2 IS SELECT 1 FROM dual where 1=2;
+begin
+LOOP
+OPEN cur1;
+FETCH cur1 INTO v_a;
+EXIT WHEN cur1%NOTFOUND;
+res:=res||'-'||lim ;
+close cur1;
+if (lim>0) then
+goto lab17;
+else
+goto lab17_end;
+end if;
+END LOOP;
+end;
+<<lab17_end>>
+null;
+end;
+END;
+$$
+SET @res='';
+CALL f17(5, @res);
+SELECT 'f17',@res;
+f17 @res
+f17 -4-3-2-1-0
+DROP PROCEDURE f17;
+#
+# Jump outside case
+#
+CREATE or replace procedure f18(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+case lim
+when 1 then
+res:='case branch 18_1';
+goto lab18_1;
+res:='error';
+when 2 then
+res:='case branch 18_2';
+goto lab18_2;
+res:='error';
+else
+res:='default branch 18';
+end case;
+<<lab18_1>>
+null;
+<<lab18_2>>
+return ;
+END;
+$$
+SET @res='';
+CALL f18(0, @res);
+SELECT 'f18',@res;
+f18 @res
+f18 default branch 18
+CALL f18(1, @res);
+SELECT 'f18',@res;
+f18 @res
+f18 case branch 18_1
+CALL f18(2, @res);
+SELECT 'f18',@res;
+f18 @res
+f18 case branch 18_2
+DROP PROCEDURE f18;
+#
+# Jump inside/outside case block
+#
+CREATE or replace procedure f19(lim INT, res OUT VARCHAR)
+AS
+a INT;
+BEGIN
+a:=1;
+case lim
+when 1 then
+<<lab19_0>>
+a:=a+1;
+if (a<10) then
+goto lab19_0;
+else
+goto lab19_1;
+end if;
+res:='case branch 19_1';
+else
+res:='default branch 18';
+end case;
+goto lab19_end;
+<<lab19_1>>
+res:=a;
+<<lab19_end>>
+return ;
+END;
+$$
+SET @res='';
+CALL f19(1, @res);
+SELECT 'f19',@res;
+f19 @res
+f19 10
+DROP PROCEDURE f19;
+#
+# Jump outside labeled loop
+#
+CREATE OR REPLACE PROCEDURE f20(res OUT VARCHAR)
+AS
+a INT := 1;
+BEGIN
+<<lab>>
+FOR i IN a..10 LOOP
+IF i = 5 THEN
+a:= a+1;
+goto lab;
+END IF;
+END LOOP;
+res:=a;
+END;
+$$
+CALL f20(@res);
+SELECT 'f20',@res;
+f20 @res
+f20 6
+DROP PROCEDURE f20;
+#
+# Jump (continue) labeled loop
+#
+CREATE OR REPLACE PROCEDURE f23(res OUT VARCHAR)
+AS
+a INT := 1;
+BEGIN
+<<lab>>
+FOR i IN a..10 LOOP
+IF i = 5 THEN
+a:= a+1;
+continue lab;
+END IF;
+END LOOP;
+res:=a;
+END;
+$$
+CALL f23(@res);
+SELECT 'f23',@res;
+f23 @res
+f23 2
+DROP PROCEDURE f23;
+#
+# Two consecutive label (backward jump)
+#
+CREATE OR REPLACE PROCEDURE p24(action IN INT, res OUT varchar) AS
+a integer;
+BEGIN
+<<lab1>>
+<<lab2>>
+if (action = 1) then
+res:=res||' '||action;
+action:=2;
+goto lab1;
+end if;
+if (action = 2) then
+res:=res||' '||action;
+action:=3;
+goto lab2;
+end if;
+END;
+$$
+call p24(1,@res);
+select 'p24',@res;
+p24 @res
+p24 1 2
+DROP PROCEDURE p24;
+#
+# Two consecutive label (backward and forward jump)
+#
+CREATE OR REPLACE PROCEDURE p25(action IN INT, res OUT varchar) AS
+a integer;
+BEGIN
+if (action = 1) then
+res:=res||' '||action;
+action:=2;
+goto lab2;
+end if;
+goto lab_end;
+<<lab1>>
+<<lab2>>
+res:=res||' '||action;
+if (action = 2) then
+res:=res||' '||action;
+action:=3;
+goto lab1;
+end if;
+<<lab_end>>
+null;
+END;
+$$
+call p25(1,@res);
+select 'p25',@res;
+p25 @res
+p25 1 2 2 3
+DROP PROCEDURE p25;
+#
+# Two consecutive label, continue to wrong label
+CREATE OR REPLACE PROCEDURE p26(action IN INT, res OUT varchar) AS
+BEGIN
+<<lab1>>
+<<lab2>>
+FOR i IN 1..10 LOOP
+continue lab1;
+END LOOP;
+END;
+$$
+ERROR 42000: CONTINUE with no matching label: lab1
+#
+# Consecutive goto label and block label
+#
+CREATE OR REPLACE PROCEDURE p27(action IN INT, res OUT varchar) AS
+BEGIN
+res:='';
+<<lab1>>
+<<lab2>>
+FOR i IN 1..10 LOOP
+if (action = 1) then
+res:=res||' '||action||'-'||i;
+action:=2;
+continue lab2;
+end if;
+if (action = 2) then
+res:=res||' '||action||'-'||i;
+action:='3';
+goto lab2;
+end if;
+if (action = 3) then
+res:=res||' '||action||'-'||i;
+action:='4';
+goto lab1;
+end if;
+if (action = 4) then
+res:=res||' '||action||'-'||i;
+exit lab2;
+end if;
+END LOOP;
+END;
+$$
+call p27(1,@res);
+select 'p27',@res;
+p27 @res
+p27 1-1 2-2 3-1 4-1
+DROP PROCEDURE p27;
+# ----------------------
+# -- TEST IN FUNCTION --
+# ----------------------
+#
+# FUNCTION : Backward jump
+#
+CREATE or replace function func1()
+return varchar
+AS
+p2 varchar(10);
+BEGIN
+p2:='a';
+<<lab1>>
+if (p2='a') then
+p2:=p2||'b';
+goto lab1;
+end if;
+if (p2='ab') then
+p2:=p2||'c';
+end if;
+return p2;
+END;
+$$
+select 'func1',func1();
+func1 func1()
+func1 abc
+DROP function func1;
+#
+# FUNCTION : forward jump
+#
+CREATE or replace function func2()
+return varchar
+AS
+p2 varchar(10);
+BEGIN
+p2:='a';
+if (p2='a') then
+goto lab1;
+end if;
+p2:='b';
+<<lab1>>
+return p2;
+END;
+$$
+select 'func2',func2();
+func2 func2()
+func2 a
+DROP function func2;
+# ---------------------
+# -- TEST IN TRIGGER --
+# ---------------------
+#
+# TRIGGER : forward jump
+#
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW
+BEGIN
+IF :NEW.a IS NULL
+THEN
+:NEW.a:= 15;
+goto end_trg;
+END IF;
+:NEW.a:= 10;
+<<end_trg>>
+null;
+END;
+$$
+insert into t1 values (1);
+insert into t1 values (null);
+SELECT * FROM t1;
+a
+10
+15
+DROP TRIGGER trg1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-goto.test b/mysql-test/suite/compat/oracle/t/sp-goto.test
new file mode 100644
index 00000000000..df7f1132666
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-goto.test
@@ -0,0 +1,872 @@
+set sql_mode=oracle;
+--echo #
+--echo # MDEV-10697 sql_mode=ORACLE: GOTO statement
+--echo #
+
+--echo # matrice of tests in procedure
+--echo # |--------------------------------------------------------
+--echo # | | Same | Outside | to sub | No |
+--echo # | | block | one block | block | matching |
+--echo # | | | | | label |
+--echo # |--------------------------------------------------------
+--echo # | Forward jump | F1 | F3 | F5 | F7 |
+--echo # |--------------------------------------------------------
+--echo # | Backward jump | F2 | F4 | F6 | F8 |
+--echo # |--------------------------------------------------------
+--echo # Jump from handler to outside handling code block : F9
+--echo # Jump from handler to handling code block : F10 (forbidden)
+--echo # Jump inside handler : F21
+--echo # Jump between handler : F22 (forbidden)
+--echo # Jump from cascaded block with handler : F11
+--echo # Duplicate label in same block : F12 (forbidden)
+--echo # Duplicate label in different block : F13
+--echo # Jump outside unlabeled block : F14
+--echo # Jump inside/outside labeled block : F15
+--echo # Jump from if / else : F16
+--echo # Jump with cursors : F17
+--echo # Jump outside case : F18
+--echo # Jump inside/outside case block : F19
+--echo # Jump outside labeled loop : F20
+--echo # Jump (continue) labeled loop : F23
+--echo # Two consecutive label : P24
+--echo # Two consecutive label (backward and forward jump) : P25
+--echo # Two consecutive label, continue to wrong label : P26
+--echo # Consecutive goto label and block label : P27
+
+--echo # Test in function
+--echo # backward jump : func1
+--echo # forward jump : func2
+
+--echo # Test in trigger
+--echo # forward jump : trg1
+
+--echo #
+--echo # Forward jump in same block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f1(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ goto lab1;
+<<lab1>>
+ goto lab2;
+ p2:='b';
+<<lab2>>
+ return ;
+END;
+$$
+
+DELIMITER ;$$
+call f1(@wp1);
+select 'f1',@wp1;
+DROP PROCEDURE f1;
+
+--echo #
+--echo # Backward jump in same block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f2(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='b') then
+ return ;
+ end if;
+ p2:='b';
+ goto lab1;
+END;
+$$
+DELIMITER ;$$
+call f2(@wp1);
+select 'f2',@wp1;
+DROP PROCEDURE f2;
+
+--echo #
+--echo # Forward jump outside one block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f3(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ if (p2='a') then
+ goto lab1;
+ end if;
+ p2:='c';
+<<lab1>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+call f3(@wp1);
+select 'f3',@wp1;
+DROP PROCEDURE f3;
+
+--echo #
+--echo # Backward jump outside one block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f4(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='a') then
+ p2:=p2||'b';
+ goto lab1;
+ end if;
+ if (p2='ab') then
+ p2:=p2||'c';
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+call f4(@wp1);
+select 'f4',@wp1;
+DROP PROCEDURE f4;
+
+DELIMITER $$;
+--echo #
+--echo # Forward jump inside sub block
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f5(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+goto lab5 ;
+ if (p2='a') then
+<<lab5>>
+ p2:=p2||'b';
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Backward jump inside sub block
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f6(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ p2:='a';
+ if (p2='a') then
+<<lab6>>
+ p2:=p2||'b';
+ return ;
+ end if;
+goto lab6 ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Backward jump - missing label
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f7(p2 IN OUT VARCHAR)
+AS
+BEGIN
+<<lab>>
+ goto lab7 ;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Forward jump - missing label
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f8(p2 IN OUT VARCHAR)
+AS
+BEGIN
+ goto lab8 ;
+<<lab>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # Jump from handler to procedure code
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f9(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab9>>
+ if lim=-1 then
+ res:=res||' -- goto end limit -1 --';
+ goto lab9_end;
+ end if;
+
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought ---';
+ lim:=0;
+ goto lab9;
+ end;
+ WHEN NO_DATA_FOUND THEN
+ begin
+ res:=res||'--- no_data_found cought ---';
+ lim:=-1;
+ goto lab9;
+ end;
+ end;
+ res:=res||'error';
+<<lab9_end>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f9(2, @res);
+SELECT 'f9',@res;
+CALL f9(0, @res);
+SELECT 'f9',@res;
+DROP PROCEDURE f9;
+
+DELIMITER $$;
+--echo #
+--echo # Jump from handler to handling bloc
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f10(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ begin
+<<lab10>>
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:='--- too_many_rows cought ---';
+ goto lab10;
+ end;
+ WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
+ end;
+ return ;
+END;
+$$
+
+--echo #
+--echo # Jump from cascaded block with handler
+--echo #
+CREATE or replace procedure f11(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab11a>>
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought 1 ---';
+ goto lab11b;
+ end;
+ WHEN NO_DATA_FOUND THEN
+ begin
+ res:=res||'--- no_data_found cought 1 ---';
+ lim:=2;
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ res:=res||'--- too_many_rows cought 2 ---';
+ goto lab11a;
+ end;
+ WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought 2 ---';
+ end;
+ end;
+ set res:=res||' error ';
+<<lab11b>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f11(0, @res);
+SELECT 'f11',@res;
+DROP PROCEDURE f11;
+
+DELIMITER $$;
+--echo #
+--echo # Jump inside handler
+--echo #
+CREATE or replace procedure f21(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ <<retry>>
+ lim:=lim-1;
+ loop
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ begin
+ lim:=lim-1;
+ goto retry;
+ end;
+ end;
+ exit ;
+ end loop;
+ end;
+ end;
+ res:=lim;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f21(10, @res);
+SELECT 'f21',@res;
+drop procedure f21;
+
+DELIMITER $$;
+--echo #
+--echo # Jump beetween handler
+--error ER_SP_LILABEL_MISMATCH
+CREATE or replace procedure f22(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ res:='ok';
+ begin
+ SELECT a INTO a FROM information_schema.tables LIMIT lim;
+ EXCEPTION
+ WHEN TOO_MANY_ROWS THEN
+ goto nodata ;
+ WHEN NO_DATA_FOUND THEN
+ begin
+<<nodata>>
+ res:='error';
+ end;
+ end;
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--echo #
+--echo # Duplicate label in same bloc
+--error 1309
+CREATE or replace procedure f12(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+<<lab12>>
+ res:='error';
+<<lab12>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+
+--echo #
+--echo # Duplicate label in different block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f13(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+<<lab13>>
+ a:=a+1;
+ begin
+ <<lab13>>
+ a:=a+1;
+ if (a<10) then
+ goto lab13;
+ end if;
+ end;
+ res:=a;
+ if (a=10) then
+ goto lab13;
+ end if;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f13(0, @res);
+SELECT 'f13',@res;
+DROP PROCEDURE f13;
+
+
+--echo #
+--echo # Jump outside unlabeled block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f14(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+ loop
+ a:=a+1;
+ if (a<10) then
+ continue;
+ end if;
+ if (a>=lim) then
+ goto lab14;
+ end if;
+ if (a>=20) then
+ exit;
+ end if;
+ end loop;
+<<lab14>>
+ res:=a;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f14(15, @res);
+SELECT 'f14',@res;
+CALL f14(8, @res);
+SELECT 'f14',@res;
+CALL f14(25, @res);
+SELECT 'f14',@res;
+DROP PROCEDURE f14;
+
+--echo #
+--echo # Jump inside/outside labeled block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f15(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=0;
+ <<looplabel>> loop
+ <<beginlooplabel>>
+ a:=a+1;
+ if (a<10) then
+ continue looplabel;
+ end if;
+ if (a>=lim) then
+ goto lab15;
+ end if;
+ if (a>=20) then
+ exit looplabel;
+ end if;
+ goto beginlooplabel;
+ end loop;
+<<lab15>>
+ res:=a;
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f15(15, @res);
+SELECT 'f15',@res;
+CALL f15(8, @res);
+SELECT 'f15',@res;
+CALL f15(25, @res);
+SELECT 'f15',@res;
+DROP PROCEDURE f15;
+
+--echo #
+--echo # Jump from if / else
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f16(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ if (lim<10) then
+ goto lab16_1;
+ else
+ goto lab16_2;
+ end if;
+<<lab16_1>>
+ res:='if lab16_1';
+ goto lab16_3;
+<<lab16_2>>
+ res:='else lab16_2';
+ goto lab16_3;
+ res:='error lab16_3';
+<<lab16_3>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f16(15, @res);
+SELECT 'f16',@res;
+CALL f16(8, @res);
+SELECT 'f16',@res;
+DROP PROCEDURE f16;
+
+--echo #
+--echo # Jump with cursors
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f17(lim INT, res OUT VARCHAR)
+AS
+ v_a INT;
+ v_b VARCHAR(10);
+ CURSOR cur1 IS SELECT 1 FROM dual where 1=2;
+BEGIN
+ OPEN cur1;
+ LOOP
+ FETCH cur1 INTO v_a;
+ EXIT WHEN cur1%NOTFOUND;
+ END LOOP;
+ CLOSE cur1;
+ <<lab17>>
+ lim:=lim-1;
+ begin
+ declare
+ CURSOR cur1 IS SELECT 1 FROM dual;
+ CURSOR cur2 IS SELECT 1 FROM dual where 1=2;
+ begin
+ LOOP
+ OPEN cur1;
+ FETCH cur1 INTO v_a;
+ EXIT WHEN cur1%NOTFOUND;
+ res:=res||'-'||lim ;
+ close cur1;
+ if (lim>0) then
+ goto lab17;
+ else
+ goto lab17_end;
+ end if;
+ END LOOP;
+ end;
+ <<lab17_end>>
+ null;
+ end;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f17(5, @res);
+SELECT 'f17',@res;
+DROP PROCEDURE f17;
+
+--echo #
+--echo # Jump outside case
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f18(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ case lim
+ when 1 then
+ res:='case branch 18_1';
+ goto lab18_1;
+ res:='error';
+ when 2 then
+ res:='case branch 18_2';
+ goto lab18_2;
+ res:='error';
+ else
+ res:='default branch 18';
+ end case;
+<<lab18_1>>
+ null;
+<<lab18_2>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f18(0, @res);
+SELECT 'f18',@res;
+CALL f18(1, @res);
+SELECT 'f18',@res;
+CALL f18(2, @res);
+SELECT 'f18',@res;
+DROP PROCEDURE f18;
+
+--echo #
+--echo # Jump inside/outside case block
+--echo #
+DELIMITER $$;
+CREATE or replace procedure f19(lim INT, res OUT VARCHAR)
+AS
+ a INT;
+BEGIN
+ a:=1;
+ case lim
+ when 1 then
+<<lab19_0>>
+ a:=a+1;
+ if (a<10) then
+ goto lab19_0;
+ else
+ goto lab19_1;
+ end if;
+ res:='case branch 19_1';
+ else
+ res:='default branch 18';
+ end case;
+ goto lab19_end;
+<<lab19_1>>
+ res:=a;
+<<lab19_end>>
+ return ;
+END;
+$$
+DELIMITER ;$$
+SET @res='';
+CALL f19(1, @res);
+SELECT 'f19',@res;
+DROP PROCEDURE f19;
+
+DELIMITER $$;
+--echo #
+--echo # Jump outside labeled loop
+--echo #
+CREATE OR REPLACE PROCEDURE f20(res OUT VARCHAR)
+AS
+ a INT := 1;
+BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ IF i = 5 THEN
+ a:= a+1;
+ goto lab;
+ END IF;
+ END LOOP;
+ res:=a;
+END;
+$$
+DELIMITER ;$$
+CALL f20(@res);
+SELECT 'f20',@res;
+DROP PROCEDURE f20;
+
+DELIMITER $$;
+--echo #
+--echo # Jump (continue) labeled loop
+--echo #
+CREATE OR REPLACE PROCEDURE f23(res OUT VARCHAR)
+AS
+ a INT := 1;
+BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ IF i = 5 THEN
+ a:= a+1;
+ continue lab;
+ END IF;
+ END LOOP;
+ res:=a;
+END;
+$$
+DELIMITER ;$$
+CALL f23(@res);
+SELECT 'f23',@res;
+DROP PROCEDURE f23;
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label (backward jump)
+--echo #
+CREATE OR REPLACE PROCEDURE p24(action IN INT, res OUT varchar) AS
+ a integer;
+BEGIN
+ <<lab1>>
+ <<lab2>>
+ if (action = 1) then
+ res:=res||' '||action;
+ action:=2;
+ goto lab1;
+ end if;
+ if (action = 2) then
+ res:=res||' '||action;
+ action:=3;
+ goto lab2;
+ end if;
+END;
+$$
+DELIMITER ;$$
+call p24(1,@res);
+select 'p24',@res;
+DROP PROCEDURE p24;
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label (backward and forward jump)
+--echo #
+CREATE OR REPLACE PROCEDURE p25(action IN INT, res OUT varchar) AS
+ a integer;
+BEGIN
+ if (action = 1) then
+ res:=res||' '||action;
+ action:=2;
+ goto lab2;
+ end if;
+ goto lab_end;
+ <<lab1>>
+ <<lab2>>
+ res:=res||' '||action;
+ if (action = 2) then
+ res:=res||' '||action;
+ action:=3;
+ goto lab1;
+ end if;
+<<lab_end>>
+ null;
+END;
+$$
+DELIMITER ;$$
+call p25(1,@res);
+select 'p25',@res;
+DROP PROCEDURE p25;
+
+
+DELIMITER $$;
+--echo #
+--echo # Two consecutive label, continue to wrong label
+--error ER_SP_LILABEL_MISMATCH
+CREATE OR REPLACE PROCEDURE p26(action IN INT, res OUT varchar) AS
+BEGIN
+ <<lab1>>
+ <<lab2>>
+ FOR i IN 1..10 LOOP
+ continue lab1;
+ END LOOP;
+END;
+$$
+DELIMITER ;$$
+
+DELIMITER $$;
+--echo #
+--echo # Consecutive goto label and block label
+--echo #
+CREATE OR REPLACE PROCEDURE p27(action IN INT, res OUT varchar) AS
+BEGIN
+ res:='';
+ <<lab1>>
+ <<lab2>>
+ FOR i IN 1..10 LOOP
+ if (action = 1) then
+ res:=res||' '||action||'-'||i;
+ action:=2;
+ continue lab2;
+ end if;
+ if (action = 2) then
+ res:=res||' '||action||'-'||i;
+ action:='3';
+ goto lab2;
+ end if;
+ if (action = 3) then
+ res:=res||' '||action||'-'||i;
+ action:='4';
+ goto lab1;
+ end if;
+ if (action = 4) then
+ res:=res||' '||action||'-'||i;
+ exit lab2;
+ end if;
+ END LOOP;
+END;
+$$
+
+DELIMITER ;$$
+call p27(1,@res);
+select 'p27',@res;
+DROP PROCEDURE p27;
+
+--echo # ----------------------
+--echo # -- TEST IN FUNCTION --
+--echo # ----------------------
+
+--echo #
+--echo # FUNCTION : Backward jump
+--echo #
+DELIMITER $$;
+CREATE or replace function func1()
+return varchar
+AS
+ p2 varchar(10);
+BEGIN
+ p2:='a';
+<<lab1>>
+ if (p2='a') then
+ p2:=p2||'b';
+ goto lab1;
+ end if;
+ if (p2='ab') then
+ p2:=p2||'c';
+ end if;
+ return p2;
+END;
+$$
+DELIMITER ;$$
+select 'func1',func1();
+DROP function func1;
+
+--echo #
+--echo # FUNCTION : forward jump
+--echo #
+DELIMITER $$;
+CREATE or replace function func2()
+return varchar
+AS
+ p2 varchar(10);
+BEGIN
+ p2:='a';
+ if (p2='a') then
+ goto lab1;
+ end if;
+ p2:='b';
+<<lab1>>
+ return p2;
+END;
+$$
+DELIMITER ;$$
+select 'func2',func2();
+DROP function func2;
+
+--echo # ---------------------
+--echo # -- TEST IN TRIGGER --
+--echo # ---------------------
+
+--echo #
+--echo # TRIGGER : forward jump
+--echo #
+CREATE TABLE t1 (a INT);
+DELIMITER $$;
+CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW
+BEGIN
+ IF :NEW.a IS NULL
+ THEN
+ :NEW.a:= 15;
+ goto end_trg;
+ END IF;
+ :NEW.a:= 10;
+<<end_trg>>
+ null;
+END;
+$$
+DELIMITER ;$$
+insert into t1 values (1);
+insert into t1 values (null);
+SELECT * FROM t1;
+DROP TRIGGER trg1;
+DROP TABLE t1; \ No newline at end of file
diff --git a/sql/lex.h b/sql/lex.h
index 67db3b746bf..cdd4daffaf8 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -261,6 +261,7 @@ static SYMBOL symbols[] = {
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GET", SYM(GET_SYM)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
+ { "GOTO", SYM(GOTO_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
{ "GROUP", SYM(GROUP_SYM)},
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 471e6d46828..a29147b8a75 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -556,6 +556,7 @@ sp_head::sp_head()
DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty();
+ m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
@@ -2212,7 +2213,8 @@ sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex)
Put the instruction on the backpatch list, associated with the label.
*/
int
-sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab,
+ List<bp_t> *list, backpatch_instr_type itype)
{
bp_t *bp= (bp_t *) thd->alloc(sizeof(bp_t));
@@ -2220,7 +2222,45 @@ sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
return 1;
bp->lab= lab;
bp->instr= i;
- return m_backpatch.push_front(bp);
+ bp->instr_type= itype;
+ return list->push_front(bp);
+}
+
+int
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+{
+ return push_backpatch(thd, i, lab, &m_backpatch, GOTO);
+}
+
+int
+sp_head::push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab)
+{
+ uint ip= instructions();
+
+ /*
+ Add cpop/hpop : they will be removed or updated later if target is in
+ the same block or not
+ */
+ sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, ctx, 0);
+ if (hpop == NULL || add_instr(hpop))
+ return true;
+ if (push_backpatch(thd, hpop, lab, &m_backpatch_goto, HPOP))
+ return true;
+
+ sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, ctx, 0);
+ if (cpop == NULL || add_instr(cpop))
+ return true;
+ if (push_backpatch(thd, cpop, lab, &m_backpatch_goto, CPOP))
+ return true;
+
+ // Add jump with ip=0. IP will be updated when label is found.
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
+ if (i == NULL || add_instr(i))
+ return true;
+ if (push_backpatch(thd, i, lab, &m_backpatch_goto, GOTO))
+ return true;
+
+ return false;
}
/**
@@ -2247,6 +2287,97 @@ sp_head::backpatch(sp_label *lab)
DBUG_VOID_RETURN;
}
+void
+sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator<bp_t> li(m_backpatch_goto);
+
+ DBUG_ENTER("sp_head::backpatch_goto");
+ while ((bp= li++))
+ {
+ if (bp->instr->m_ip < lab_begin_block->ip || bp->instr->m_ip > lab->ip)
+ {
+ /*
+ Update only jump target from the beginning of the block where the
+ label is defined.
+ */
+ continue;
+ }
+ if (my_strcasecmp(system_charset_info,
+ bp->lab->name.str,
+ lab->name.str) == 0)
+ {
+ if (bp->instr_type == GOTO)
+ {
+ DBUG_PRINT("info",
+ ("backpatch_goto: (m_ip %d, label 0x%lx <%s>) to dest %d",
+ bp->instr->m_ip, (ulong) lab, lab->name.str, dest));
+ bp->instr->backpatch(dest, lab->ctx);
+ // Jump resolved, remove from the list
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == CPOP)
+ {
+ int n= lab->ctx->diff_cursors(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove cpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_cpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == HPOP)
+ {
+ int n= lab->ctx->diff_handlers(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove hpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_hpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool
+sp_head::check_unresolved_goto()
+{
+ DBUG_ENTER("sp_head::check_unresolved_goto");
+ bool has_unresolved_label=false;
+ if (m_backpatch_goto.elements > 0)
+ {
+ List_iterator_fast<bp_t> li(m_backpatch_goto);
+ bp_t *bp;
+ while ((bp= li++))
+ {
+ if ((bp->instr_type == GOTO))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name);
+ has_unresolved_label=true;
+ }
+ }
+ }
+ DBUG_RETURN(has_unresolved_label);
+}
int
sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
diff --git a/sql/sp_head.h b/sql/sp_head.h
index fc3dfa49f63..257b4d9655e 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -516,11 +516,19 @@ public:
/// Put the instruction on the backpatch list, associated with the label.
int
push_backpatch(THD *thd, sp_instr *, sp_label *);
+ int
+ push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab);
/// Update all instruction with this label in the backpatch list to
/// the current position.
void
backpatch(sp_label *);
+ void
+ backpatch_goto(THD *thd, sp_label *, sp_label *);
+
+ /// Check for unresolved goto label
+ bool
+ check_unresolved_goto();
/// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
int
@@ -695,12 +703,17 @@ private:
sp_pcontext *m_pcont; ///< Parse context
List<LEX> m_lex; ///< Temp. store for the other lex
DYNAMIC_ARRAY m_instr; ///< The "instructions"
+
+ enum backpatch_instr_type { GOTO, CPOP, HPOP };
typedef struct
{
sp_label *lab;
sp_instr *instr;
+ backpatch_instr_type instr_type;
} bp_t;
List<bp_t> m_backpatch; ///< Instructions needing backpatching
+ List<bp_t> m_backpatch_goto; // Instructions needing backpatching (for goto)
+
/**
We need a special list for backpatching of instructions with a continue
destination (in the case of a continue handler catching an error in
@@ -738,6 +751,12 @@ private:
by routine.
*/
bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check);
+
+ /// Put the instruction on the a backpatch list, associated with the label.
+ int
+ push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> *list,
+ backpatch_instr_type itype);
+
}; // class sp_head : public Sql_alloc
@@ -1359,6 +1378,11 @@ public:
virtual ~sp_instr_hpop()
{}
+ void update_count(uint count)
+ {
+ m_count= count;
+ }
+
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
@@ -1451,6 +1475,11 @@ public:
virtual ~sp_instr_cpop()
{}
+ void update_count(uint count)
+ {
+ m_count= count;
+ }
+
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 06c51642d68..628c79a880b 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -87,6 +87,7 @@ void sp_pcontext::init(uint var_offset,
m_num_case_exprs= num_case_expressions;
m_labels.empty();
+ m_goto_labels.empty();
}
@@ -129,6 +130,12 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
}
+bool cmp_labels(sp_label *a, sp_label *b)
+{
+ return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0
+ && a->type == b->type);
+}
+
sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
@@ -140,6 +147,18 @@ sp_pcontext *sp_pcontext::pop_context()
if (m_num_case_exprs > m_parent->m_num_case_exprs)
m_parent->m_num_case_exprs= m_num_case_exprs;
+ /*
+ ** Push unresolved goto label to parent context
+ */
+ sp_label *label;
+ List_iterator_fast<sp_label> li(m_goto_labels);
+ while ((label= li++))
+ {
+ if (label->ip == 0)
+ {
+ m_parent->m_goto_labels.add_unique(label, &cmp_labels);
+ }
+ }
return m_parent;
}
@@ -227,9 +246,9 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
return m_vars.append(p) ? NULL : p;
}
-
sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip,
- sp_label::enum_type type)
+ sp_label::enum_type type,
+ List<sp_label> *list)
{
sp_label *label=
new (thd->mem_root) sp_label(name, ip, type, this);
@@ -237,11 +256,47 @@ sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip,
if (!label)
return NULL;
- m_labels.push_front(label, thd->mem_root);
+ list->push_front(label, thd->mem_root);
return label;
}
+sp_label *sp_pcontext::find_goto_label(const LEX_STRING name, bool recusive)
+{
+ List_iterator_fast<sp_label> li(m_goto_labels);
+ sp_label *lab;
+
+ while ((lab= li++))
+ {
+ if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
+ return lab;
+ }
+
+ if (!recusive)
+ return NULL;
+
+ /*
+ Note about exception handlers.
+ See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003),
+ section 13.1 <compound statement>,
+ syntax rule 4.
+ In short, a DECLARE HANDLER block can not refer
+ to labels from the parent context, as they are out of scope.
+ */
+ if (m_scope == HANDLER_SCOPE && m_parent)
+ {
+ if (m_parent->m_parent)
+ {
+ // Skip the parent context
+ return m_parent->m_parent->find_goto_label(name);
+ }
+ }
+
+ return m_parent && (m_scope == REGULAR_SCOPE) ?
+ m_parent->find_goto_label(name) :
+ NULL;
+}
+
sp_label *sp_pcontext::find_label(const LEX_STRING name)
{
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 00cc81964eb..85d0afa8feb 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -94,6 +94,7 @@ public:
/// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a
/// combination of low-level jump/jump_if instructions and labels.
+
class sp_label : public Sql_alloc
{
public:
@@ -106,7 +107,10 @@ public:
BEGIN,
/// Label at iteration control
- ITERATION
+ ITERATION,
+
+ /// Label for jump
+ GOTO
};
/// Name of the label.
@@ -132,6 +136,7 @@ public:
{ }
};
+
///////////////////////////////////////////////////////////////////////////
/// This class represents condition-value term in DECLARE CONDITION or
@@ -507,15 +512,29 @@ public:
/////////////////////////////////////////////////////////////////////////
sp_label *push_label(THD *thd, const LEX_STRING name, uint ip,
- sp_label::enum_type type);
+ sp_label::enum_type type, List<sp_label> * list);
+
+ sp_label *push_label(THD *thd, LEX_STRING name, uint ip,
+ sp_label::enum_type type)
+ { return push_label(thd, name, ip, type, &m_labels); }
+
+ sp_label *push_goto_label(THD *thd, LEX_STRING name, uint ip,
+ sp_label::enum_type type)
+ { return push_label(thd, name, ip, type, &m_goto_labels); }
sp_label *push_label(THD *thd, const LEX_STRING name, uint ip)
- {
- return push_label(thd, name, ip, sp_label::IMPLICIT);
- }
+ { return push_label(thd, name, ip, sp_label::IMPLICIT); }
+
+ sp_label *push_goto_label(THD *thd, const LEX_STRING name, uint ip)
+ { return push_goto_label(thd, name, ip, sp_label::GOTO); }
sp_label *find_label(const LEX_STRING name);
+ sp_label *find_goto_label(const LEX_STRING name, bool recusive);
+
+ sp_label *find_goto_label(const LEX_STRING name)
+ { return find_goto_label(name, true); }
+
sp_label *find_label_current_loop_start();
sp_label *last_label()
@@ -528,6 +547,11 @@ public:
return label;
}
+ sp_label *last_goto_label()
+ {
+ return m_goto_labels.head();
+ }
+
sp_label *pop_label()
{ return m_labels.pop(); }
@@ -697,8 +721,27 @@ private:
/// Stack of SQL-handlers.
Dynamic_array<sp_handler *> m_handlers;
- /// List of labels.
+ /*
+ In the below example the label <<lab>> has two meanings:
+ - GOTO lab : must go before the beginning of the loop
+ - CONTINUE lab : must go to the beginning of the loop
+ We solve this by storing block labels and goto labels into separate lists.
+
+ BEGIN
+ <<lab>>
+ FOR i IN a..10 LOOP
+ ...
+ GOTO lab;
+ ...
+ CONTINUE lab;
+ ...
+ END LOOP;
+ END;
+ */
+ /// List of block labels
List<sp_label> m_labels;
+ /// List of goto labels
+ List<sp_label> m_goto_labels;
/// Children contexts, used for destruction.
Dynamic_array<sp_pcontext *> m_children;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index b0647f6b404..b8d150139cf 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5704,6 +5704,54 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name)
return sp_exit_block(thd, lab, NULL);
}
+bool LEX::sp_goto_statement(THD *thd, const LEX_STRING label_name)
+{
+ sp_label *lab= spcont->find_goto_label(label_name);
+ if (!lab || lab->ip == 0)
+ {
+ sp_label *delayedlabel;
+ if (!lab)
+ {
+ // Label not found --> add forward jump to an unknown label
+ spcont->push_goto_label(thd, label_name, 0, sp_label::GOTO);
+ delayedlabel= spcont->last_goto_label();
+ }
+ else
+ {
+ delayedlabel= lab;
+ }
+ return sphead->push_backpatch_goto(thd, spcont, delayedlabel);
+ }
+ else
+ {
+ // Label found (backward goto)
+ return sp_change_context(thd, lab->ctx, false) ||
+ sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */
+ }
+ return false;
+}
+
+bool LEX::sp_push_goto_label(THD *thd, const LEX_STRING label_name)
+{
+ sp_label *lab= spcont->find_goto_label(label_name, false);
+ if (lab)
+ {
+ if (lab->ip != 0)
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name.str);
+ return true;
+ }
+ lab->ip= sphead->instructions();
+
+ sp_label *beginblocklabel= spcont->find_label(empty_lex_str);
+ sphead->backpatch_goto(thd, lab, beginblocklabel);
+ }
+ else
+ {
+ spcont->push_goto_label(thd, label_name, sphead->instructions());
+ }
+ return false;
+}
bool LEX::sp_exit_block(THD *thd, sp_label *lab)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 913478b9589..794e6b44702 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3317,6 +3317,7 @@ public:
bool sp_exit_statement(THD *thd, Item *when);
bool sp_exit_statement(THD *thd, const LEX_STRING label_name, Item *item);
bool sp_leave_statement(THD *thd, const LEX_STRING label_name);
+ bool sp_goto_statement(THD *thd, const LEX_STRING label_name);
bool sp_continue_statement(THD *thd, Item *when);
bool sp_continue_statement(THD *thd, const LEX_STRING label_name, Item *when);
@@ -3329,6 +3330,7 @@ public:
void sp_pop_loop_empty_label(THD *thd);
bool sp_while_loop_expression(THD *thd, Item *expr);
bool sp_while_loop_finalize(THD *thd);
+ bool sp_push_goto_label(THD *thd, LEX_STRING label_name);
Item_param *add_placeholder(THD *thd, char *name,
uint pos_in_query, uint len_in_query);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 3115f9603e3..43fb40bbcf4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1098,6 +1098,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token GET_FORMAT /* MYSQL-FUNC */
%token GET_SYM /* SQL-2003-R */
%token GLOBAL_SYM /* SQL-2003-R */
+%token GOTO_SYM /* Oracle, reserved in PL/SQL*/
%token GRANT /* SQL-2003-R */
%token GRANTS
%token GROUP_SYM /* SQL-2003-R */
@@ -14288,6 +14289,7 @@ keyword_sp:
| GET_FORMAT {}
| GRANTS {}
| GLOBAL_SYM {}
+ | GOTO_SYM {}
| HASH_SYM {}
| HARD_SYM {}
| HOSTS_SYM {}
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index c652192622e..b994023c195 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -516,6 +516,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token GET_FORMAT /* MYSQL-FUNC */
%token GET_SYM /* SQL-2003-R */
%token GLOBAL_SYM /* SQL-2003-R */
+%token GOTO_SYM /* Oracle, reserved in PL/SQL*/
%token GRANT /* SQL-2003-R */
%token GRANTS
%token GROUP_SYM /* SQL-2003-R */
@@ -1005,7 +1006,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
opt_component key_cache_name
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
opt_constraint constraint opt_ident
- label_declaration_oracle ident_directly_assignable
+ label_declaration_oracle labels_declaration_oracle
+ ident_directly_assignable
sp_decl_ident
sp_block_label
@@ -1307,15 +1309,16 @@ END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return
- sp_proc_stmt_in_returns_clause
%type <NONE> sp_proc_stmt_compound_ok
%type <NONE> sp_proc_stmt_if
%type <NONE> sp_labeled_control sp_unlabeled_control
%type <NONE> sp_labeled_block sp_unlabeled_block
+%type <NONE> sp_labelable_stmt
%type <NONE> sp_proc_stmt_continue
%type <NONE> sp_proc_stmt_exit
%type <NONE> sp_proc_stmt_leave
%type <NONE> sp_proc_stmt_iterate
+%type <NONE> sp_proc_stmt_goto
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
%type <NONE> case_stmt_specification
%type <NONE> loop_body while_body repeat_body
@@ -2425,6 +2428,17 @@ sp_proc_stmts1:
| sp_proc_stmts1 sp_proc_stmt ';'
;
+sp_proc_stmts1_implicit_block:
+ {
+ Lex->sp_block_init(thd);
+ }
+ sp_proc_stmts1
+ {
+ if (Lex->sp_block_finalize(thd))
+ MYSQL_YYABORT;
+ }
+ ;
+
opt_sp_decl_body_list:
/* Empty */
{
@@ -3034,32 +3048,28 @@ sp_opt_default:
| SET_VAR expr { $$ = $2; }
;
-/*
- ps_proc_stmt_in_returns_clause is a statement that is allowed
- in the RETURNS clause of a stored function definition directly,
- without the BEGIN..END block.
- It should not include any syntax structures starting with '(', to avoid
- shift/reduce conflicts with the rule "field_type" and its sub-rules
- that scan an optional length, like CHAR(1) or YEAR(4).
- See MDEV-9166.
-*/
-sp_proc_stmt_in_returns_clause:
- sp_proc_stmt_return
- | sp_labeled_block
+sp_proc_stmt:
+ sp_labeled_block
+ | sp_unlabeled_block
| sp_labeled_control
- | sp_proc_stmt_compound_ok
+ | sp_unlabeled_control
+ | sp_labelable_stmt
+ | labels_declaration_oracle sp_labelable_stmt {}
;
-sp_proc_stmt:
- sp_proc_stmt_in_returns_clause
- | sp_proc_stmt_statement
+sp_labelable_stmt:
+ sp_proc_stmt_statement
| sp_proc_stmt_continue
| sp_proc_stmt_exit
| sp_proc_stmt_leave
| sp_proc_stmt_iterate
+ | sp_proc_stmt_goto
| sp_proc_stmt_open
| sp_proc_stmt_fetch
| sp_proc_stmt_close
+ | sp_proc_stmt_return
+ | sp_proc_stmt_if
+ | case_stmt_specification
| NULL_SYM { }
;
@@ -3245,6 +3255,15 @@ sp_proc_stmt_iterate:
}
;
+sp_proc_stmt_goto:
+ GOTO_SYM label_ident
+ {
+ if (Lex->sp_goto_statement(thd, $2))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
remember_lex:
{
$$= thd->lex;
@@ -3396,7 +3415,7 @@ sp_if:
if (sp->restore_lex(thd))
MYSQL_YYABORT;
}
- sp_proc_stmts1
+ sp_proc_stmts1_implicit_block
{
sp_head *sp= Lex->sphead;
sp_pcontext *ctx= Lex->spcont;
@@ -3419,7 +3438,7 @@ sp_if:
sp_elseifs:
/* Empty */
| ELSIF_SYM sp_if
- | ELSE sp_proc_stmts1
+ | ELSE sp_proc_stmts1_implicit_block
;
case_stmt_specification:
@@ -3535,7 +3554,7 @@ simple_when_clause:
MYSQL_YYABORT;
}
THEN_SYM
- sp_proc_stmts1
+ sp_proc_stmts1_implicit_block
{
if (Lex->case_stmt_action_then())
MYSQL_YYABORT;
@@ -3557,7 +3576,7 @@ searched_when_clause:
MYSQL_YYABORT;
}
THEN_SYM
- sp_proc_stmts1
+ sp_proc_stmts1_implicit_block
{
if (Lex->case_stmt_action_then())
MYSQL_YYABORT;
@@ -3576,7 +3595,7 @@ else_clause_opt:
sp->add_instr(i))
MYSQL_YYABORT;
}
- | ELSE sp_proc_stmts1
+ | ELSE sp_proc_stmts1_implicit_block
;
sp_opt_label:
@@ -3585,7 +3604,7 @@ sp_opt_label:
;
sp_block_label:
- label_declaration_oracle
+ labels_declaration_oracle
{
if (Lex->spcont->block_label_declare($1))
MYSQL_YYABORT;
@@ -3819,14 +3838,14 @@ pop_sp_loop_label:
;
sp_labeled_control:
- label_declaration_oracle LOOP_SYM
+ labels_declaration_oracle LOOP_SYM
{
if (Lex->sp_push_loop_label(thd, $1))
MYSQL_YYABORT;
}
loop_body pop_sp_loop_label
{ }
- | label_declaration_oracle WHILE_SYM
+ | labels_declaration_oracle WHILE_SYM
{
if (Lex->sp_push_loop_label(thd, $1))
MYSQL_YYABORT;
@@ -3834,7 +3853,7 @@ sp_labeled_control:
}
while_body pop_sp_loop_label
{ }
- | label_declaration_oracle FOR_SYM
+ | labels_declaration_oracle FOR_SYM
{
// See "The FOR LOOP statement" comments in sql_lex.cc
Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block
@@ -3858,7 +3877,7 @@ sp_labeled_control:
if (Lex->sp_block_finalize(thd)) // The outer DECLARE..BEGIN..END
MYSQL_YYABORT;
}
- | label_declaration_oracle REPEAT_SYM
+ | labels_declaration_oracle REPEAT_SYM
{
if (Lex->sp_push_loop_label(thd, $1))
MYSQL_YYABORT;
@@ -14188,8 +14207,18 @@ label_ident:
}
;
+labels_declaration_oracle:
+ label_declaration_oracle { $$= $1; }
+ | labels_declaration_oracle label_declaration_oracle { $$= $2; }
+ ;
+
label_declaration_oracle:
- SHIFT_LEFT label_ident SHIFT_RIGHT { $$= $2; }
+ SHIFT_LEFT label_ident SHIFT_RIGHT
+ {
+ if (Lex->sp_push_goto_label(thd, $2))
+ MYSQL_YYABORT;
+ $$= $2;
+ }
;
ident_or_text:
@@ -16434,6 +16463,8 @@ trigger_tail:
{ /* $21 */
LEX *lex= Lex;
sp_head *sp= lex->sphead;
+ if (sp->check_unresolved_goto())
+ MYSQL_YYABORT;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
sp->set_stmt_end(thd);
@@ -16517,6 +16548,8 @@ sf_tail:
{
LEX *lex= thd->lex;
sp_head *sp= lex->sphead;
+ if (sp->check_unresolved_goto())
+ MYSQL_YYABORT;
if (sp->is_not_allowed_in_function("function"))
MYSQL_YYABORT;
@@ -16549,7 +16582,8 @@ sp_tail:
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
-
+ if (sp->check_unresolved_goto())
+ MYSQL_YYABORT;
sp->set_stmt_end(thd);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
sp->restore_thd_mem_root(thd);