diff options
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp-goto.result | 834 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp-goto.test | 872 | ||||
-rw-r--r-- | sql/lex.h | 1 | ||||
-rw-r--r-- | sql/sp_head.cc | 135 | ||||
-rw-r--r-- | sql/sp_head.h | 29 | ||||
-rw-r--r-- | sql/sp_pcontext.cc | 61 | ||||
-rw-r--r-- | sql/sp_pcontext.h | 55 | ||||
-rw-r--r-- | sql/sql_lex.cc | 48 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 94 |
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); |