Commit af7f287b authored by halfspawn's avatar halfspawn Committed by Alexander Barkov

MDEV-10697 GOTO statement

parent d836f52b
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;
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
......@@ -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)},
......
......@@ -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)
......
......@@ -516,11 +516,19 @@ class sp_head :private Query_arena,
/// 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 @@ class sp_head :private Query_arena,
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 @@ class sp_head :private Query_arena,
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 @@ class sp_instr_hpop : public sp_instr
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 @@ class sp_instr_cpop : public sp_instr
virtual ~sp_instr_cpop()
{}
void update_count(uint count)
{
m_count= count;
}
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
......
......@@ -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)
{
......
......@@ -94,6 +94,7 @@ class sp_variable : public Sql_alloc
/// 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 @@ class sp_label : public Sql_alloc
BEGIN,
/// Label at iteration control
ITERATION
ITERATION,
/// Label for jump
GOTO
};
/// Name of the label.
......@@ -132,6 +136,7 @@ class sp_label : public Sql_alloc
{ }
};
///////////////////////////////////////////////////////////////////////////
/// This class represents condition-value term in DECLARE CONDITION or
......@@ -507,15 +512,29 @@ class sp_pcontext : public Sql_alloc
/////////////////////////////////////////////////////////////////////////
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 @@ class sp_pcontext : public Sql_alloc
return label;
}
sp_label *last_goto_label()
{
return m_goto_labels.head();
}
sp_label *pop_label()
{ return m_labels.pop(); }
......@@ -697,8 +721,27 @@ class sp_pcontext : public Sql_alloc
/// 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;
......
......@@ -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)
{
......
......@@ -3317,6 +3317,7 @@ struct LEX: public Query_tables_list
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 @@ struct LEX: public Query_tables_list
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);
......
......@@ -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 {}
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment