Commit 8feb9842 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-10411 Providing compatibility for basic PL/SQL constructs

Part 5: EXIT statement

Adding unconditional EXIT statement:

  EXIT [ label ]

Conditional EXIT statements with WHERE clause
will be added in a separate patch.
parent 765d9d64
...@@ -396,3 +396,113 @@ SELECT @v; ...@@ -396,3 +396,113 @@ SELECT @v;
@v @v
324 324
DROP PROCEDURE p1; DROP PROCEDURE p1;
#
# Testing EXIT statement
#
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
END LOOP;
RETURN i;
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)
3 jump 4
4 freturn 3 i@0
SELECT f1() FROM DUAL;
f1()
5
DROP FUNCTION f1;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
BEGIN
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN i:= 1000;
END;
END LOOP;
RETURN i;
END;
/
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)
4 jump 10
5 hpush_jump 2 1 EXIT
6 set i@0 1000
7 hreturn 0 8
8 hpop 1
9 jump 5
10 freturn 3 i@0
SELECT f1() FROM DUAL;
f1()
5
DROP FUNCTION f1;
CREATE PROCEDURE p1(a IN OUT INT)
IS
i INT := 0;
BEGIN
LOOP
LOOP
BEGIN
i:= i + 1;
IF i >=5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN a:=1000;
END;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
a:= i;
EXCEPTION
WHEN OTHERS THEN a:=11;
END;
/
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)
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)
11 jump 12
12 set a@0 i@1
13 jump 17
14 hpush_jump 5 2 EXIT
15 set a@0 11
16 hreturn 0 17
17 hpop 1
set @v= 10;
CALL p1(@v);
SELECT @v;
@v
105
DROP PROCEDURE p1;
...@@ -493,3 +493,95 @@ SELECT @v; ...@@ -493,3 +493,95 @@ SELECT @v;
@v @v
10 10
DROP PROCEDURE sp1; DROP PROCEDURE sp1;
#
# Testing EXIT statement
#
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
EXIT;
END;
/
ERROR 42000: EXIT with no matching label:
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<lable1>>
BEGIN
<<label2>>
LOOP
EXIT label1;
END LOOP;
END;
END;
/
ERROR 42000: EXIT with no matching label: label1
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
SELECT f1() FROM DUAL;
f1()
105
DROP FUNCTION f1;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<label1>>
LOOP
<<label2>>
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT label2;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
SELECT f1() FROM DUAL;
f1()
105
DROP FUNCTION f1;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<label1>>
LOOP
<<label2>>
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT label1;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
SELECT f1() FROM DUAL;
f1()
5
DROP FUNCTION f1;
...@@ -319,3 +319,81 @@ SET @v=10; ...@@ -319,3 +319,81 @@ SET @v=10;
CALL p1(@v); CALL p1(@v);
SELECT @v; SELECT @v;
DROP PROCEDURE p1; DROP PROCEDURE p1;
--echo #
--echo # Testing EXIT statement
--echo #
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
BEGIN
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN i:= 1000;
END;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE PROCEDURE p1(a IN OUT INT)
IS
i INT := 0;
BEGIN
LOOP
LOOP
BEGIN
i:= i + 1;
IF i >=5 THEN
EXIT;
END IF;
EXCEPTION
WHEN OTHERS THEN a:=1000;
END;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
a:= i;
EXCEPTION
WHEN OTHERS THEN a:=11;
END;
/
DELIMITER ;/
SHOW PROCEDURE CODE p1;
set @v= 10;
CALL p1(@v);
SELECT @v;
DROP PROCEDURE p1;
...@@ -525,3 +525,110 @@ SET @v=10; ...@@ -525,3 +525,110 @@ SET @v=10;
CALL sp1(@v, 30002); CALL sp1(@v, 30002);
SELECT @v; SELECT @v;
DROP PROCEDURE sp1; DROP PROCEDURE sp1;
--echo #
--echo # Testing EXIT statement
--echo #
DELIMITER /;
--error ER_SP_LILABEL_MISMATCH
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
EXIT;
END;
/
DELIMITER ;/
DELIMITER /;
--error ER_SP_LILABEL_MISMATCH
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<lable1>>
BEGIN
<<label2>>
LOOP
EXIT label1;
END LOOP;
END;
END;
/
DELIMITER ;/
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
LOOP
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<label1>>
LOOP
<<label2>>
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT label2;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DELIMITER /;
CREATE FUNCTION f1 RETURN INT
IS
i INT := 0;
BEGIN
<<label1>>
LOOP
<<label2>>
LOOP
i:= i + 1;
IF i >= 5 THEN
EXIT label1;
END IF;
END LOOP;
i:= i + 100;
EXIT;
END LOOP;
RETURN i;
END;
/
DELIMITER ;/
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
...@@ -197,10 +197,11 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name) ...@@ -197,10 +197,11 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
} }
sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip) sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip,
sp_label::enum_type type)
{ {
sp_label *label= sp_label *label=
new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this); new (thd->mem_root) sp_label(name, ip, type, this);
if (!label) if (!label)
return NULL; return NULL;
...@@ -236,6 +237,23 @@ sp_label *sp_pcontext::find_label(const LEX_STRING name) ...@@ -236,6 +237,23 @@ sp_label *sp_pcontext::find_label(const LEX_STRING name)
} }
sp_label *sp_pcontext::find_label_current_loop_start()
{
List_iterator_fast<sp_label> li(m_labels);
sp_label *lab;
while ((lab= li++))
{
if (lab->type == sp_label::ITERATION)
return lab;
}
// See a comment in sp_pcontext::find_label()
return (m_parent && (m_scope == REGULAR_SCOPE)) ?
m_parent->find_label_current_loop_start() :
NULL;
}
bool sp_pcontext::add_condition(THD *thd, bool sp_pcontext::add_condition(THD *thd,
LEX_STRING name, LEX_STRING name,
sp_condition_value *value) sp_condition_value *value)
......
...@@ -402,10 +402,18 @@ class sp_pcontext : public Sql_alloc ...@@ -402,10 +402,18 @@ class sp_pcontext : public Sql_alloc
// Labels. // Labels.
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
sp_label *push_label(THD *thd, const LEX_STRING name, uint ip); sp_label *push_label(THD *thd, const LEX_STRING name, uint ip,
sp_label::enum_type type);
sp_label *push_label(THD *thd, const LEX_STRING name, uint ip)
{
return push_label(thd, name, ip, sp_label::IMPLICIT);
}
sp_label *find_label(const LEX_STRING name); sp_label *find_label(const LEX_STRING name);
sp_label *find_label_current_loop_start();
sp_label *last_label() sp_label *last_label()
{ {
sp_label *label= m_labels.head(); sp_label *label= m_labels.head();
......
...@@ -5312,8 +5312,7 @@ bool LEX::sp_handler_declaration_finalize(THD *thd, int type) ...@@ -5312,8 +5312,7 @@ bool LEX::sp_handler_declaration_finalize(THD *thd, int type)
void LEX::sp_block_init(THD *thd, const LEX_STRING label) void LEX::sp_block_init(THD *thd, const LEX_STRING label)
{ {
sp_label *lab= spcont->push_label(thd, label, sphead->instructions()); spcont->push_label(thd, label, sphead->instructions(), sp_label::BEGIN);
lab->type= sp_label::BEGIN;
spcont= spcont->push_context(thd, sp_pcontext::REGULAR_SCOPE); spcont= spcont->push_context(thd, sp_pcontext::REGULAR_SCOPE);
} }
...@@ -5478,7 +5477,12 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name) ...@@ -5478,7 +5477,12 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name)
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name.str); my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name.str);
return true; return true;
} }
return sp_exit_block(thd, lab);
}
bool LEX::sp_exit_block(THD *thd, sp_label *lab)
{
/* /*
When jumping to a BEGIN-END block end, the target jump When jumping to a BEGIN-END block end, the target jump
points to the block hpop/cpop cleanup instructions, points to the block hpop/cpop cleanup instructions,
...@@ -5493,6 +5497,31 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name) ...@@ -5493,6 +5497,31 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name)
} }
bool LEX::sp_exit_statement(THD *thd)
{
sp_label *lab= spcont->find_label_current_loop_start();
if (!lab)
{
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", "");
return true;
}
DBUG_ASSERT(lab->type == sp_label::ITERATION);
return sp_exit_block(thd, lab);
}
bool LEX::sp_exit_statement(THD *thd, const LEX_STRING label_name)
{
sp_label *lab= spcont->find_label(label_name);
if (!lab || lab->type != sp_label::ITERATION)
{
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", label_name);
return true;
}
return sp_exit_block(thd, lab);
}
bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name) bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name)
{ {
sp_label *lab= spcont->find_label(label_name); sp_label *lab= spcont->find_label(label_name);
...@@ -5527,8 +5556,8 @@ bool LEX::sp_push_loop_label(THD *thd, const LEX_STRING label_name) ...@@ -5527,8 +5556,8 @@ bool LEX::sp_push_loop_label(THD *thd, const LEX_STRING label_name)
my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name.str); my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name.str);
return true; return true;
} }
lab= spcont->push_label(thd, label_name, sphead->instructions()); spcont->push_label(thd, label_name, sphead->instructions(),
lab->type= sp_label::ITERATION; sp_label::ITERATION);
return false; return false;
} }
...@@ -5538,7 +5567,8 @@ bool LEX::sp_push_loop_empty_label(THD *thd) ...@@ -5538,7 +5567,8 @@ bool LEX::sp_push_loop_empty_label(THD *thd)
if (maybe_start_compound_statement(thd)) if (maybe_start_compound_statement(thd))
return true; return true;
/* Unlabeled controls get an empty label. */ /* Unlabeled controls get an empty label. */
spcont->push_label(thd, empty_lex_str, sphead->instructions()); spcont->push_label(thd, empty_lex_str, sphead->instructions(),
sp_label::ITERATION);
return false; return false;
} }
......
...@@ -3152,6 +3152,9 @@ struct LEX: public Query_tables_list ...@@ -3152,6 +3152,9 @@ struct LEX: public Query_tables_list
uint executable_section_ip, uint executable_section_ip,
uint exception_count); uint exception_count);
bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive);
bool sp_exit_block(THD *thd, sp_label *lab);
bool sp_exit_statement(THD *thd);
bool sp_exit_statement(THD *thd, const LEX_STRING label_name);
bool sp_leave_statement(THD *thd, const LEX_STRING label_name); bool sp_leave_statement(THD *thd, const LEX_STRING label_name);
bool sp_iterate_statement(THD *thd, const LEX_STRING label_name); bool sp_iterate_statement(THD *thd, const LEX_STRING label_name);
......
...@@ -1260,6 +1260,7 @@ END_OF_INPUT ...@@ -1260,6 +1260,7 @@ END_OF_INPUT
%type <NONE> sp_proc_stmt_if %type <NONE> sp_proc_stmt_if
%type <NONE> sp_labeled_control sp_unlabeled_control %type <NONE> sp_labeled_control sp_unlabeled_control
%type <NONE> sp_labeled_block sp_unlabeled_block sp_unlabeled_block_not_atomic %type <NONE> sp_labeled_block sp_unlabeled_block sp_unlabeled_block_not_atomic
%type <NONE> sp_proc_stmt_exit
%type <NONE> sp_proc_stmt_leave %type <NONE> sp_proc_stmt_leave
%type <NONE> sp_proc_stmt_iterate %type <NONE> sp_proc_stmt_iterate
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close %type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
...@@ -2858,6 +2859,7 @@ sp_proc_stmt_in_returns_clause: ...@@ -2858,6 +2859,7 @@ sp_proc_stmt_in_returns_clause:
sp_proc_stmt: sp_proc_stmt:
sp_proc_stmt_in_returns_clause sp_proc_stmt_in_returns_clause
| sp_proc_stmt_statement | sp_proc_stmt_statement
| sp_proc_stmt_exit
| sp_proc_stmt_leave | sp_proc_stmt_leave
| sp_proc_stmt_iterate | sp_proc_stmt_iterate
| sp_proc_stmt_open | sp_proc_stmt_open
...@@ -2961,6 +2963,19 @@ sp_proc_stmt_return: ...@@ -2961,6 +2963,19 @@ sp_proc_stmt_return:
} }
; ;
sp_proc_stmt_exit:
EXIT_SYM
{
if (Lex->sp_exit_statement(thd))
MYSQL_YYABORT;
}
| EXIT_SYM label_ident
{
if (Lex->sp_exit_statement(thd, $2))
MYSQL_YYABORT;
}
;
sp_proc_stmt_leave: sp_proc_stmt_leave:
LEAVE_SYM label_ident LEAVE_SYM label_ident
{ {
......
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