Skip to content

Commit

Permalink
MDEV-10411 Providing compatibility for basic PL/SQL constructs
Browse files Browse the repository at this point in the history
Fixed that the ITERATE statement inside a FOR LOOP statement did not
increment the index variable before jumping to the beginning
of the loop, which caused the loop to repeat endlessly.
  • Loading branch information
Alexander Barkov committed Apr 5, 2017
1 parent 2ea6349 commit 442ea81
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 0 deletions.
120 changes: 120 additions & 0 deletions mysql-test/suite/compat/oracle/r/sp-code.result
Expand Up @@ -677,3 +677,123 @@ SELECT f1(2, 3, 2, 3) FROM DUAL;
f1(2, 3, 2, 3)
2004
DROP FUNCTION f1;
# Testing labeled ITERATE in a labeled FOR LOOP
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
RETURN total;
END;
/
SHOW FUNCTION CODE f1;
Pos Instruction
0 set total@1 0
1 set i@2 1
2 set [upper_bound]@3 a@0
3 jump_if_not 11(11) (i@2 <= [upper_bound]@3)
4 set total@1 (total@1 + 1000)
5 jump_if_not 8(8) (i@2 = 5)
6 set i@2 (i@2 + 1)
7 jump 3
8 set total@1 (total@1 + 1)
9 set i@2 (i@2 + 1)
10 jump 3
11 freturn 3 total@1
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
f1(3) f1(4) f1(5) f1(6)
3003 4004 5004 6005
DROP FUNCTION f1;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
FOR j IN 1 .. 2
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
SHOW FUNCTION CODE f1;
Pos Instruction
0 set total@1 0
1 set i@2 1
2 set [upper_bound]@3 a@0
3 jump_if_not 16(16) (i@2 <= [upper_bound]@3)
4 set j@4 1
5 set [upper_bound]@5 2
6 jump_if_not 14(14) (j@4 <= [upper_bound]@5)
7 set total@1 (total@1 + 1000)
8 jump_if_not 11(11) (i@2 = 5)
9 set i@2 (i@2 + 1)
10 jump 3
11 set total@1 (total@1 + 1)
12 set j@4 (j@4 + 1)
13 jump 6
14 set i@2 (i@2 + 1)
15 jump 3
16 freturn 3 total@1
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
f1(3) f1(4) f1(5) f1(6)
6006 8008 9008 11010
DROP FUNCTION f1;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<lj>>
FOR j IN 1 .. 2
LOOP
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
SHOW FUNCTION CODE f1;
Pos Instruction
0 set total@1 0
1 set j@2 1
2 set [upper_bound]@3 2
3 jump_if_not 16(16) (j@2 <= [upper_bound]@3)
4 set i@4 1
5 set [upper_bound]@5 a@0
6 jump_if_not 14(14) (i@4 <= [upper_bound]@5)
7 set total@1 (total@1 + 1000)
8 jump_if_not 11(11) (i@4 = 5)
9 set i@4 (i@4 + 1)
10 jump 6
11 set total@1 (total@1 + 1)
12 set i@4 (i@4 + 1)
13 jump 6
14 set j@2 (j@2 + 1)
15 jump 3
16 freturn 3 total@1
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
f1(3) f1(4) f1(5) f1(6)
6006 8008 10008 12010
DROP FUNCTION f1;
47 changes: 47 additions & 0 deletions mysql-test/suite/compat/oracle/r/sp.result
Expand Up @@ -899,3 +899,50 @@ SELECT f1(2, 3, 2, 3) FROM DUAL;
f1(2, 3, 2, 3)
2004
DROP FUNCTION f1;
# Testing labeled ITERATE in a labeled FOR LOOP statement
CREATE FUNCTION f1 (a INT, b INT, blim INT) RETURN INT
AS
total INT := 0;
BEGIN
<<la>>
FOR ia IN 1 .. a
LOOP
total:= total + 1000;
DECLARE
ib INT:= 1;
BEGIN
WHILE ib <= b
LOOP
IF ib > blim THEN
ITERATE la;
END IF;
ib:= ib + 1;
total:= total + 1;
END LOOP;
END;
END LOOP la;
RETURN total;
END;
/
SHOW FUNCTION CODE f1;
Pos Instruction
0 set total@3 0
1 set ia@4 1
2 set [upper_bound]@5 a@0
3 jump_if_not 15(15) (ia@4 <= [upper_bound]@5)
4 set total@3 (total@3 + 1000)
5 set ib@6 1
6 jump_if_not 13(13) (ib@6 <= b@1)
7 jump_if_not 10(10) (ib@6 > blim@2)
8 set ia@4 (ia@4 + 1)
9 jump 3
10 set ib@6 (ib@6 + 1)
11 set total@3 (total@3 + 1)
12 jump 6
13 set ia@4 (ia@4 + 1)
14 jump 3
15 freturn 3 total@3
SELECT f1(3,3,0), f1(3,3,1), f1(3,3,2), f1(3,3,3), f1(3,3,4) FROM DUAL;
f1(3,3,0) f1(3,3,1) f1(3,3,2) f1(3,3,3) f1(3,3,4)
3000 3003 3006 3009 3009
DROP FUNCTION f1;
79 changes: 79 additions & 0 deletions mysql-test/suite/compat/oracle/t/sp-code.test
Expand Up @@ -486,6 +486,7 @@ SELECT f1(3, 100) FROM DUAL;
SELECT f1(3, 2) FROM DUAL;
DROP FUNCTION f1;


--echo # Testing labeled FOR LOOP statement

DELIMITER /;
Expand Down Expand Up @@ -514,3 +515,81 @@ SELECT f1(2, 1, 2, 2) FROM DUAL;
SELECT f1(2, 2, 2, 2) FROM DUAL;
SELECT f1(2, 3, 2, 3) FROM DUAL;
DROP FUNCTION f1;


--echo # Testing labeled ITERATE in a labeled FOR LOOP

DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;


DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<li>>
FOR i IN 1 .. a
LOOP
FOR j IN 1 .. 2
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;


DELIMITER /;
CREATE FUNCTION f1(a INT) RETURN INT
AS
total INT:= 0;
BEGIN
<<lj>>
FOR j IN 1 .. 2
LOOP
<<li>>
FOR i IN 1 .. a
LOOP
total:= total + 1000;
IF i = 5 THEN
ITERATE li;
END IF;
total:= total + 1;
END LOOP;
END LOOP;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL;
DROP FUNCTION f1;
33 changes: 33 additions & 0 deletions mysql-test/suite/compat/oracle/t/sp.test
Expand Up @@ -963,3 +963,36 @@ SELECT f1(2, 1, 2, 2) FROM DUAL;
SELECT f1(2, 2, 2, 2) FROM DUAL;
SELECT f1(2, 3, 2, 3) FROM DUAL;
DROP FUNCTION f1;


--echo # Testing labeled ITERATE in a labeled FOR LOOP statement

DELIMITER /;
CREATE FUNCTION f1 (a INT, b INT, blim INT) RETURN INT
AS
total INT := 0;
BEGIN
<<la>>
FOR ia IN 1 .. a
LOOP
total:= total + 1000;
DECLARE
ib INT:= 1;
BEGIN
WHILE ib <= b
LOOP
IF ib > blim THEN
ITERATE la;
END IF;
ib:= ib + 1;
total:= total + 1;
END LOOP;
END;
END LOOP la;
RETURN total;
END;
/
DELIMITER ;/
SHOW FUNCTION CODE f1;
SELECT f1(3,3,0), f1(3,3,1), f1(3,3,2), f1(3,3,3), f1(3,3,4) FROM DUAL;
DROP FUNCTION f1;
18 changes: 18 additions & 0 deletions sql/sp_pcontext.h
Expand Up @@ -268,6 +268,12 @@ class sp_pcontext : public Sql_alloc
HANDLER_SCOPE
};

class Lex_for_loop: public Lex_for_loop_st
{
public:
Lex_for_loop() { init(); }
};

public:
sp_pcontext();
~sp_pcontext();
Expand Down Expand Up @@ -513,6 +519,15 @@ class sp_pcontext : public Sql_alloc
uint current_cursor_count() const
{ return m_cursor_offset + m_cursors.elements(); }

void set_for_loop(const Lex_for_loop_st &for_loop)
{
m_for_loop.init(for_loop);
}
const Lex_for_loop_st &for_loop()
{
return m_for_loop;
}

private:
/// Constructor for a tree node.
/// @param prev the parent parsing context
Expand Down Expand Up @@ -583,6 +598,9 @@ class sp_pcontext : public Sql_alloc

/// Scope of this parsing context.
enum_scope m_scope;

/// FOR LOOP characteristics
Lex_for_loop m_for_loop;
}; // class sp_pcontext : public Sql_alloc


Expand Down
10 changes: 10 additions & 0 deletions sql/sql_lex.cc
Expand Up @@ -5317,6 +5317,7 @@ bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop)
*/
bool LEX::sp_for_loop_index_and_bounds(THD *thd, const Lex_for_loop_st &loop)
{
spcont->set_for_loop(loop);
sphead->reset_lex(thd);
if (thd->lex->sp_for_loop_condition(thd, loop))
return true;
Expand Down Expand Up @@ -5674,6 +5675,15 @@ bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name)
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", label_name.str);
return true;
}
if (lab->ctx->for_loop().m_index)
{
// We're in a FOR loop, increment the index variable before backward jump
sphead->reset_lex(thd);
DBUG_ASSERT(this != thd->lex);
if (thd->lex->sp_for_loop_increment(thd, lab->ctx->for_loop()) ||
thd->lex->sphead->restore_lex(thd))
return true;
}
return sp_change_context(thd, lab->ctx, false) ||
sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */
}
Expand Down
10 changes: 10 additions & 0 deletions sql/structs.h
Expand Up @@ -675,6 +675,16 @@ struct Lex_for_loop_st
class sp_variable *m_index;
class sp_variable *m_upper_bound;
int m_direction;
void init()
{
m_index= 0;
m_upper_bound= 0;
m_direction= 0;
}
void init(const Lex_for_loop_st &other)
{
*this= other;
}
};


Expand Down

0 comments on commit 442ea81

Please sign in to comment.