Commit 534a2bf1 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-32275 getting error 'Illegal parameter data types row and bigint for...

MDEV-32275 getting error 'Illegal parameter data types row and bigint for operation '+' ' when using ITERATE in a FOR..DO

An "ITERATE innerLoop" did not work properly inside
a WHILE loop, which itself is inside an outer FOR loop:

outerLoop:
  FOR
   ...
   innerLoop:
    WHILE
      ...
      ITERATE innerLoop;
      ...
    END WHILE;
    ...
  END FOR;

It erroneously generated an integer increment code for the outer FOR loop.
There were two problems:
1. "ITERATE innerLoop" worked like "ITERATE outerLoop"
2. It was always integer increment, even in case of FOR cursor loops.

Background:
- A FOR loop automatically creates a dedicated sp_pcontext stack entry,
  to put the iteration and bound variables on it.

- Other loop types (LOOP, WHILE, REPEAT), do not generate a dedicated
  slack entry.

  The old code erroneously assumed that sp_pcontext::m_for_loop
  either describes the most inner loop (in case the inner loop is FOR),
  or is empty (in case the inner loop is not FOR).

  But in fact, sp_pcontext::m_for_loop is never empty inside a FOR loop:
  it describes the closest FOR loop, even if this FOR loop has nested
  non-FOR loops inside.

  So when we're near the ITERATE statement in the above script,
  sp_pcontext::m_for_loop is not empty - it stores information about
  the FOR loop labeled as "outrLoop:".

Fix:
- Adding a new member sp_pcontext::Lex_for_loop::m_start_label,
  to remember the explicit or the auto-generated label correspoding
  to the start of the FOR body. It's used during generation
  of "ITERATE loop_label" code to check if "loop_label" belongs
  to the current FOR loop pointed by sp_pcontext::m_for_loop,
  or belongs to a non-FOR nested loop.

- Adding LEX methods sp_for_loop_intrange_iterate() and
  sp_for_loop_cursor_iterate() to reuse the code between
  methods handling:
  * ITERATE
  * END FOR

- Adding a test for Lex_for_loop::is_for_loop_cursor()
  and generate a code either a cursor fetch, or for an integer increment.
  Before this change, it always erroneously generated an integer increment
  version.

- Cleanup: Initialize Lex_for_loop_st::m_cursor_offset inside
  Lex_for_loop_st::init(), to avoid not initialized members.

- Cleanup: Removing a redundant method:
    Lex_for_loop_st::init(const Lex_for_loop_st &other)
  Using Lex_for_loop_st::operator(const Lex_for_loop_st &other) instead.
parent e2da748c
...@@ -1356,3 +1356,330 @@ drop function f1; ...@@ -1356,3 +1356,330 @@ drop function f1;
# #
# End of 10.3 tests # End of 10.3 tests
# #
#
# Start of 10.4 tests
#
#
# MDEV-32275 getting error 'Illegal parameter data types row and bigint for operation '+' ' when using ITERATE in a FOR..DO
#
#
# Unlabeled FOR/cursor with a nested labeled LOOP inside
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _row IN (SELECT '' AS a) DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _row.a = 'v1'
THEN
SELECT 'start of THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 cpush [implicit_cursor]@0
2 cursor_copy_struct [implicit_cursor] _row@1
3 copen [implicit_cursor]@0
4 cfetch [implicit_cursor]@0 _row@1
5 jump_if_not 19(19) `[implicit_cursor]`%FOUND
6 stmt 0 "SELECT 'start of outerLoop'"
7 stmt 0 "SELECT 'start of innerLoop'"
8 jump_if_not 10(10) loopDone@0
9 jump 16
10 set loopDone@0 1
11 jump_if_not 14(14) _row.a@1["a"] = 'v1'
12 stmt 0 "SELECT 'start of THEN block'"
13 jump 7
14 stmt 0 "SELECT 'end of innerLoop'"
15 jump 7
16 stmt 0 "SELECT 'end of outerLoop'"
17 cfetch [implicit_cursor]@0 _row@1
18 jump 5
19 cpop 1
DROP PROCEDURE p1;
#
# Labeled FOR/cursor with a nested labeled LOOP
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _row IN (SELECT '' AS a) DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _row.a = 'v1'
THEN
SELECT 'start of IF/v1/THEN block';
ITERATE innerLoop;
END IF;
IF _row.a = 'v2'
THEN
SELECT 'start of IF/v2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 cpush [implicit_cursor]@0
2 cursor_copy_struct [implicit_cursor] _row@1
3 copen [implicit_cursor]@0
4 cfetch [implicit_cursor]@0 _row@1
5 jump_if_not 23(23) `[implicit_cursor]`%FOUND
6 stmt 0 "SELECT 'start of outerLoop'"
7 stmt 0 "SELECT 'start of innerLoop'"
8 jump_if_not 10(10) loopDone@0
9 jump 20
10 set loopDone@0 1
11 jump_if_not 14(14) _row.a@1["a"] = 'v1'
12 stmt 0 "SELECT 'start of IF/v1/THEN block'"
13 jump 7
14 jump_if_not 18(18) _row.a@1["a"] = 'v2'
15 stmt 0 "SELECT 'start of IF/v2/THEN block'"
16 cfetch [implicit_cursor]@0 _row@1
17 jump 5
18 stmt 0 "SELECT 'end of innerLoop'"
19 jump 7
20 stmt 0 "SELECT 'end of outerLoop'"
21 cfetch [implicit_cursor]@0 _row@1
22 jump 5
23 cpop 1
DROP PROCEDURE p1;
#
# Unlabeled FOR/integer with a labeled LOOP inside
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _index IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index = 1
THEN
SELECT 'start of THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 set _index@1 1
2 set [target_bound]@2 10
3 jump_if_not 19(19) _index@1 <= [target_bound]@2
4 stmt 0 "SELECT 'start of outerLoop'"
5 stmt 0 "SELECT 'start of innerLoop'"
6 jump_if_not 8(8) loopDone@0
7 jump 14
8 set loopDone@0 1
9 jump_if_not 12(12) _index@1 = 1
10 stmt 0 "SELECT 'start of THEN block'"
11 jump 5
12 stmt 0 "SELECT 'end of innerLoop'"
13 jump 5
14 stmt 0 "SELECT 'end of outerLoop'"
15 set _index@1 _index@1 + 1
16 jump 3
DROP PROCEDURE p1;
#
# Labeled FOR/integer with a labeled LOOP inside
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _index IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
IF _index = 2
THEN
SELECT 'start of IF/2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 set _index@1 1
2 set [target_bound]@2 10
3 jump_if_not 24(24) _index@1 <= [target_bound]@2
4 stmt 0 "SELECT 'start of outerLoop'"
5 stmt 0 "SELECT 'start of innerLoop'"
6 jump_if_not 8(8) loopDone@0
7 jump 18
8 set loopDone@0 1
9 jump_if_not 12(12) _index@1 = 1
10 stmt 0 "SELECT 'start of IF/1/THEN block'"
11 jump 5
12 jump_if_not 16(16) _index@1 = 2
13 stmt 0 "SELECT 'start of IF/2/THEN block'"
14 set _index@1 _index@1 + 1
15 jump 3
16 stmt 0 "SELECT 'end of innerLoop'"
17 jump 5
18 stmt 0 "SELECT 'end of outerLoop'"
19 set _index@1 _index@1 + 1
20 jump 3
DROP PROCEDURE p1;
#
# Unlabeled FOR/integer with a labeled FOR inside
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _index_outer IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop:
FOR _index_inner IN 1..10 DO
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index_inner = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END FOR;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 set _index_outer@1 1
2 set [target_bound]@2 10
3 jump_if_not 24(24) _index_outer@1 <= [target_bound]@2
4 stmt 0 "SELECT 'start of outerLoop'"
5 set _index_inner@3 1
6 set [target_bound]@4 10
7 jump_if_not 19(19) _index_inner@3 <= [target_bound]@4
8 stmt 0 "SELECT 'start of innerLoop'"
9 jump_if_not 11(11) loopDone@0
10 jump 19
11 set loopDone@0 1
12 jump_if_not 16(16) _index_inner@3 = 1
13 stmt 0 "SELECT 'start of IF/1/THEN block'"
14 set _index_inner@3 _index_inner@3 + 1
15 jump 7
16 stmt 0 "SELECT 'end of innerLoop'"
17 set _index_inner@3 _index_inner@3 + 1
18 jump 7
19 stmt 0 "SELECT 'end of outerLoop'"
20 set _index_outer@1 _index_outer@1 + 1
21 jump 3
DROP PROCEDURE p1;
#
# Labeled FOR/integer with a labeled FOR inside
#
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _index_outer IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop:
FOR _index_inner IN 1..10 DO
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index_inner = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
IF _index_inner = 2
THEN
SELECT 'start of IF/2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END FOR;
SELECT 'end of outerLoop';
END FOR;
END
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set loopDone@0 0
1 set _index_outer@1 1
2 set [target_bound]@2 10
3 jump_if_not 29(29) _index_outer@1 <= [target_bound]@2
4 stmt 0 "SELECT 'start of outerLoop'"
5 set _index_inner@3 1
6 set [target_bound]@4 10
7 jump_if_not 23(23) _index_inner@3 <= [target_bound]@4
8 stmt 0 "SELECT 'start of innerLoop'"
9 jump_if_not 11(11) loopDone@0
10 jump 23
11 set loopDone@0 1
12 jump_if_not 16(16) _index_inner@3 = 1
13 stmt 0 "SELECT 'start of IF/1/THEN block'"
14 set _index_inner@3 _index_inner@3 + 1
15 jump 7
16 jump_if_not 20(20) _index_inner@3 = 2
17 stmt 0 "SELECT 'start of IF/2/THEN block'"
18 set _index_outer@1 _index_outer@1 + 1
19 jump 3
20 stmt 0 "SELECT 'end of innerLoop'"
21 set _index_inner@3 _index_inner@3 + 1
22 jump 7
23 stmt 0 "SELECT 'end of outerLoop'"
24 set _index_outer@1 _index_outer@1 + 1
25 jump 3
DROP PROCEDURE p1;
#
# End of 10.4 tests
#
...@@ -975,3 +975,227 @@ drop function f1; ...@@ -975,3 +975,227 @@ drop function f1;
--echo # --echo #
--echo # End of 10.3 tests --echo # End of 10.3 tests
--echo # --echo #
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-32275 getting error 'Illegal parameter data types row and bigint for operation '+' ' when using ITERATE in a FOR..DO
--echo #
--echo #
--echo # Unlabeled FOR/cursor with a nested labeled LOOP inside
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _row IN (SELECT '' AS a) DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _row.a = 'v1'
THEN
SELECT 'start of THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Labeled FOR/cursor with a nested labeled LOOP
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _row IN (SELECT '' AS a) DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _row.a = 'v1'
THEN
SELECT 'start of IF/v1/THEN block';
ITERATE innerLoop;
END IF;
IF _row.a = 'v2'
THEN
SELECT 'start of IF/v2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Unlabeled FOR/integer with a labeled LOOP inside
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _index IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index = 1
THEN
SELECT 'start of THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Labeled FOR/integer with a labeled LOOP inside
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _index IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop: LOOP
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
IF _index = 2
THEN
SELECT 'start of IF/2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END LOOP;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Unlabeled FOR/integer with a labeled FOR inside
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _index_outer IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop:
FOR _index_inner IN 1..10 DO
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index_inner = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
SELECT 'end of innerLoop';
END FOR;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Labeled FOR/integer with a labeled FOR inside
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
outerLoop:
FOR _index_outer IN 1..10 DO
SELECT 'start of outerLoop';
innerLoop:
FOR _index_inner IN 1..10 DO
SELECT 'start of innerLoop';
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
IF _index_inner = 1
THEN
SELECT 'start of IF/1/THEN block';
ITERATE innerLoop;
END IF;
IF _index_inner = 2
THEN
SELECT 'start of IF/2/THEN block';
ITERATE outerLoop;
END IF;
SELECT 'end of innerLoop';
END FOR;
SELECT 'end of outerLoop';
END FOR;
END
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # End of 10.4 tests
--echo #
...@@ -206,3 +206,33 @@ SELECT f1(3), f1(4), f1(5) FROM DUAL; ...@@ -206,3 +206,33 @@ SELECT f1(3), f1(4), f1(5) FROM DUAL;
f1(3) f1(4) f1(5) f1(3) f1(4) f1(5)
6 8 8 6 8 8
DROP FUNCTION f1; DROP FUNCTION f1;
#
# End of 10.3 tests
#
#
# Start of 10.4 tests
#
#
# MDEV-32275 getting error 'Illegal parameter data types row and bigint for operation '+' ' when using ITERATE in a FOR..DO
#
CREATE OR REPLACE PROCEDURE forIterateBug()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _unused IN (SELECT '') DO
innerLoop: LOOP
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
BEGIN
ITERATE innerLoop;
END;
END LOOP;
END FOR;
END;
$$
CALL forIterateBug;
DROP PROCEDURE forIterateBug;
#
# End of 10.4 tests
#
...@@ -210,3 +210,41 @@ END; ...@@ -210,3 +210,41 @@ END;
DELIMITER ;/ DELIMITER ;/
SELECT f1(3), f1(4), f1(5) FROM DUAL; SELECT f1(3), f1(4), f1(5) FROM DUAL;
DROP FUNCTION f1; DROP FUNCTION f1;
--echo #
--echo # End of 10.3 tests
--echo #
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-32275 getting error 'Illegal parameter data types row and bigint for operation '+' ' when using ITERATE in a FOR..DO
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE forIterateBug()
BEGIN
DECLARE loopDone TINYINT DEFAULT FALSE;
FOR _unused IN (SELECT '') DO
innerLoop: LOOP
IF loopDone THEN
LEAVE innerLoop;
END IF;
SET loopDone = TRUE;
BEGIN
ITERATE innerLoop;
END;
END LOOP;
END FOR;
END;
$$
DELIMITER ;$$
CALL forIterateBug;
DROP PROCEDURE forIterateBug;
--echo #
--echo # End of 10.4 tests
--echo #
...@@ -371,7 +371,28 @@ class sp_pcontext : public Sql_alloc ...@@ -371,7 +371,28 @@ class sp_pcontext : public Sql_alloc
class Lex_for_loop: public Lex_for_loop_st class Lex_for_loop: public Lex_for_loop_st
{ {
public: public:
Lex_for_loop() { init(); } /*
The label poiting to the body start,
either explicit or automatically generated.
Used during generation of "ITERATE loop_label"
to check if "loop_label" is a FOR loop label.
- In case of a FOR loop, some additional code
(cursor fetch or iteger increment) is generated before
the backward jump to the beginning of the loop body.
- In case of other loop types (WHILE, REPEAT)
only the jump is generated.
*/
const sp_label *m_start_label;
Lex_for_loop()
:m_start_label(NULL)
{ Lex_for_loop_st::init(); }
Lex_for_loop(const Lex_for_loop_st &for_loop, const sp_label *start)
:m_start_label(start)
{
Lex_for_loop_st::operator=(for_loop);
}
}; };
public: public:
...@@ -679,9 +700,9 @@ class sp_pcontext : public Sql_alloc ...@@ -679,9 +700,9 @@ class sp_pcontext : public Sql_alloc
void set_for_loop(const Lex_for_loop_st &for_loop) void set_for_loop(const Lex_for_loop_st &for_loop)
{ {
m_for_loop.init(for_loop); m_for_loop= Lex_for_loop(for_loop, last_label());
} }
const Lex_for_loop_st &for_loop() const Lex_for_loop &for_loop()
{ {
return m_for_loop; return m_for_loop;
} }
......
...@@ -6362,7 +6362,7 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop) ...@@ -6362,7 +6362,7 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
} }
bool LEX::sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop) bool LEX::sp_for_loop_intrange_iterate(THD *thd, const Lex_for_loop_st &loop)
{ {
sphead->reset_lex(thd); sphead->reset_lex(thd);
...@@ -6372,13 +6372,12 @@ bool LEX::sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop) ...@@ -6372,13 +6372,12 @@ bool LEX::sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop)
thd->lex->sphead->restore_lex(thd))) thd->lex->sphead->restore_lex(thd)))
return true; return true;
// Generate a jump to the beginning of the loop
DBUG_ASSERT(this == thd->lex); DBUG_ASSERT(this == thd->lex);
return sp_while_loop_finalize(thd); return false;
} }
bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop) bool LEX::sp_for_loop_cursor_iterate(THD *thd, const Lex_for_loop_st &loop)
{ {
sp_instr_cfetch *instr= sp_instr_cfetch *instr=
new (thd->mem_root) sp_instr_cfetch(sphead->instructions(), new (thd->mem_root) sp_instr_cfetch(sphead->instructions(),
...@@ -6386,10 +6385,10 @@ bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop) ...@@ -6386,10 +6385,10 @@ bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop)
if (unlikely(instr == NULL) || unlikely(sphead->add_instr(instr))) if (unlikely(instr == NULL) || unlikely(sphead->add_instr(instr)))
return true; return true;
instr->add_to_varlist(loop.m_index); instr->add_to_varlist(loop.m_index);
// Generate a jump to the beginning of the loop return false;
return sp_while_loop_finalize(thd);
} }
bool LEX::sp_for_loop_outer_block_finalize(THD *thd, bool LEX::sp_for_loop_outer_block_finalize(THD *thd,
const Lex_for_loop_st &loop) const Lex_for_loop_st &loop)
{ {
...@@ -6970,13 +6969,22 @@ bool LEX::sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name) ...@@ -6970,13 +6969,22 @@ bool LEX::sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name)
bool LEX::sp_continue_loop(THD *thd, sp_label *lab) bool LEX::sp_continue_loop(THD *thd, sp_label *lab)
{ {
if (lab->ctx->for_loop().m_index) const sp_pcontext::Lex_for_loop &for_loop= lab->ctx->for_loop();
/*
FOR loops need some additional instructions (e.g. an integer increment or
a cursor fetch) before the "jump to the start of the body" instruction.
We need to check two things here:
- If we're in a FOR loop at all.
- If the label pointed by "lab" belongs exactly to the nearest FOR loop,
rather than to a nested LOOP/WHILE/REPEAT inside the FOR.
*/
if (for_loop.m_index /* we're in some FOR loop */ &&
for_loop.m_start_label == lab /* lab belongs to the FOR loop */)
{ {
// We're in a FOR loop, increment the index variable before backward jump // We're in a FOR loop, and "ITERATE loop_label" belongs to this FOR loop.
sphead->reset_lex(thd); if (for_loop.is_for_loop_cursor() ?
DBUG_ASSERT(this != thd->lex); sp_for_loop_cursor_iterate(thd, for_loop) :
if (thd->lex->sp_for_loop_increment(thd, lab->ctx->for_loop()) || sp_for_loop_intrange_iterate(thd, for_loop))
thd->lex->sphead->restore_lex(thd))
return true; return true;
} }
return sp_change_context(thd, lab->ctx, false) || return sp_change_context(thd, lab->ctx, false) ||
......
...@@ -4158,7 +4158,7 @@ struct LEX: public Query_tables_list ...@@ -4158,7 +4158,7 @@ struct LEX: public Query_tables_list
const LEX_CSTRING *index, const LEX_CSTRING *index,
const Lex_for_loop_bounds_st &bounds); const Lex_for_loop_bounds_st &bounds);
bool sp_for_loop_intrange_condition_test(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_intrange_condition_test(THD *thd, const Lex_for_loop_st &loop);
bool sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_intrange_iterate(THD *thd, const Lex_for_loop_st &loop);
/* Cursor FOR LOOP methods */ /* Cursor FOR LOOP methods */
bool sp_for_loop_cursor_declarations(THD *thd, Lex_for_loop_st *loop, bool sp_for_loop_cursor_declarations(THD *thd, Lex_for_loop_st *loop,
...@@ -4174,7 +4174,7 @@ struct LEX: public Query_tables_list ...@@ -4174,7 +4174,7 @@ struct LEX: public Query_tables_list
Lex_for_loop_bounds_st *bounds, Lex_for_loop_bounds_st *bounds,
sp_lex_cursor *cur); sp_lex_cursor *cur);
bool sp_for_loop_cursor_condition_test(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_cursor_condition_test(THD *thd, const Lex_for_loop_st &loop);
bool sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &); bool sp_for_loop_cursor_iterate(THD *thd, const Lex_for_loop_st &);
/* Generic FOR LOOP methods*/ /* Generic FOR LOOP methods*/
...@@ -4232,9 +4232,12 @@ struct LEX: public Query_tables_list ...@@ -4232,9 +4232,12 @@ struct LEX: public Query_tables_list
*/ */
bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop) bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop)
{ {
return loop.is_for_loop_cursor() ? if (loop.is_for_loop_cursor() ?
sp_for_loop_cursor_finalize(thd, loop) : sp_for_loop_cursor_iterate(thd, loop) :
sp_for_loop_intrange_finalize(thd, loop); sp_for_loop_intrange_iterate(thd, loop))
return true;
// Generate a jump to the beginning of the loop
return sp_while_loop_finalize(thd);
} }
bool sp_for_loop_outer_block_finalize(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_outer_block_finalize(THD *thd, const Lex_for_loop_st &loop);
......
...@@ -744,13 +744,10 @@ struct Lex_for_loop_st ...@@ -744,13 +744,10 @@ struct Lex_for_loop_st
{ {
m_index= 0; m_index= 0;
m_target_bound= 0; m_target_bound= 0;
m_cursor_offset= 0;
m_direction= 0; m_direction= 0;
m_implicit_cursor= false; m_implicit_cursor= false;
} }
void init(const Lex_for_loop_st &other)
{
*this= other;
}
bool is_for_loop_cursor() const { return m_target_bound == NULL; } bool is_for_loop_cursor() const { return m_target_bound == NULL; }
bool is_for_loop_explicit_cursor() const bool is_for_loop_explicit_cursor() const
{ {
......
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