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;
@v
324
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;
@v
10
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;
CALL p1(@v);
SELECT @v;
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;
CALL sp1(@v, 30002);
SELECT @v;
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)
}
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=
new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this);
new (thd->mem_root) sp_label(name, ip, type, this);
if (!label)
return NULL;
......@@ -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,
LEX_STRING name,
sp_condition_value *value)
......
......@@ -402,10 +402,18 @@ class sp_pcontext : public Sql_alloc
// 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_current_loop_start();
sp_label *last_label()
{
sp_label *label= m_labels.head();
......
......@@ -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)
{
sp_label *lab= spcont->push_label(thd, label, sphead->instructions());
lab->type= sp_label::BEGIN;
spcont->push_label(thd, label, sphead->instructions(), sp_label::BEGIN);
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)
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name.str);
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
points to the block hpop/cpop cleanup instructions,
......@@ -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)
{
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)
my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name.str);
return true;
}
lab= spcont->push_label(thd, label_name, sphead->instructions());
lab->type= sp_label::ITERATION;
spcont->push_label(thd, label_name, sphead->instructions(),
sp_label::ITERATION);
return false;
}
......@@ -5538,7 +5567,8 @@ bool LEX::sp_push_loop_empty_label(THD *thd)
if (maybe_start_compound_statement(thd))
return true;
/* 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;
}
......
......@@ -3152,6 +3152,9 @@ struct LEX: public Query_tables_list
uint executable_section_ip,
uint exception_count);
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_iterate_statement(THD *thd, const LEX_STRING label_name);
......
......@@ -1260,6 +1260,7 @@ END_OF_INPUT
%type <NONE> sp_proc_stmt_if
%type <NONE> sp_labeled_control sp_unlabeled_control
%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_iterate
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
......@@ -2858,6 +2859,7 @@ sp_proc_stmt_in_returns_clause:
sp_proc_stmt:
sp_proc_stmt_in_returns_clause
| sp_proc_stmt_statement
| sp_proc_stmt_exit
| sp_proc_stmt_leave
| sp_proc_stmt_iterate
| sp_proc_stmt_open
......@@ -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:
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