You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

949 lines
20 KiB

Bug#19194 (Right recursion in parser for CASE causes excessive stack usage, limitation) Note to the reviewer ==================== Warning: reviewing this patch is somewhat involved. Due to the nature of several issues all affecting the same area, fixing separately each issue is not practical, since each fix can not be implemented and tested independently. In particular, the issues with - rule recursion - nested case statements - forward jump resolution (backpatch list) are tightly coupled (see below). Definitions =========== The expression CASE expr WHEN expr THEN expr WHEN expr THEN expr ... END is a "Simple Case Expression". The expression CASE WHEN expr THEN expr WHEN expr THEN expr ... END is a "Searched Case Expression". The statement CASE expr WHEN expr THEN stmts WHEN expr THEN stmts ... END CASE is a "Simple Case Statement". The statement CASE WHEN expr THEN stmts WHEN expr THEN stmts ... END CASE is a "Searched Case Statement". A "Left Recursive" rule is like list: element | list element ; A "Right Recursive" rule is like list: element | element list ; Left and right recursion produces the same language, the difference only affects the *order* in which the text is parsed. In a descendant parser (usually written manually), right recursion works very well, and is typically implemented with a while loop. In an ascendant parser (yacc/bison) left recursion works very well, and is implemented naturally by the parser stack. In both cases, using the wrong type or recursion is very bad and should be avoided, as it causes technical issues with the parser implementation. Before this change ================== The "Simple Case Expression" and "Searched Case Expression" were both implemented by the "when_list" and "when_list2" rules, which are left recursive (ok). These rules, however, used lex->when_list instead of using the parser stack, which is more complex that necessary, and potentially dangerous because of other rules using THD::reset_lex. The "Simple Case Statement" and "Searched Case Statements" were implemented by the "sp_case", "sp_whens" and in part by "sp_proc_stmt" rules. Both cases were right recursive (bad). The grammar involved was convoluted, and is assumed to be the results of tweaks to get the code generation to work, but is not what someone would naturally write. In addition, using a common rule for both "Simple" and "Searched" case statements was implemented with sp_head::m_flags |= IN_SIMPLE_CASE, which is a flag and not a stack, and therefore does not take into account *nested* case statements. This leads to incorrect generated code, and either a server crash or an incorrect result. With regards to the backpatch mechanism, a *different* backpatch list was created for each jump from "WHEN expr THEN stmt" to "END CASE", which relied on the grammar to be right recursive. This is a mis-use of the backpatch list, since this list can resolve multiple references to the same target at once. The optimizer algorithm used to detect dead code in the "assembly" SQL instructions, implemented by sp_head::opt_mark(uint ip), was recursive in some cases (a conditional jump pointing forward to another conditional jump). In case of specially crafted code, like - a long list of "IF expr THEN stmt END IF" - a long CASE statement this would actually cause a server crash with a stack overflow. In general, having a stack that grows proportionally with user data (the SQL code given by the client in a CREATE PROCEDURE) is to be avoided. In debug builds only, creating a SP / SF / Trigger which had a significant amount of code would spend --literally-- several minutes in sp_head::create, because of the debug code involved with DBUG_PRINT("info", ("Code %s ... There are several issues with this code: - in a CASE with 5 000 WHEN, there are 15 000 instructions generated, which create a sting representation of the code which is 500 000 bytes long, - using a String instead of an io stream causes performances to degrade to a total server freeze, as time is spent doing realloc of a buffer always too short, - Printing a 500 000 long string in the debug log is too verbose, - Generating this string even when DBUG_PRINT is off is useless, - Having code that potentially can affect the server behavior, used with #ifdef / #endif is useful in some cases, but is also a bad practice. After this change ================= "Case Expressions" (both simple and searched) have been simplified to not use LEX::when_list, which has been removed. Considering all the issues affecting case statements, the grammar for these has been totally re written. The existing actions, used to generate "assembly" sp_inst* code, have been preserved but moved in the new grammar, with the following changes: a) Bison rules are no longer shared between "Simple" and "Searched" case statements, because a stack instead of a flag is required to handle them. Nested statements are handled naturally by the parser stack, which by definition uses the correct rule in the correct context. Nested statements of the opposite type (simple vs searched) works correctly. The flag sp_head::IN_SIMPLE_CASE is no longer used. This is a step towards resolution of WL#2999, which correctly identified that temporary parsing flags do not belong to sp_head. The code in the action is shared by mean of the case_stmt_action_xxx() helpers. b) The backpatch mechanism, used to resolve forward jumps in the generated code, has been changed to: - create a label for the instruction following 'END CASE', - register each jump at the end of a "WHEN expr THEN stmt" in a *unique* backpatch list associated with the 'END CASE' label - resolve all the forward jumps for this label at once. In addition, the code involving backpatch has been commented, so that a reader can now understand by reading matching "Registering" and "Resolving" comments how the forward jumps are resolved and what target they resolve to, as this is far from evident when reading the code alone. The implementation of sp_head::opt_mark() has been revised to avoid recursive calls from jump instructions, and instead add the jump location to the list of paths to explore during the flow analysis of the instruction graph, with a call to sp_head::add_mark_lead(). In addition, the flow analysis will stop if an instruction has already been marked as reachable, which the previous code failed to do in the recursive case. sp_head::opt_mark() is now private, to prevent new calls to this method from being introduced. The debug code present in sp_head::create() has been removed. Considering that SHOW PROCEDURE CODE is also available in debug builds, and can be used anytime regardless of the trace level, as opposed to "CREATE PROCEDURE" time and only if the trace was on, removing the code actually makes debugging easier (usable trace). Tests have been written to cover the parser overflow (big CASE), and to cover nested CASE statements. mysql-test/r/sp-code.result: Test cases for nested CASE statements. mysql-test/t/sp-code.test: Test cases for nested CASE statements. sql/sp_head.cc: Re factored opt_mark() to avoid recursion, clean up. sql/sp_head.h: Re factored opt_mark() to avoid recursion, clean up. sql/sql_lex.cc: Removed when_list. sql/sql_lex.h: Removed when_list. sql/sql_yacc.yy: Minor clean up for case expressions, Major re write for case statements (Bug#19194). mysql-test/r/sp_stress_case.result: New test for massive CASE statements. mysql-test/t/sp_stress_case.sh: New test for massive CASE statements. mysql-test/t/sp_stress_case.test: New test for massive CASE statements.
19 years ago
Auto-merge from mysql-trunk-bugfixing. ****** This patch fixes the following bugs: - Bug#5889: Exit handler for a warning doesn't hide the warning in trigger - Bug#9857: Stored procedures: handler for sqlwarning ignored - Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP - Bug#36185: Incorrect precedence for warning and exception handlers The problem was in the way warnings/errors during stored routine execution were handled. Prior to this patch the logic was as follows: - when a warning/an error happens: if we're executing a stored routine, and there is a handler for that warning/error, remember the handler, ignore the warning/error and continue execution. - after a stored routine instruction is executed: check for a remembered handler and activate one (if any). This logic caused several problems: - if one instruction generates several warnings (errors) it's impossible to choose the right handler -- a handler for the first generated condition was chosen and remembered for activation. - mess with handling conditions in scopes different from the current one. - not putting generated warnings/errors into Warning Info (Diagnostic Area) is against The Standard. The patch changes the logic as follows: - Diagnostic Area is cleared on the beginning of each statement that either is able to generate warnings, or is able to work with tables. - at the end of a stored routine instruction, Diagnostic Area is left intact. - Diagnostic Area is checked after each stored routine instruction. If an instruction generates several condition, it's now possible to take a look at all of them and determine an appropriate handler. mysql-test/r/signal.result: Update result file: 1. handled conditions are not cleared any more; 2. reflect changes in signal.test mysql-test/r/signal_demo3.result: Update result file: handled conditions are not cleared any more. Due to playing with max_error_count, resulting warning lists have changed. mysql-test/r/sp-big.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-bugs.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-code.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-code.test. mysql-test/r/sp-error.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-error.test. mysql-test/r/sp.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp_trans.result: Update result file: handled conditions are not cleared any more. mysql-test/r/strict.result: Update result file: handled conditions are not cleared any more. mysql-test/r/view.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/innodb_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/memory_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/myisam_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/storedproc.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp005.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp006_InnoDB.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_trig003.result: Update result file: handled conditions are not cleared any more. mysql-test/t/signal.test: Make a test case more readable in the result file. mysql-test/t/sp-code.test: Add a test case for Bug#23032 checking that No Data takes precedence on Warning. mysql-test/t/sp-error.test: Adding test cases for: - Bug#23032 - Bug#36185 - Bug#5889 - Bug#9857 mysql-test/t/sp.test: Fixing test case to reflect behavioral changes made by the patch. sql/sp_head.cc: Reset the per-statement warning count before executing a stored procedure instruction. Move to a separate function code which checks the completion status of the executed statement and searches for a handler. Remove redundant code now that search for a handler is done after execution, errors are always pushed. sql/sp_pcontext.h: Remove unused code. sql/sp_rcontext.cc: - Polish sp_rcontext::find_handler(): use sp_rcontext::m_hfound instead of an extra local variable; - Remove sp_rcontext::handle_condition(); - Introduce sp_rcontext::activate_handler(), which prepares previously found handler for execution. - Move sp_rcontext::enter_handler() code into activate_handler(), because enter_handler() is used only from there; - Cleanups; - Introduce DBUG_EXECUTE_IF() for a test case in sp-code.test sql/sp_rcontext.h: - Remove unused code - Cleanups sql/sql_class.cc: Merge THD::raise_condition_no_handler() into THD::raise_condition(). After the patch raise_condition_no_handler() was called in raise_condition() only. sql/sql_class.h: Remove raise_condition_no_handler(). sql/sql_error.cc: Remove Warning_info::reserve_space() -- handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them. sql/sql_error.h: Remove Warning_info::reserve_space(). sql/sql_signal.cc: Handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them.
15 years ago
Auto-merge from mysql-trunk-bugfixing. ****** This patch fixes the following bugs: - Bug#5889: Exit handler for a warning doesn't hide the warning in trigger - Bug#9857: Stored procedures: handler for sqlwarning ignored - Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP - Bug#36185: Incorrect precedence for warning and exception handlers The problem was in the way warnings/errors during stored routine execution were handled. Prior to this patch the logic was as follows: - when a warning/an error happens: if we're executing a stored routine, and there is a handler for that warning/error, remember the handler, ignore the warning/error and continue execution. - after a stored routine instruction is executed: check for a remembered handler and activate one (if any). This logic caused several problems: - if one instruction generates several warnings (errors) it's impossible to choose the right handler -- a handler for the first generated condition was chosen and remembered for activation. - mess with handling conditions in scopes different from the current one. - not putting generated warnings/errors into Warning Info (Diagnostic Area) is against The Standard. The patch changes the logic as follows: - Diagnostic Area is cleared on the beginning of each statement that either is able to generate warnings, or is able to work with tables. - at the end of a stored routine instruction, Diagnostic Area is left intact. - Diagnostic Area is checked after each stored routine instruction. If an instruction generates several condition, it's now possible to take a look at all of them and determine an appropriate handler. mysql-test/r/signal.result: Update result file: 1. handled conditions are not cleared any more; 2. reflect changes in signal.test mysql-test/r/signal_demo3.result: Update result file: handled conditions are not cleared any more. Due to playing with max_error_count, resulting warning lists have changed. mysql-test/r/sp-big.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-bugs.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-code.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-code.test. mysql-test/r/sp-error.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-error.test. mysql-test/r/sp.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp_trans.result: Update result file: handled conditions are not cleared any more. mysql-test/r/strict.result: Update result file: handled conditions are not cleared any more. mysql-test/r/view.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/innodb_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/memory_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/myisam_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/storedproc.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp005.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp006_InnoDB.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_trig003.result: Update result file: handled conditions are not cleared any more. mysql-test/t/signal.test: Make a test case more readable in the result file. mysql-test/t/sp-code.test: Add a test case for Bug#23032 checking that No Data takes precedence on Warning. mysql-test/t/sp-error.test: Adding test cases for: - Bug#23032 - Bug#36185 - Bug#5889 - Bug#9857 mysql-test/t/sp.test: Fixing test case to reflect behavioral changes made by the patch. sql/sp_head.cc: Reset the per-statement warning count before executing a stored procedure instruction. Move to a separate function code which checks the completion status of the executed statement and searches for a handler. Remove redundant code now that search for a handler is done after execution, errors are always pushed. sql/sp_pcontext.h: Remove unused code. sql/sp_rcontext.cc: - Polish sp_rcontext::find_handler(): use sp_rcontext::m_hfound instead of an extra local variable; - Remove sp_rcontext::handle_condition(); - Introduce sp_rcontext::activate_handler(), which prepares previously found handler for execution. - Move sp_rcontext::enter_handler() code into activate_handler(), because enter_handler() is used only from there; - Cleanups; - Introduce DBUG_EXECUTE_IF() for a test case in sp-code.test sql/sp_rcontext.h: - Remove unused code - Cleanups sql/sql_class.cc: Merge THD::raise_condition_no_handler() into THD::raise_condition(). After the patch raise_condition_no_handler() was called in raise_condition() only. sql/sql_class.h: Remove raise_condition_no_handler(). sql/sql_error.cc: Remove Warning_info::reserve_space() -- handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them. sql/sql_error.h: Remove Warning_info::reserve_space(). sql/sql_signal.cc: Handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them.
15 years ago
Auto-merge from mysql-trunk-bugfixing. ****** This patch fixes the following bugs: - Bug#5889: Exit handler for a warning doesn't hide the warning in trigger - Bug#9857: Stored procedures: handler for sqlwarning ignored - Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP - Bug#36185: Incorrect precedence for warning and exception handlers The problem was in the way warnings/errors during stored routine execution were handled. Prior to this patch the logic was as follows: - when a warning/an error happens: if we're executing a stored routine, and there is a handler for that warning/error, remember the handler, ignore the warning/error and continue execution. - after a stored routine instruction is executed: check for a remembered handler and activate one (if any). This logic caused several problems: - if one instruction generates several warnings (errors) it's impossible to choose the right handler -- a handler for the first generated condition was chosen and remembered for activation. - mess with handling conditions in scopes different from the current one. - not putting generated warnings/errors into Warning Info (Diagnostic Area) is against The Standard. The patch changes the logic as follows: - Diagnostic Area is cleared on the beginning of each statement that either is able to generate warnings, or is able to work with tables. - at the end of a stored routine instruction, Diagnostic Area is left intact. - Diagnostic Area is checked after each stored routine instruction. If an instruction generates several condition, it's now possible to take a look at all of them and determine an appropriate handler. mysql-test/r/signal.result: Update result file: 1. handled conditions are not cleared any more; 2. reflect changes in signal.test mysql-test/r/signal_demo3.result: Update result file: handled conditions are not cleared any more. Due to playing with max_error_count, resulting warning lists have changed. mysql-test/r/sp-big.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-bugs.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-code.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-code.test. mysql-test/r/sp-error.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-error.test. mysql-test/r/sp.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp_trans.result: Update result file: handled conditions are not cleared any more. mysql-test/r/strict.result: Update result file: handled conditions are not cleared any more. mysql-test/r/view.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/innodb_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/memory_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/myisam_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/storedproc.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp005.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp006_InnoDB.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_trig003.result: Update result file: handled conditions are not cleared any more. mysql-test/t/signal.test: Make a test case more readable in the result file. mysql-test/t/sp-code.test: Add a test case for Bug#23032 checking that No Data takes precedence on Warning. mysql-test/t/sp-error.test: Adding test cases for: - Bug#23032 - Bug#36185 - Bug#5889 - Bug#9857 mysql-test/t/sp.test: Fixing test case to reflect behavioral changes made by the patch. sql/sp_head.cc: Reset the per-statement warning count before executing a stored procedure instruction. Move to a separate function code which checks the completion status of the executed statement and searches for a handler. Remove redundant code now that search for a handler is done after execution, errors are always pushed. sql/sp_pcontext.h: Remove unused code. sql/sp_rcontext.cc: - Polish sp_rcontext::find_handler(): use sp_rcontext::m_hfound instead of an extra local variable; - Remove sp_rcontext::handle_condition(); - Introduce sp_rcontext::activate_handler(), which prepares previously found handler for execution. - Move sp_rcontext::enter_handler() code into activate_handler(), because enter_handler() is used only from there; - Cleanups; - Introduce DBUG_EXECUTE_IF() for a test case in sp-code.test sql/sp_rcontext.h: - Remove unused code - Cleanups sql/sql_class.cc: Merge THD::raise_condition_no_handler() into THD::raise_condition(). After the patch raise_condition_no_handler() was called in raise_condition() only. sql/sql_class.h: Remove raise_condition_no_handler(). sql/sql_error.cc: Remove Warning_info::reserve_space() -- handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them. sql/sql_error.h: Remove Warning_info::reserve_space(). sql/sql_signal.cc: Handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them.
15 years ago
  1. #
  2. # Test the debugging feature "show procedure/function code <name>"
  3. #
  4. -- source include/have_debug.inc
  5. --disable_warnings
  6. drop procedure if exists empty;
  7. drop procedure if exists code_sample;
  8. --enable_warnings
  9. create procedure empty()
  10. begin
  11. end;
  12. show procedure code empty;
  13. drop procedure empty;
  14. create function almost_empty()
  15. returns int
  16. return 0;
  17. show function code almost_empty;
  18. drop function almost_empty;
  19. delimiter //;
  20. create procedure code_sample(x int, out err int, out nulls int)
  21. begin
  22. declare count int default 0;
  23. set nulls = 0;
  24. begin
  25. declare c cursor for select name from t1;
  26. declare exit handler for not found close c;
  27. open c;
  28. loop
  29. begin
  30. declare n varchar(20);
  31. declare continue handler for sqlexception set err=1;
  32. fetch c into n;
  33. if isnull(n) then
  34. set nulls = nulls + 1;
  35. else
  36. set count = count + 1;
  37. update t2 set idx = count where name=n;
  38. end if;
  39. end;
  40. end loop;
  41. end;
  42. select t.name, t.idx from t2 t order by idx asc;
  43. end//
  44. delimiter ;//
  45. show procedure code code_sample;
  46. drop procedure code_sample;
  47. #
  48. # BUG#15737: Stored procedure optimizer bug with LEAVE
  49. #
  50. # This is a much more extensive test case than is strictly needed,
  51. # but it was kept as is for two reasons:
  52. # - The bug occurs under some quite special circumstances, so it
  53. # wasn't trivial to create a smaller test,
  54. # - There's some value in having another more complex code sample
  55. # in this test file. This might catch future code generation bugs
  56. # that doesn't show in behaviour in any obvious way.
  57. --disable_warnings
  58. drop procedure if exists sudoku_solve;
  59. --enable_warnings
  60. delimiter //;
  61. create procedure sudoku_solve(p_naive boolean, p_all boolean)
  62. deterministic
  63. modifies sql data
  64. begin
  65. drop temporary table if exists sudoku_work, sudoku_schedule;
  66. create temporary table sudoku_work
  67. (
  68. row smallint not null,
  69. col smallint not null,
  70. dig smallint not null,
  71. cnt smallint,
  72. key using btree (cnt),
  73. key using btree (row),
  74. key using btree (col),
  75. unique key using hash (row,col)
  76. );
  77. create temporary table sudoku_schedule
  78. (
  79. idx int not null auto_increment primary key,
  80. row smallint not null,
  81. col smallint not null
  82. );
  83. call sudoku_init();
  84. if p_naive then
  85. update sudoku_work set cnt = 0 where dig = 0;
  86. else
  87. call sudoku_count();
  88. end if;
  89. insert into sudoku_schedule (row,col)
  90. select row,col from sudoku_work where cnt is not null order by cnt desc;
  91. begin
  92. declare v_scounter bigint default 0;
  93. declare v_i smallint default 1;
  94. declare v_dig smallint;
  95. declare v_schedmax smallint;
  96. select count(*) into v_schedmax from sudoku_schedule;
  97. more:
  98. loop
  99. begin
  100. declare v_tcounter bigint default 0;
  101. sched:
  102. while v_i <= v_schedmax do
  103. begin
  104. declare v_row, v_col smallint;
  105. select row,col into v_row,v_col from sudoku_schedule where v_i = idx;
  106. select dig into v_dig from sudoku_work
  107. where v_row = row and v_col = col;
  108. case v_dig
  109. when 0 then
  110. set v_dig = 1;
  111. update sudoku_work set dig = 1
  112. where v_row = row and v_col = col;
  113. when 9 then
  114. if v_i > 0 then
  115. update sudoku_work set dig = 0
  116. where v_row = row and v_col = col;
  117. set v_i = v_i - 1;
  118. iterate sched;
  119. else
  120. select v_scounter as 'Solutions';
  121. leave more;
  122. end if;
  123. else
  124. set v_dig = v_dig + 1;
  125. update sudoku_work set dig = v_dig
  126. where v_row = row and v_col = col;
  127. end case;
  128. set v_tcounter = v_tcounter + 1;
  129. if not sudoku_digit_ok(v_row, v_col, v_dig) then
  130. iterate sched;
  131. end if;
  132. set v_i = v_i + 1;
  133. end;
  134. end while sched;
  135. select dig from sudoku_work;
  136. select v_tcounter as 'Tests';
  137. set v_scounter = v_scounter + 1;
  138. if p_all and v_i > 0 then
  139. set v_i = v_i - 1;
  140. else
  141. leave more;
  142. end if;
  143. end;
  144. end loop more;
  145. end;
  146. drop temporary table sudoku_work, sudoku_schedule;
  147. end//
  148. delimiter ;//
  149. # The interestings parts are where the code for the two "leave" are:
  150. # ...
  151. #| 26 | jump_if_not 30 (v_i@3 > 0) |
  152. # ...
  153. #| 30 | stmt 0 "select v_scounter as 'Solutions'" |
  154. #| 31 | jump 45 |
  155. # ...
  156. #| 42 | jump_if_not 45 (p_all@1 and (v_i@3 > 0)) |
  157. #| 43 | set v_i@3 (v_i@3 - 1) |
  158. #| 44 | jump 14 |
  159. #| 45 | stmt 9 "drop temporary table sudoku_work, sud..." |
  160. #+-----+-----------------------------------------------------------------------+
  161. # The bug appeared at position 42 (with the wrong destination).
  162. show procedure code sudoku_solve;
  163. drop procedure sudoku_solve;
  164. #
  165. # Bug#19194 (Right recursion in parser for CASE causes excessive stack
  166. # usage, limitation)
  167. # This bug also exposed a flaw in the generated code with nested case
  168. # statements
  169. #
  170. --disable_warnings
  171. DROP PROCEDURE IF EXISTS proc_19194_simple;
  172. DROP PROCEDURE IF EXISTS proc_19194_searched;
  173. DROP PROCEDURE IF EXISTS proc_19194_nested_1;
  174. DROP PROCEDURE IF EXISTS proc_19194_nested_2;
  175. DROP PROCEDURE IF EXISTS proc_19194_nested_3;
  176. DROP PROCEDURE IF EXISTS proc_19194_nested_4;
  177. --enable_warnings
  178. delimiter |;
  179. CREATE PROCEDURE proc_19194_simple(i int)
  180. BEGIN
  181. DECLARE str CHAR(10);
  182. CASE i
  183. WHEN 1 THEN SET str="1";
  184. WHEN 2 THEN SET str="2";
  185. WHEN 3 THEN SET str="3";
  186. ELSE SET str="unknown";
  187. END CASE;
  188. SELECT str;
  189. END|
  190. CREATE PROCEDURE proc_19194_searched(i int)
  191. BEGIN
  192. DECLARE str CHAR(10);
  193. CASE
  194. WHEN i=1 THEN SET str="1";
  195. WHEN i=2 THEN SET str="2";
  196. WHEN i=3 THEN SET str="3";
  197. ELSE SET str="unknown";
  198. END CASE;
  199. SELECT str;
  200. END|
  201. # Outer SIMPLE case, inner SEARCHED case
  202. CREATE PROCEDURE proc_19194_nested_1(i int, j int)
  203. BEGIN
  204. DECLARE str_i CHAR(10);
  205. DECLARE str_j CHAR(10);
  206. CASE i
  207. WHEN 10 THEN SET str_i="10";
  208. WHEN 20 THEN
  209. BEGIN
  210. set str_i="20";
  211. CASE
  212. WHEN j=1 THEN SET str_j="1";
  213. WHEN j=2 THEN SET str_j="2";
  214. WHEN j=3 THEN SET str_j="3";
  215. ELSE SET str_j="unknown";
  216. END CASE;
  217. select "i was 20";
  218. END;
  219. WHEN 30 THEN SET str_i="30";
  220. WHEN 40 THEN SET str_i="40";
  221. ELSE SET str_i="unknown";
  222. END CASE;
  223. SELECT str_i, str_j;
  224. END|
  225. # Outer SEARCHED case, inner SIMPLE case
  226. CREATE PROCEDURE proc_19194_nested_2(i int, j int)
  227. BEGIN
  228. DECLARE str_i CHAR(10);
  229. DECLARE str_j CHAR(10);
  230. CASE
  231. WHEN i=10 THEN SET str_i="10";
  232. WHEN i=20 THEN
  233. BEGIN
  234. set str_i="20";
  235. CASE j
  236. WHEN 1 THEN SET str_j="1";
  237. WHEN 2 THEN SET str_j="2";
  238. WHEN 3 THEN SET str_j="3";
  239. ELSE SET str_j="unknown";
  240. END CASE;
  241. select "i was 20";
  242. END;
  243. WHEN i=30 THEN SET str_i="30";
  244. WHEN i=40 THEN SET str_i="40";
  245. ELSE SET str_i="unknown";
  246. END CASE;
  247. SELECT str_i, str_j;
  248. END|
  249. # Outer SIMPLE case, inner SIMPLE case
  250. CREATE PROCEDURE proc_19194_nested_3(i int, j int)
  251. BEGIN
  252. DECLARE str_i CHAR(10);
  253. DECLARE str_j CHAR(10);
  254. CASE i
  255. WHEN 10 THEN SET str_i="10";
  256. WHEN 20 THEN
  257. BEGIN
  258. set str_i="20";
  259. CASE j
  260. WHEN 1 THEN SET str_j="1";
  261. WHEN 2 THEN SET str_j="2";
  262. WHEN 3 THEN SET str_j="3";
  263. ELSE SET str_j="unknown";
  264. END CASE;
  265. select "i was 20";
  266. END;
  267. WHEN 30 THEN SET str_i="30";
  268. WHEN 40 THEN SET str_i="40";
  269. ELSE SET str_i="unknown";
  270. END CASE;
  271. SELECT str_i, str_j;
  272. END|
  273. # Outer SEARCHED case, inner SEARCHED case
  274. CREATE PROCEDURE proc_19194_nested_4(i int, j int)
  275. BEGIN
  276. DECLARE str_i CHAR(10);
  277. DECLARE str_j CHAR(10);
  278. CASE
  279. WHEN i=10 THEN SET str_i="10";
  280. WHEN i=20 THEN
  281. BEGIN
  282. set str_i="20";
  283. CASE
  284. WHEN j=1 THEN SET str_j="1";
  285. WHEN j=2 THEN SET str_j="2";
  286. WHEN j=3 THEN SET str_j="3";
  287. ELSE SET str_j="unknown";
  288. END CASE;
  289. select "i was 20";
  290. END;
  291. WHEN i=30 THEN SET str_i="30";
  292. WHEN i=40 THEN SET str_i="40";
  293. ELSE SET str_i="unknown";
  294. END CASE;
  295. SELECT str_i, str_j;
  296. END|
  297. delimiter ;|
  298. SHOW PROCEDURE CODE proc_19194_simple;
  299. SHOW PROCEDURE CODE proc_19194_searched;
  300. SHOW PROCEDURE CODE proc_19194_nested_1;
  301. SHOW PROCEDURE CODE proc_19194_nested_2;
  302. SHOW PROCEDURE CODE proc_19194_nested_3;
  303. SHOW PROCEDURE CODE proc_19194_nested_4;
  304. CALL proc_19194_nested_1(10, 1);
  305. #
  306. # Before 19194, the generated code was:
  307. # 20 jump_if_not 23(27) 30
  308. # 21 set str_i@2 _latin1'30'
  309. # As opposed to the expected:
  310. # 20 jump_if_not 23(27) (case_expr@0 = 30)
  311. # 21 set str_i@2 _latin1'30'
  312. #
  313. # and as a result, this call returned "30",
  314. # because the expression 30 is always true,
  315. # masking the case 40, case 0 and the else.
  316. #
  317. CALL proc_19194_nested_1(25, 1);
  318. CALL proc_19194_nested_1(20, 1);
  319. CALL proc_19194_nested_1(20, 2);
  320. CALL proc_19194_nested_1(20, 3);
  321. CALL proc_19194_nested_1(20, 4);
  322. CALL proc_19194_nested_1(30, 1);
  323. CALL proc_19194_nested_1(40, 1);
  324. CALL proc_19194_nested_1(0, 0);
  325. CALL proc_19194_nested_2(10, 1);
  326. #
  327. # Before 19194, the generated code was:
  328. # 20 jump_if_not 23(27) (case_expr@0 = (i@0 = 30))
  329. # 21 set str_i@2 _latin1'30'
  330. # As opposed to the expected:
  331. # 20 jump_if_not 23(27) (i@0 = 30)
  332. # 21 set str_i@2 _latin1'30'
  333. # and as a result, this call crashed the server, because there is no
  334. # such variable as "case_expr@0".
  335. #
  336. CALL proc_19194_nested_2(25, 1);
  337. CALL proc_19194_nested_2(20, 1);
  338. CALL proc_19194_nested_2(20, 2);
  339. CALL proc_19194_nested_2(20, 3);
  340. CALL proc_19194_nested_2(20, 4);
  341. CALL proc_19194_nested_2(30, 1);
  342. CALL proc_19194_nested_2(40, 1);
  343. CALL proc_19194_nested_2(0, 0);
  344. CALL proc_19194_nested_3(10, 1);
  345. CALL proc_19194_nested_3(25, 1);
  346. CALL proc_19194_nested_3(20, 1);
  347. CALL proc_19194_nested_3(20, 2);
  348. CALL proc_19194_nested_3(20, 3);
  349. CALL proc_19194_nested_3(20, 4);
  350. CALL proc_19194_nested_3(30, 1);
  351. CALL proc_19194_nested_3(40, 1);
  352. CALL proc_19194_nested_3(0, 0);
  353. CALL proc_19194_nested_4(10, 1);
  354. CALL proc_19194_nested_4(25, 1);
  355. CALL proc_19194_nested_4(20, 1);
  356. CALL proc_19194_nested_4(20, 2);
  357. CALL proc_19194_nested_4(20, 3);
  358. CALL proc_19194_nested_4(20, 4);
  359. CALL proc_19194_nested_4(30, 1);
  360. CALL proc_19194_nested_4(40, 1);
  361. CALL proc_19194_nested_4(0, 0);
  362. DROP PROCEDURE proc_19194_simple;
  363. DROP PROCEDURE proc_19194_searched;
  364. DROP PROCEDURE proc_19194_nested_1;
  365. DROP PROCEDURE proc_19194_nested_2;
  366. DROP PROCEDURE proc_19194_nested_3;
  367. DROP PROCEDURE proc_19194_nested_4;
  368. #
  369. # Bug#19207: Final parenthesis omitted for CREATE INDEX in Stored
  370. # Procedure
  371. #
  372. # Wrong criteria was used to distinguish the case when there was no
  373. # lookahead performed in the parser. Bug affected only statements
  374. # ending in one-character token without any optional tail, like CREATE
  375. # INDEX and CALL.
  376. #
  377. --disable_warnings
  378. DROP PROCEDURE IF EXISTS p1;
  379. --enable_warnings
  380. CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1);
  381. SHOW PROCEDURE CODE p1;
  382. DROP PROCEDURE p1;
  383. #
  384. # Bug#26977 exception handlers never hreturn
  385. #
  386. --disable_warnings
  387. drop table if exists t1;
  388. drop procedure if exists proc_26977_broken;
  389. drop procedure if exists proc_26977_works;
  390. --enable_warnings
  391. create table t1(a int unique);
  392. delimiter //;
  393. create procedure proc_26977_broken(v int)
  394. begin
  395. declare i int default 5;
  396. declare continue handler for sqlexception
  397. begin
  398. select 'caught something';
  399. retry:
  400. while i > 0 do
  401. begin
  402. set i = i - 1;
  403. select 'looping', i;
  404. end;
  405. end while retry;
  406. end;
  407. select 'do something';
  408. insert into t1 values (v);
  409. select 'do something again';
  410. insert into t1 values (v);
  411. end//
  412. create procedure proc_26977_works(v int)
  413. begin
  414. declare i int default 5;
  415. declare continue handler for sqlexception
  416. begin
  417. select 'caught something';
  418. retry:
  419. while i > 0 do
  420. begin
  421. set i = i - 1;
  422. select 'looping', i;
  423. end;
  424. end while retry;
  425. select 'optimizer: keep hreturn';
  426. end;
  427. select 'do something';
  428. insert into t1 values (v);
  429. select 'do something again';
  430. insert into t1 values (v);
  431. end//
  432. delimiter ;//
  433. show procedure code proc_26977_broken;
  434. show procedure code proc_26977_works;
  435. ## This caust an error because of jump short cut
  436. ## optimization.
  437. call proc_26977_broken(1);
  438. ## This works
  439. call proc_26977_works(2);
  440. drop table t1;
  441. drop procedure proc_26977_broken;
  442. drop procedure proc_26977_works;
  443. #
  444. # Bug#33618 Crash in sp_rcontext
  445. #
  446. --disable_warnings
  447. drop procedure if exists proc_33618_h;
  448. drop procedure if exists proc_33618_c;
  449. --enable_warnings
  450. delimiter //;
  451. create procedure proc_33618_h(num int)
  452. begin
  453. declare count1 int default '0';
  454. declare vb varchar(30);
  455. declare last_row int;
  456. while(num>=1) do
  457. set num=num-1;
  458. begin
  459. declare cur1 cursor for select `a` from t_33618;
  460. declare continue handler for not found set last_row = 1;
  461. set last_row:=0;
  462. open cur1;
  463. rep1:
  464. repeat
  465. begin
  466. declare exit handler for 1062 begin end;
  467. fetch cur1 into vb;
  468. if (last_row = 1) then
  469. ## should generate a hpop instruction here
  470. leave rep1;
  471. end if;
  472. end;
  473. until last_row=1
  474. end repeat;
  475. close cur1;
  476. end;
  477. end while;
  478. end//
  479. create procedure proc_33618_c(num int)
  480. begin
  481. declare count1 int default '0';
  482. declare vb varchar(30);
  483. declare last_row int;
  484. while(num>=1) do
  485. set num=num-1;
  486. begin
  487. declare cur1 cursor for select `a` from t_33618;
  488. declare continue handler for not found set last_row = 1;
  489. set last_row:=0;
  490. open cur1;
  491. rep1:
  492. repeat
  493. begin
  494. declare cur2 cursor for select `b` from t_33618;
  495. fetch cur1 into vb;
  496. if (last_row = 1) then
  497. ## should generate a cpop instruction here
  498. leave rep1;
  499. end if;
  500. end;
  501. until last_row=1
  502. end repeat;
  503. close cur1;
  504. end;
  505. end while;
  506. end//
  507. delimiter ;//
  508. show procedure code proc_33618_h;
  509. show procedure code proc_33618_c;
  510. drop procedure proc_33618_h;
  511. drop procedure proc_33618_c;
  512. #
  513. # Bug#20906 (Multiple assignments in SET in stored routine produce incorrect
  514. # instructions)
  515. #
  516. --disable_warnings
  517. drop procedure if exists p_20906_a;
  518. drop procedure if exists p_20906_b;
  519. --enable_warnings
  520. create procedure p_20906_a() SET @a=@a+1, @b=@b+1;
  521. show procedure code p_20906_a;
  522. set @a=1;
  523. set @b=1;
  524. call p_20906_a();
  525. select @a, @b;
  526. create procedure p_20906_b() SET @a=@a+1, @b=@b+1, @c=@c+1;
  527. show procedure code p_20906_b;
  528. set @a=1;
  529. set @b=1;
  530. set @c=1;
  531. call p_20906_b();
  532. select @a, @b, @c;
  533. drop procedure p_20906_a;
  534. drop procedure p_20906_b;
  535. --echo End of 5.0 tests.
  536. #
  537. # Bug #26303: reserve() not called before qs_append() may lead to buffer
  538. # overflow
  539. #
  540. DELIMITER //;
  541. CREATE PROCEDURE p1()
  542. BEGIN
  543. DECLARE dummy int default 0;
  544. CASE 12
  545. WHEN 12
  546. THEN SET dummy = 0;
  547. END CASE;
  548. END//
  549. DELIMITER ;//
  550. SHOW PROCEDURE CODE p1;
  551. DROP PROCEDURE p1;
  552. --echo #
  553. --echo # Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP
  554. --echo #
  555. --echo
  556. --echo # - Case 4: check that "No Data trumps Warning".
  557. --echo
  558. CREATE TABLE t1(a INT);
  559. INSERT INTO t1 VALUES (1), (2), (3);
  560. delimiter |;
  561. CREATE PROCEDURE p1()
  562. BEGIN
  563. DECLARE c CURSOR FOR SELECT a FROM t1;
  564. OPEN c;
  565. BEGIN
  566. DECLARE v INT;
  567. DECLARE CONTINUE HANDLER FOR SQLWARNING
  568. BEGIN
  569. SELECT "Warning found!";
  570. SHOW WARNINGS;
  571. END;
  572. DECLARE EXIT HANDLER FOR NOT FOUND
  573. BEGIN
  574. SELECT "End of Result Set found!";
  575. SHOW WARNINGS;
  576. END;
  577. WHILE TRUE DO
  578. FETCH c INTO v;
  579. END WHILE;
  580. END;
  581. CLOSE c;
  582. SELECT a INTO @foo FROM t1 LIMIT 1; # Clear warning stack
  583. END|
  584. delimiter ;|
  585. SET @save_dbug = @@debug_dbug;
  586. SET SESSION debug_dbug="+d,bug23032_emit_warning";
  587. CALL p1();
  588. SET SESSION debug_dbug=@save_dbug;
  589. DROP PROCEDURE p1;
  590. DROP TABLE t1;
  591. --echo #
  592. --echo # Bug#11763507 - 56224: FUNCTION NAME IS CASE-SENSITIVE
  593. --echo #
  594. SET @@SQL_MODE = '';
  595. DELIMITER $;
  596. CREATE FUNCTION testf_bug11763507() RETURNS INT
  597. BEGIN
  598. RETURN 0;
  599. END
  600. $
  601. CREATE PROCEDURE testp_bug11763507()
  602. BEGIN
  603. SELECT "PROCEDURE testp_bug11763507";
  604. END
  605. $
  606. DELIMITER ;$
  607. # STORED FUNCTIONS
  608. SHOW FUNCTION CODE testf_bug11763507;
  609. SHOW FUNCTION CODE TESTF_bug11763507;
  610. # STORED PROCEDURE
  611. SHOW PROCEDURE CODE testp_bug11763507;
  612. SHOW PROCEDURE CODE TESTP_bug11763507;
  613. DROP PROCEDURE testp_bug11763507;
  614. DROP FUNCTION testf_bug11763507;
  615. --echo #END OF BUG#11763507 test.
  616. --echo #
  617. --echo # MDEV-13581 ROW TYPE OF t1 and t1%ROWTYPE for routine parameters
  618. --echo #
  619. CREATE TABLE t1 (a INT, b TEXT);
  620. DELIMITER $$;
  621. CREATE PROCEDURE p1(a ROW TYPE OF t1, OUT b ROW TYPE OF t1)
  622. BEGIN
  623. SET a.a = 100;
  624. SET a.b = 'aaa';
  625. SET b.a = 200;
  626. SET b.b = 'bbb';
  627. SET a = b;
  628. SET b = a;
  629. SET a.a = b.a;
  630. SET b.a = a.a;
  631. END;
  632. $$
  633. DELIMITER ;$$
  634. SHOW PROCEDURE CODE p1;
  635. DROP PROCEDURE p1;
  636. DROP TABLE t1;
  637. --echo #
  638. --echo # MDEV-14415 Add Oracle-style FOR loop to sql_mode=DEFAULT
  639. --echo #
  640. --echo # Integer range FOR loop
  641. DELIMITER $$;
  642. CREATE PROCEDURE p1()
  643. BEGIN
  644. FOR i IN 1..3
  645. DO
  646. SELECT i;
  647. END FOR;
  648. END;
  649. $$
  650. DELIMITER ;$$
  651. CALL p1;
  652. SHOW PROCEDURE CODE p1;
  653. DROP PROCEDURE p1;
  654. --echo # Nested integer range FOR loops
  655. DELIMITER $$;
  656. CREATE PROCEDURE p1()
  657. BEGIN
  658. fori:
  659. FOR i IN 1..3
  660. DO
  661. forj:
  662. FOR j IN 1..3
  663. DO
  664. IF i = 3 THEN
  665. LEAVE fori;
  666. END IF;
  667. IF j = 3 THEN
  668. LEAVE forj;
  669. END IF;
  670. SELECT i,j;
  671. END FOR;
  672. END FOR;
  673. END;
  674. $$
  675. DELIMITER ;$$
  676. CALL p1;
  677. SHOW PROCEDURE CODE p1;
  678. DROP PROCEDURE p1;
  679. --echo # Explicit cursor FOR loops
  680. DELIMITER $$;
  681. CREATE PROCEDURE p1()
  682. BEGIN
  683. DECLARE cur0 CURSOR FOR SELECT 10 AS a, 'b0' AS b;
  684. DECLARE cur1 CURSOR FOR SELECT 10 AS a, 'b0' AS b;
  685. DECLARE cur2 CURSOR FOR SELECT 10 AS a, 'b0' AS b;
  686. FOR rec1 IN cur1
  687. DO
  688. SELECT rec1.a, rec1.b;
  689. SET rec1.a= 11;
  690. SET rec1.b= 'b1';
  691. SELECT rec1.a, rec1.b;
  692. END FOR;
  693. FOR rec0 IN cur0
  694. DO
  695. SET rec0.a= 10;
  696. SET rec0.b='b0';
  697. END FOR;
  698. FOR rec2 IN cur2
  699. DO
  700. SET rec2.a= 10;
  701. SET rec2.b='b0';
  702. END FOR;
  703. END;
  704. $$
  705. DELIMITER ;$$
  706. SHOW PROCEDURE CODE p1;
  707. DROP PROCEDURE p1;
  708. --echo # Nested explicit cursor FOR loops
  709. DELIMITER $$;
  710. CREATE PROCEDURE p1()
  711. BEGIN
  712. DECLARE cur0 CURSOR FOR SELECT 10 AS a, 'b0' AS b;
  713. FOR rec0 IN cur0
  714. DO
  715. BEGIN
  716. DECLARE cur1 CURSOR FOR SELECT 11 AS a, 'b1' AS b;
  717. SET rec0.a= 11;
  718. SET rec0.b= 'b0';
  719. FOR rec1 IN cur1
  720. DO
  721. SET rec1.a= 11;
  722. SET rec1.b= 'b1';
  723. BEGIN
  724. DECLARE cur2 CURSOR FOR SELECT 12 AS a, 'b2' AS b;
  725. FOR rec2 IN cur2
  726. DO
  727. SET rec2.a=12;
  728. SET rec2.b='b2';
  729. END FOR;
  730. END;
  731. END FOR;
  732. END;
  733. END FOR;
  734. END;
  735. $$
  736. DELIMITER ;$$
  737. SHOW PROCEDURE CODE p1;
  738. DROP PROCEDURE p1;
  739. --echo # Implicit cursor FOR loops
  740. DELIMITER $$;
  741. CREATE PROCEDURE p1()
  742. BEGIN
  743. FOR rec1 IN (SELECT 11 AS a, 'b1' AS b)
  744. DO
  745. SELECT rec1.a, rec1.b;
  746. SET rec1.a= 11;
  747. SET rec1.b= 'b1';
  748. SELECT rec1.a, rec1.b;
  749. END FOR;
  750. FOR rec0 IN (SELECT 10 AS a, 'b0' AS b)
  751. DO
  752. SET rec0.a= 10;
  753. SET rec0.b='b0';
  754. END FOR;
  755. FOR rec2 IN (SELECT 12 AS a, 'b2' AS b)
  756. DO
  757. SET rec2.a= 10;
  758. SET rec2.b='b0';
  759. END FOR;
  760. END;
  761. $$
  762. DELIMITER ;$$
  763. SHOW PROCEDURE CODE p1;
  764. DROP PROCEDURE p1;
  765. --echo # Nested implicit cursor FOR loops
  766. DELIMITER $$;
  767. CREATE PROCEDURE p1()
  768. BEGIN
  769. FOR rec0 IN (SELECT 10 AS a, 'b0' AS b)
  770. DO
  771. SET rec0.a= 11;
  772. SET rec0.b= 'b0';
  773. FOR rec1 IN (SELECT 11 AS a, 'b1' AS b)
  774. DO
  775. SET rec1.a= 11;
  776. SET rec1.b= 'b1';
  777. FOR rec2 IN (SELECT 12 AS a, 'b2' AS b)
  778. DO
  779. SET rec2.a=12;
  780. SET rec2.b='b2';
  781. END FOR;
  782. END FOR;
  783. END FOR;
  784. END;
  785. $$
  786. DELIMITER ;$$
  787. SHOW PROCEDURE CODE p1;
  788. DROP PROCEDURE p1;
  789. --echo #
  790. --echo # MDEV-14623: Output of show function code does not show FETCH GROUP NEXT ROW
  791. --echo # for custom aggregates
  792. --echo #
  793. delimiter |;
  794. create aggregate function f1(x INT) returns int
  795. begin
  796. declare continue handler for not found return 0;
  797. loop
  798. fetch group next row;
  799. insert into t2 (sal) values (x);
  800. end loop;
  801. end|
  802. delimiter ;|
  803. show function code f1;
  804. drop function f1;