From 4212039db78b8a559addd38eaeef3e97b8c5abe9 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 22 Aug 2016 06:17:26 +0400 Subject: [PATCH] MDEV-10411 Providing compatibility for basic PL/SQL constructs Part 17: RETURN in stored procedures --- .../suite/compat/oracle/r/sp-code.result | 50 +++++++++++--- mysql-test/suite/compat/oracle/r/sp.result | 59 +++++++++++++++++ mysql-test/suite/compat/oracle/t/sp-code.test | 25 +++++++ mysql-test/suite/compat/oracle/t/sp.test | 66 +++++++++++++++++++ sql/sp_head.h | 35 ++++++++++ sql/sql_yacc_ora.yy | 16 +++++ 6 files changed, 242 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result index 9ac733274e1..4f2dc58d6c8 100644 --- a/mysql-test/suite/compat/oracle/r/sp-code.result +++ b/mysql-test/suite/compat/oracle/r/sp-code.result @@ -415,8 +415,8 @@ END; SHOW FUNCTION CODE f1; Pos Instruction 0 set i@0 0 -1 set i@0 (i@0 + 1) -2 jump_if_not 1(1) (i@0 >= 5) +1 set i@0 i@0 + 1 +2 jump_if_not 1(1) i@0 >= 5 3 jump 4 4 freturn 3 i@0 SELECT f1() FROM DUAL; @@ -437,8 +437,8 @@ END; SHOW FUNCTION CODE f1; Pos Instruction 0 set i@0 0 -1 set i@0 (i@0 + 1) -2 jump_if_not 1(0) (i@0 >= 5) +1 set i@0 i@0 + 1 +2 jump_if_not 1(0) i@0 >= 5 3 jump 4 4 freturn 3 i@0 SELECT f1() FROM DUAL; @@ -466,8 +466,8 @@ SHOW FUNCTION CODE f1; Pos Instruction 0 set i@0 0 1 jump 5 -2 set i@0 (i@0 + 1) -3 jump_if_not 8(8) (i@0 >= 5) +2 set i@0 i@0 + 1 +3 jump_if_not 8(8) i@0 >= 5 4 jump 10 5 hpush_jump 2 1 EXIT 6 set i@0 1000 @@ -506,15 +506,15 @@ SHOW PROCEDURE CODE p1; Pos Instruction 0 set i@1 0 1 jump 14 -2 set i@1 (i@1 + 1) -3 jump_if_not 8(8) (i@1 >= 5) +2 set i@1 i@1 + 1 +3 jump_if_not 8(8) i@1 >= 5 4 jump 10 5 hpush_jump 2 2 EXIT 6 set a@0 1000 7 hreturn 0 8 8 hpop 1 9 jump 5 -10 set i@1 (i@1 + 100) +10 set i@1 i@1 + 100 11 jump 12 12 set a@0 i@1 13 jump 17 @@ -528,3 +528,35 @@ SELECT @v; @v 105 DROP PROCEDURE p1; +# Testing RETURN in procedures +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN +IF a < 10 THEN +BEGIN +a:= a + 1; +RETURN; +END; +END IF; +a:= 200; +EXCEPTION +WHEN OTHERS THEN +BEGIN +a:= 100; +RETURN; +END; +END; +/ +SHOW PROCEDURE CODE p1; +Pos Instruction +0 jump 6 +1 jump_if_not 4(4) a@0 < 10 +2 set a@0 a@0 + 1 +3 preturn +4 set a@0 200 +5 jump 9 +6 hpush_jump 1 1 EXIT +7 set a@0 100 +8 preturn +9 hpop 1 +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index 3f5c7fa2eb3..ec68f45b5cd 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -677,3 +677,62 @@ f1() 1 DROP FUNCTION f1; DROP TABLE t1; +# Testing RETURN in procedures +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN +RETURN 10; +END; +/ +ERROR 42000: RETURN is only allowed in a FUNCTION +CREATE FUNCTION f1 (a INT) RETURN INT +AS +BEGIN +RETURN; +END; +/ +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '; +END' at line 4 +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN +IF a < 10 THEN +BEGIN +a:= a - 1; +RETURN; +END; +END IF; +a:= a + 1; +EXCEPTION +WHEN OTHERS THEN RETURN; +END; +/ +SET @v=10; +CALL p1(@v); +SELECT @v; +@v +11 +SET @v=9; +CALL p1(@v); +SELECT @v; +@v +8 +DROP PROCEDURE p1; +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN +DROP TABLE t1_non_existent; +EXCEPTION +WHEN OTHERS THEN +BEGIN +a:= 100; +RETURN; +END; +END; +/ +SET @v=10; +CALL p1(@v); +SELECT @v; +@v +100 +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/t/sp-code.test b/mysql-test/suite/compat/oracle/t/sp-code.test index 09f3a8aa30c..082d6bc4249 100644 --- a/mysql-test/suite/compat/oracle/t/sp-code.test +++ b/mysql-test/suite/compat/oracle/t/sp-code.test @@ -416,3 +416,28 @@ set @v= 10; CALL p1(@v); SELECT @v; DROP PROCEDURE p1; + + +--echo # Testing RETURN in procedures +DELIMITER /; +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN + IF a < 10 THEN + BEGIN + a:= a + 1; + RETURN; + END; + END IF; + a:= 200; +EXCEPTION + WHEN OTHERS THEN + BEGIN + a:= 100; + RETURN; + END; +END; +/ +DELIMITER ;/ +SHOW PROCEDURE CODE p1; +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index debc0f326c8..2ececbb905e 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -735,3 +735,69 @@ DELIMITER ;/ SELECT f1() FROM DUAL; DROP FUNCTION f1; DROP TABLE t1; + + +--echo # Testing RETURN in procedures + +DELIMITER /; +--error ER_SP_BADRETURN +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN + RETURN 10; +END; +/ +DELIMITER ;/ + +DELIMITER /; +--error ER_PARSE_ERROR +CREATE FUNCTION f1 (a INT) RETURN INT +AS +BEGIN + RETURN; +END; +/ +DELIMITER ;/ + +DELIMITER /; +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN + IF a < 10 THEN + BEGIN + a:= a - 1; + RETURN; + END; + END IF; + a:= a + 1; +EXCEPTION + WHEN OTHERS THEN RETURN; +END; +/ +DELIMITER ;/ +SET @v=10; +CALL p1(@v); +SELECT @v; +SET @v=9; +CALL p1(@v); +SELECT @v; +DROP PROCEDURE p1; + +DELIMITER /; +CREATE PROCEDURE p1 (a IN OUT INT) +AS +BEGIN + DROP TABLE t1_non_existent; +EXCEPTION + WHEN OTHERS THEN + BEGIN + a:= 100; + RETURN; + END; +END; +/ +DELIMITER ;/ +SET @v=10; +CALL p1(@v); +SELECT @v; +DROP PROCEDURE p1; diff --git a/sql/sp_head.h b/sql/sp_head.h index 3d55d9e911d..fe83c27c769 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1030,6 +1030,41 @@ private: }; // class sp_instr_jump_if_not : public sp_instr_jump +class sp_instr_preturn : public sp_instr +{ + sp_instr_preturn(const sp_instr_preturn &); /**< Prevent use of these */ + void operator=(sp_instr_preturn &); + +public: + + sp_instr_preturn(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx) + {} + + virtual ~sp_instr_preturn() + {} + + virtual int execute(THD *thd, uint *nextp) + { + DBUG_ENTER("sp_instr_preturn::execute"); + *nextp= UINT_MAX; + DBUG_RETURN(0); + } + + virtual void print(String *str) + { + str->append(STRING_WITH_LEN("preturn")); + } + + virtual uint opt_mark(sp_head *sp, List *leads) + { + marked= 1; + return UINT_MAX; + } + +}; // class sp_instr_preturn : public sp_instr + + class sp_instr_freturn : public sp_instr { sp_instr_freturn(const sp_instr_freturn &); /**< Prevent use of these */ diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 05291cf1033..956da01df00 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -2963,6 +2963,22 @@ sp_proc_stmt_return: if (sp->restore_lex(thd)) MYSQL_YYABORT; } + | RETURN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->m_type != TYPE_ENUM_PROCEDURE) + { + thd->parse_error(); + MYSQL_YYABORT; + } + sp_instr_preturn *i; + i= new (thd->mem_root) + sp_instr_preturn(sp->instructions(), lex->spcont); + if (i == NULL || sp->add_instr(i)) + MYSQL_YYABORT; + } ; opt_sp_proc_stmt_exit_when_clause: