From 06ecdc923f487f90b8f0524423132c4d1dea1256 Mon Sep 17 00:00:00 2001
From: jb <jb@JB-PC>
Date: Mon, 6 Feb 2017 10:13:35 +0100
Subject: [PATCH] MDEV-10697  sql_mode=ORACLE: GOTO statement

---
 mysql-test/suite/compat/oracle/r/sp-goto.result | 727 +++++++++++++++++++++++
 mysql-test/suite/compat/oracle/t/sp-goto.test   | 758 ++++++++++++++++++++++++
 sql/lex.h                                       |   1 +
 sql/sp_head.cc                                  | 132 ++++-
 sql/sp_head.h                                   |  33 ++
 sql/sp_pcontext.cc                              |  63 +-
 sql/sp_pcontext.h                               |  35 +-
 sql/sql_lex.cc                                  |  48 ++
 sql/sql_lex.h                                   |   2 +
 sql/sql_yacc.yy                                 |   2 +
 sql/sql_yacc_ora.yy                             |  82 ++-
 11 files changed, 1844 insertions(+), 39 deletions(-)
 create mode 100644 mysql-test/suite/compat/oracle/r/sp-goto.result
 create mode 100644 mysql-test/suite/compat/oracle/t/sp-goto.test

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 0000000..d101f53
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-goto.result
@@ -0,0 +1,727 @@
+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 : F21
+# 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
+#
+# 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 f21(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 f21(@res);
+SELECT 'f21',@res;
+f21	@res
+f21	2
+DROP PROCEDURE f21;
+# ----------------------
+# -- 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:='c';
+<<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 0000000..0f34158
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-goto.test
@@ -0,0 +1,758 @@
+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 : F21
+
+--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 1308
+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 1308
+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 1308
+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 1308
+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 1308
+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;
+
+DELIMITER $$;
+--echo #
+--echo # Jump beetween handler
+--error 1308
+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 f21(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 f21(@res);
+SELECT 'f21',@res;
+DROP PROCEDURE f21;
+
+
+--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:='c';
+<<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 e198a57..161ea92 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 627b239..bac31da 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -591,6 +591,7 @@ sp_head::sp_head()
   DBUG_ENTER("sp_head::sp_head");
 
   m_backpatch.empty();
+  m_backpatch_jump.empty();
   m_cont_backpatch.empty();
   m_lex.empty();
   my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
@@ -2252,7 +2253,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));
 
@@ -2260,9 +2262,46 @@ 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, JUMP);
 }
 
+int
+sp_head::push_backpatch_jump(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_jump, 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_jump, 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_jump, JUMP))
+      return true;
+
+    return false;
+}
+
+
 /**
   Update all instruction with this label in the backpatch list to
   the current position.
@@ -2287,6 +2326,95 @@ sp_head::backpatch(sp_label *lab)
   DBUG_VOID_RETURN;
 }
 
+void
+sp_head::backpatch_jump(THD *thd, sp_label *lab,sp_label *lab_begin_block)
+{
+  bp_t *bp;
+  uint dest= instructions();
+  List_iterator<bp_t> li(m_backpatch_jump);
+
+  DBUG_ENTER("sp_head::backpatch_jump");
+  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 == JUMP)
+      {
+        DBUG_PRINT("info",
+                   ("backpatch_jump: (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
+          ((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
+          ((sp_instr_hpop *)(bp->instr))->update_count(n);
+          n= 1;
+        }
+
+        li.remove();
+        continue;
+      }
+    }
+  }
+  DBUG_VOID_RETURN;
+}
+
+void
+sp_head::check_unresolved_jump()
+{
+  DBUG_ENTER("sp_head::check_unresolved_jump");
+  if (m_backpatch_jump.elements>0)
+  {
+    List_iterator_fast<bp_t> li(m_backpatch_jump);
+    bp_t *bp;
+
+    while ((bp= li++))
+    {
+        if (dynamic_cast<sp_instr_jump *>(bp->instr))
+        {
+           my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name);
+        }
+    }
+  }
+  DBUG_VOID_RETURN;
+}
 
 int
 sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 41e280c..2d5b41c 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -525,11 +525,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
+  sp_head::push_backpatch_jump(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_jump(THD *thd, sp_label *, sp_label *);
+
+  /// Check for unresolved goto label
+  void
+  check_unresolved_jump();
 
   /// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
   int
@@ -704,12 +712,19 @@ 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 { JUMP, CPOP, HPOP };
+
   typedef struct
   {
     sp_label *lab;
     sp_instr *instr;
+    uint32 instr_type;
   } bp_t;
   List<bp_t> m_backpatch;	///< Instructions needing backpatching
+
+  List<bp_t> m_backpatch_jump; // 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
@@ -747,6 +762,14 @@ 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);
+  int
+  push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> * list,
+                 backpatch_instr_type itype);
+
 }; // class sp_head : public Sql_alloc
 
 
@@ -1368,6 +1391,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);
@@ -1460,6 +1488,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);
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 06c5164..0a66ebd 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_jump_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,19 @@ 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 jump label to parent context
+  */
+  sp_label * label;
+  List_iterator_fast<sp_label> li(m_jump_labels);
+
+  while ((label= li++))
+  {
+    if (label->ip == 0)
+    {
+      m_parent->m_jump_labels.add_unique(label, &cmp_labels);
+    }
+  }
   return m_parent;
 }
 
@@ -227,9 +247,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 +257,48 @@ 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_jump_label(const LEX_STRING name, bool recusive)
+{
+  List_iterator_fast<sp_label> li(m_jump_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_jump_label(name);
+    }
+  }
+
+  return (m_parent && (m_scope == REGULAR_SCOPE)) ?
+         m_parent->find_jump_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 00cc819..864f916 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -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
+    JUMP
   };
 
   /// 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 *sp_pcontext::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 *sp_pcontext::push_jump_label(THD *thd, LEX_STRING name, uint ip,
+                                         sp_label::enum_type type)
+  { return push_label(thd, name, ip, type, &m_jump_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_jump_label(THD *thd, const LEX_STRING name, uint ip)
+  { return push_jump_label(thd, name, ip, sp_label::JUMP); }
 
   sp_label *find_label(const LEX_STRING name);
 
+  sp_label *find_jump_label(const LEX_STRING name, bool recusive);
+
+  sp_label *sp_pcontext::find_jump_label(const LEX_STRING name)
+  { return find_jump_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_jump_label()
+  {
+    return m_jump_labels.head();
+  }
+
   sp_label *pop_label()
   { return m_labels.pop(); }
 
@@ -699,6 +723,7 @@ class sp_pcontext : public Sql_alloc
 
   /// List of labels.
   List<sp_label> m_labels;
+  List<sp_label> m_jump_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 c361ee0..776318c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5511,6 +5511,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_jump_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_jump_label(thd, label_name, 0, sp_label::JUMP);
+      delayedlabel= spcont->last_jump_label();
+    }
+    else
+    {
+       delayedlabel= lab;
+    }
+    return sphead->push_backpatch_jump(thd,spcont,delayedlabel);
+  }
+  else
+  {
+    // Label found (backward jump)
+    return sp_change_context(thd, lab->ctx, false) ||
+           sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */
+  }
+  return false;
+}
+
+bool LEX::sp_push_jump_label(THD *thd, const LEX_STRING label_name)
+{
+  sp_label *lab= spcont->find_jump_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_jump(thd, lab, beginblocklabel);
+  }
+  else
+  {
+    spcont->push_jump_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 466b360..2fb51bd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3288,6 +3288,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);
@@ -3300,6 +3301,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_jump_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 764bb42..1cf0472 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1064,6 +1064,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 */
@@ -14230,6 +14231,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 0ff2318..a6c5d10 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -512,6 +512,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 */
@@ -1297,15 +1298,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_non_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
@@ -2532,6 +2534,16 @@ 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 */
           {
@@ -3141,32 +3153,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_non_labelable_stmt
+        | label_declaration_oracle sp_non_labelable_stmt {}
         ;
 
-sp_proc_stmt:
-          sp_proc_stmt_in_returns_clause
-        | sp_proc_stmt_statement
+sp_non_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 { }
         ;
 
@@ -3352,6 +3360,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;
@@ -3503,7 +3520,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;
@@ -3526,7 +3543,7 @@ sp_if:
 sp_elseifs:
           /* Empty */
         | ELSIF_SYM sp_if
-        | ELSE sp_proc_stmts1
+        | ELSE sp_proc_stmts1_implicit_block
         ;
 
 case_stmt_specification:
@@ -3642,7 +3659,7 @@ simple_when_clause:
               MYSQL_YYABORT;
           }
           THEN_SYM
-          sp_proc_stmts1
+          sp_proc_stmts1_implicit_block
           {
             if (Lex->case_stmt_action_then())
               MYSQL_YYABORT;
@@ -3664,7 +3681,7 @@ searched_when_clause:
               MYSQL_YYABORT;
           }
           THEN_SYM
-          sp_proc_stmts1
+          sp_proc_stmts1_implicit_block
           {
             if (Lex->case_stmt_action_then())
               MYSQL_YYABORT;
@@ -3683,7 +3700,7 @@ else_clause_opt:
                 sp->add_instr(i))
               MYSQL_YYABORT;
           }
-        | ELSE sp_proc_stmts1
+        | ELSE sp_proc_stmts1_implicit_block
         ;
 
 sp_opt_label:
@@ -11674,8 +11691,8 @@ limit_option:
                                            $1.m_pos -
                                            lex->substatement_query(thd),
                                            lip->get_tok_end() - $1.m_pos)))
-            MYSQL_YYABORT;
-        }
+              MYSQL_YYABORT;
+          }
         | ident_with_tok_start '.' ident
         {
           LEX *lex= thd->lex;
@@ -14011,8 +14028,8 @@ simple_ident_q:
           ident '.' ident
           {
             if (!($$= Lex->create_item_ident_nospvar(thd, $1, $3)))
-              MYSQL_YYABORT;
-          }
+                MYSQL_YYABORT;
+            }
         | simple_ident_q2
         ;
 
@@ -14287,7 +14304,12 @@ label_ident:
         ;
 
 label_declaration_oracle:
-          SHIFT_LEFT label_ident SHIFT_RIGHT { $$= $2; }
+          SHIFT_LEFT label_ident SHIFT_RIGHT
+          {
+            if (Lex->sp_push_jump_label(thd, $2))
+              MYSQL_YYABORT;
+            $$= $2;
+          }
         ;
 
 ident_or_text:
@@ -15042,7 +15064,7 @@ option_value_no_option_type:
             if (Lex->init_default_internal_variable(&var, $3) ||
                 Lex->set_variable(&var, $5))
               MYSQL_YYABORT;
-           }
+          }
         | '@' ident_or_text equal expr
           {
             Item_func_set_user_var *item;
@@ -16523,6 +16545,7 @@ trigger_tail:
           { /* $21 */
             LEX *lex= Lex;
             sp_head *sp= lex->sphead;
+            sp->check_unresolved_jump();
 
             lex->sql_command= SQLCOM_CREATE_TRIGGER;
             sp->set_stmt_end(thd);
@@ -16610,6 +16633,7 @@ sf_tail:
           {
             LEX *lex= thd->lex;
             sp_head *sp= lex->sphead;
+            sp->check_unresolved_jump();
 
             if (sp->is_not_allowed_in_function("function"))
               MYSQL_YYABORT;
@@ -16641,7 +16665,7 @@ sp_tail:
           {
             LEX *lex= Lex;
             sp_head *sp= lex->sphead;
-
+            sp->check_unresolved_jump();
             sp->set_stmt_end(thd);
             lex->sql_command= SQLCOM_CREATE_PROCEDURE;
             sp->restore_thd_mem_root(thd);
-- 
2.6.3.windows.1

