← Back to team overview

maria-developers team mailing list archive

Re: MDEV-10598 - bb-10.2-compatibility

 

Hello Jerome,


On 03/14/2017 06:07 PM, jerome brauge wrote:
> Hi Alexander,
> Can you review this patch ?
> I could add more tests when your others points will be corrected.

I have a few suggestions.
Let's discuss different topics in separate letters.


Here're my suggestions for sql_yacc_ora.yy

I found the the grammar was quite hard to read:
- Too similar names sp_decl_body_list vs sp_decls_body_list looked confusing
- It's not easy to follow when sp_push_cursors_declaration_instr() was
actually called.


I tried to add new HANDLER related rules:

opt_sp_decl_handler_list
sp_decl_handler_list

and renamed sp_decl_body to sp_decl_non_handler
(to make the name more self-descriptive).


So now variable/cursor/exception/condition declarations
are not allowed after handler declarations syntactically
and just cause a syntax error.


Advantages:
- I believe it's easier to read the rules this way.
- There is no a need for a new method sp_declarations_join_ora()
- It's now obvious that sp_push_cursors_declaration_instr() is called
  between non-handler and optional handler declarations.



Disadvantages:
- The error message
"Variable or condition declaration after cursor or handler declaration"
was probably more readable than
"You have an error in your SQL syntax"

but I think this is not a big problem, because:

- Oracle users should normally use EXCEPTION rather than HANDLER.
- Later we can introduce delayed HANDLER declarations in the same way
  with what we're doing for CURSORs now. So the error will just be gone.


What do you think about this proposal?

I'm sending two diffs:

- cursor-00-incremental.diff is a patch on top of your patch
- cursor-00-full.diff is the full patch
  (your patch combined with cursor-00-incremental.diff)

Also, I found that in your patch a semicolon was missing at the end of
some rules. Please don't forgot semicolons, we require semicolons
in our coding style.


Thanks.


> 
> Thanks.
> Jérôme.
> 
> 
>> -----Message d'origine-----
>> De : Alexander Barkov [mailto:bar@xxxxxxxxxxx]
>> Envoyé : mardi 14 mars 2017 11:54
>> À : jerome brauge
>> Cc : MariaDB Developers (maria-developers@xxxxxxxxxxxxxxxxxxx)
>> Objet : Re: MDEV-10598 - bb-10.2-compatibility
>>
>> Hi Jerome,
>>
>> On 03/14/2017 02:29 PM, jerome brauge wrote:
>>> Thanks Alexander,
>>>
>>> I have two other problems to finalize my patch:
>>> - Attached script cur_err_warning.sql produce a warning 1931 (Query
>> execution was interrupted. The query examined at least 2 rows, which
>> exceeds LIMIT ROWS EXAMINED (0). The query result may be incomplete)
>> and I don't see why.
>>
>> That's my fault. Please ignore this warning for now.
>> This warning happens when the procedure opens a cursor to resolve its
>> structure. I will suppress this warning later.
>>
>>
>>>
>>> - Attached script cur_err_field_name.sql produce an error 4057 (HY000) at
>> line 23: Row variable 'rec2' does not have a field 'a'
>>
>> This is also my bug.
>> The problem is that rec2 has a name "rec1.a" instead of just "a".
>>
>>
>>
>> Please change
>>
>> CURSOR cur2 IS SELECT rec1.a ;
>>
>> to
>>
>> CURSOR cur2 IS SELECT rec1.a AS a;
>>
>>
>>>
>>> These two scripts work fine on oracle.
>>>
>>> Jérôme.
>>>
>>>
>>>> -----Message d'origine-----
>>>> De : Alexander Barkov [mailto:bar@xxxxxxxxxxx] Envoyé : mardi 14 mars
>>>> 2017 11:11 À : jerome brauge Objet : Re: MDEV-10598 -
>>>> bb-10.2-compatibility
>>>>
>>>> Hello Jerome,
>>>>
>>>>
>>>>
>>>> On 03/13/2017 07:46 PM, jerome brauge wrote:
>>>>> Alexander,
>>>>> I think there is a little bug with ROWTYPE: when I affect a variable
>>>>> with a
>>>> field value, the server crash.
>>>>> I attached the script which cause the issue.
>>>>
>>>> Thanks for reporting this.
>>>>
>>>>
>>>> This quick patch fixes the problem:
>>>>
>>>>
>>>> diff --git a/sql/sp_head.cc b/sql/sp_head.cc index faf9f5f..745f982
>>>> 100644
>>>> --- a/sql/sp_head.cc
>>>> +++ b/sql/sp_head.cc
>>>> @@ -347,6 +347,12 @@ Item *
>>>>  sp_prepare_func_item(THD* thd, Item **it_addr, uint cols)  {
>>>>    DBUG_ENTER("sp_prepare_func_item");
>>>> +  if (!(*it_addr)->fixed &&
>>>> +      (*it_addr)->fix_fields(thd, it_addr))  {
>>>> +    DBUG_PRINT("info", ("fix_fields() failed"));
>>>> +    DBUG_RETURN(NULL);
>>>> +  }
>>>>    it_addr= (*it_addr)->this_item_addr(thd, it_addr);
>>>>
>>>>    if ((!(*it_addr)->fixed &&
>>>>
>>>>
>>>>
>>>> But I'm still thinking.
>>>> Perhaps I'll end some with some different patch.
>>>> I let you know when the final fix is pushed.
>>>>
>>>>
>>>>>
>>>>> This is the call stack :
>>>>>
>>>>>> 	mysqld.exe!my_sigabrt_handler(int sig) Line 477	C
>>>>>  	mysqld.exe!raise(int signum) Line 516	C++
>>>>>  	mysqld.exe!abort() Line 64	C++
>>>>>  	mysqld.exe!common_assert_to_stderr_direct(const wchar_t * const
>>>> expression, const wchar_t * const file_name, const unsigned int
>>>> line_number) Line 124	C++
>>>>>  	mysqld.exe!common_assert_to_stderr<wchar_t>(const wchar_t *
>>>> const expression, const wchar_t * const file_name, const unsigned int
>>>> line_number) Line 138	C++
>>>>>  	mysqld.exe!common_assert<wchar_t>(const wchar_t * const
>>>> expression, const wchar_t * const file_name, const unsigned int
>>>> line_number, void * const return_address) Line 383	C++
>>>>>  	mysqld.exe!_wassert(const wchar_t * expression, const wchar_t *
>>>> file_name, unsigned int line_number) Line 404	C++
>>>>>  	mysqld.exe!Item_splocal_row_field_by_name::this_item_addr(THD
>>>> * thd, Item * * it) Line 1814	C++
>>>>>  	mysqld.exe!sp_prepare_func_item(THD * thd, Item * * it_addr,
>>>> unsigned int cols) Line 350	C++
>>>>>  	mysqld.exe!sp_eval_expr(THD * thd, Item * result_item, Field *
>>>> result_field, Item * * expr_item_ptr) Line 391	C++
>>>>>  	mysqld.exe!sp_rcontext::set_variable(THD * thd, unsigned int idx,
>>>> Item * * value) Line 566	C++
>>>>>  	mysqld.exe!sp_instr_set::exec_core(THD * thd, unsigned int *
>>>> nextp) Line 3378	C++
>>>>>  	mysqld.exe!sp_lex_keeper::reset_lex_and_exec_core(THD * thd,
>>>> unsigned int * nextp, bool open_tables, sp_instr * instr) Line 3097	C++
>>>>>  	mysqld.exe!sp_instr_set::execute(THD * thd, unsigned int * nextp)
>>>> Line 3371	C++
>>>>>  	mysqld.exe!sp_head::execute(THD * thd, bool
>>>> merge_da_on_success) Line 1261	C++
>>>>>  	mysqld.exe!sp_head::execute_procedure(THD * thd, List<Item> *
>>>> args) Line 2086	C++
>>>>>  	mysqld.exe!do_execute_sp(THD * thd, sp_head * sp) Line 2890
>>>> 	C++
>>>>>  	mysqld.exe!mysql_execute_command(THD * thd) Line 5919	C++
>>>>>  	mysqld.exe!mysql_parse(THD * thd, char * rawbuf, unsigned int
>>>> length, Parser_state * parser_state, bool is_com_multi, bool
>>>> is_next_command) Line 8006	C++
>>>>>  	mysqld.exe!dispatch_command(enum_server_command command,
>>>> THD * thd, char * packet, unsigned int packet_length, bool is_com_multi,
>>>> bool is_next_command) Line 1821	C++
>>>>>  	mysqld.exe!do_command(THD * thd) Line 1369	C++
>>>>>  	mysqld.exe!threadpool_process_request(THD * thd) Line 346
>>>> 	C++
>>>>>  	mysqld.exe!tp_callback(TP_connection * c) Line 192	C++
>>>>>  	mysqld.exe!tp_callback(_TP_CALLBACK_INSTANCE * instance, void *
>>>> context) Line 377	C++
>>>>>  	mysqld.exe!work_callback(_TP_CALLBACK_INSTANCE * instance,
>>>> void * context, _TP_WORK * work) Line 451	C++
>>>>>
>>>>> Can you reproduce this ?
>>>>>
>>>>> Jérôme.
>>>>>
>>>>>> -----Message d'origine-----
>>>>>> De : Alexander Barkov [mailto:bar@xxxxxxxxxxx] Envoyé : lundi 13
>>>>>> mars
>>>>>> 2017 14:47 À : jerome brauge Objet : Re: MDEV-10598 -
>>>>>> bb-10.2-compatibility
>>>>>>
>>>>>> Jérôme,
>>>>>>
>>>>>> On 03/13/2017 05:43 PM, jerome brauge wrote:
>>>>>>> Hello Alexander,
>>>>>>> I have to do some changes in the patch and add some tests cases
>>>>>>> (with row type) I think it will be ready this afternoon (CET).
>>>>>>
>>>>>> Excellent. You rock!
>>>>>>
>>>>>>>
>>>>>>> Jérôme.
>>>>>>>
>>>>>>>
>>>>>>>> -----Message d'origine-----
>>>>>>>> De : Alexander Barkov [mailto:bar@xxxxxxxxxxx] Envoyé : lundi 13
>>>>>>>> mars
>>>>>>>> 2017 14:38 À : jerome brauge Cc : maria-developers Objet : Re:
>>>>>>>> MDEV-10598 - bb-10.2-compatibility
>>>>>>>>
>>>>>>>> Hello Jerome,
>>>>>>>>
>>>>>>>> will you try to apply your patch on top of the current bb-10.2-
>>>>>> compatibility?
>>>>>>>>
>>>>>>>> Or should I do that?
>>>>>>>>
>>>>>>>> Thanks!
>>>>>>>>
>>>>>>>>
>>>>>>>> On 03/10/2017 02:36 PM, Alexander Barkov wrote:
>>>>>>>>> Hello Jerome,
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 02/27/2017 11:39 PM, jerome brauge wrote:
>>>>>>>>>> Hello Alexander,
>>>>>>>>>> Thanks for the explanation.
>>>>>>>>>> It's something we do not use. I did not think about it.
>>>>>>>>>> I look forward to your patch.
>>>>>>>>>
>>>>>>>>> I pushed these tasks:
>>>>>>>>>
>>>>>>>>> MDEV-10581 sql_mode=ORACLE: Explicit cursor FOR LOOP
>>>>>>>>> MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR loop
>>>>>>>>> MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable
>>>>>> declarations
>>>>>>>>> MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable
>>>>>> declarations
>>>>>>>>>
>>>>>>>>> Please clone the branch again.
>>>>>>>>> Git pull will not work, because I recently rebased
>>>>>>>>> bb-10.2-compatibility on top of the latest 10.2.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> When implementing cursor%ROWTYPE, I had your patch in mind
>> and
>>>>>> made
>>>>>>>>> some refactoring to help us apply MDEV-10598 easier.
>>>>>>>>> Please see a comment to MDEV-12011 in "git log".
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Now the tricky thing (when adding your patch) is to make sure
>>>>>>>>> that this work fine:
>>>>>>>>>
>>>>>>>>> CREATE PROCEDURE p1
>>>>>>>>> AS
>>>>>>>>>   a INT:=10;
>>>>>>>>>   CURSOR cur1 IS SELECT a;
>>>>>>>>>   rec1 cur1%ROWTYPE;
>>>>>>>>>   CURSOR cur2 IS SELECT rec1.a;
>>>>>>>>>   rec2 cur2%ROWTYPE;
>>>>>>>>> BEGIN
>>>>>>>>>   OPEN cur2;
>>>>>>>>>   FETCH cur2 INTO rec2;
>>>>>>>>>   CLOSE cur2;
>>>>>>>>>   SELECT rec2.a;
>>>>>>>>> END;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I.e. a set of intermixed CURSOR and cursor%ROWTYPE variable
>>>>>>>>> declarations referencing each other recursively.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks.
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Regards,
>>>>>>>>>> Jérôme.
>>>>>>>>>>
>>>>>>>>>>> -----Message d'origine-----
>>>>>>>>>>> De : Alexander Barkov [mailto:bar@xxxxxxxxxxx] Envoyé : lundi
>>>>>>>>>>> 27 février 2017 11:28 À : jerome brauge Cc : maria-developers
>> Objet :
>>>>>>>>>>> Re: MDEV-10598 - bb-10.2-compatibility
>>>>>>>>>>>
>>>>>>>>>>> Hello Jerome,
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 02/21/2017 07:18 PM, jerome brauge wrote:
>>>>>>>>>>>> Hello Alexander,
>>>>>>>>>>>> I've done this patch for MDEV-10598.
>>>>>>>>>>>> Can you review it ?
>>>>>>>>>>>
>>>>>>>>>>> It seems we'll have to postpone this patch.
>>>>>>>>>>>
>>>>>>>>>>> I'm currently working on:
>>>>>>>>>>>
>>>>>>>>>>> MDEV-10598 Variable declarations can go after cursor
>>>>>>>>>>> declarations
>>>>>>>>>>> MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable
>>>>>>>> declarations
>>>>>>>>>>> So the trick with postponing variable declarations using a
>>>>>>>>>>> temporary list might not work properly after adding MDEV-10598
>>>>>>>>>>> abd MDEV-12011, the order of cursors and variables is important.
>>>>>>>>>>>
>>>>>>>>>>> Example:
>>>>>>>>>>>
>>>>>>>>>>> DECLARE
>>>>>>>>>>>   CURSOR cur1 IS SELECT a,b FROM t1;
>>>>>>>>>>>   v cur1%ROWTYPE;
>>>>>>>>>>>   CURSOR cur2 IS SELECT v.a, v.b FROM DUAL; BEGIN
>>>>>>>>>>>   ...
>>>>>>>>>>> END;
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> So the order of cur1, v and cur2 is important.
>>>>>>>>>>>
>>>>>>>>>>> I'll let you known when I'm ready with %ROWTYPE tasks.
>>>>>>>>>>>
>>>>>>>>>>> Thanks!
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Regards,
>>>>>>>>>>>> Jérôme.
>>>>>>>>>>>>
diff --git a/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result b/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result
new file mode 100644
index 0000000..1e185f7
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result
@@ -0,0 +1,265 @@
+SET sql_mode=ORACLE;
+#
+# MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
+#
+#
+# Variable after cursor declaration
+#
+CREATE OR REPLACE TABLE t1 (a INT);
+insert into t1 values (1);
+insert into t1 values (2);
+CREATE or replace PROCEDURE p1
+AS
+CURSOR c IS SELECT a FROM t1;
+var1 varchar(10);
+BEGIN
+OPEN c;
+fetch c into var1;
+SELECT c%ROWCOUNT,var1;
+close c;
+END;
+$$
+CALL p1;
+c%ROWCOUNT	var1
+1	1
+DROP PROCEDURE p1;
+drop table t1;
+#
+# Variable after condition declaration
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+dup_key CONDITION FOR SQLSTATE '23000';
+var1 varchar(40);
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+BEGIN
+var1:='';
+insert into t1 values (1);
+select var1;
+END;
+$$
+CALL p1;
+var1
+duplicate key in index
+DROP PROCEDURE p1;
+drop table t1;
+#
+# Condition after cursor declaration
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+var1 varchar(40);
+var2 integer;
+CURSOR c IS SELECT col1 FROM t1;
+dup_key CONDITION FOR SQLSTATE '23000';
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELECT var1;
+END;
+$$
+CALL p1;
+var1
+duplicate key in index
+DROP PROCEDURE p1;
+drop table t1;
+#
+# Cursor after handler declaration
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+var1 varchar(40);
+var2 integer;
+dup_key CONDITION FOR SQLSTATE '23000';
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+CURSOR c IS SELECT col1 FROM t1;
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELECT var1;
+END;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'CURSOR c IS SELECT col1 FROM t1;
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELE' at line 6
+drop table t1;
+#
+# Condition after handler declaration
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+var1 varchar(40);
+var2 integer;
+dup_key CONDITION FOR SQLSTATE '23000';
+CURSOR c IS SELECT col1 FROM t1;
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELECT var1;
+END;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 va' at line 7
+drop table t1;
+#
+# Variable after handler declaration
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+var1 varchar(40);
+var2 integer;
+dup_key CONDITION FOR SQLSTATE '23000';
+CURSOR c IS SELECT col1 FROM t1;
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELECT var1;
+END;
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 va' at line 7
+drop table t1;
+#
+# Variable after cursor (inner block)
+#
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+insert into t1 values (2);
+create unique index t1_col1 on t1 (col1);
+CREATE or replace PROCEDURE p1
+AS
+CURSOR c IS SELECT col1 FROM t1;
+var1 varchar(40);
+BEGIN
+OPEN c;
+begin
+declare
+CURSOR c IS SELECT col1 FROM t1 where col1=2;
+var2 integer;
+dup_key CONDITION FOR SQLSTATE '23000';
+CONTINUE HANDLER FOR dup_key
+BEGIN
+var1:='duplicate key in index';
+END;
+begin
+OPEN c;
+fetch c into var1;
+SELECT 'inner cursor',var1;
+insert into t1 values (2);
+close c;
+end;
+end;
+SELECT var1;
+fetch c into var1;
+SELECT c%ROWCOUNT,var1;
+begin
+insert into t1 values (2);
+exception when 1062 then
+begin
+SELECT 'dup key caugth';
+end;
+end;
+close c;
+END;
+$$
+CALL p1;
+inner cursor	var1
+inner cursor	2
+var1
+duplicate key in index
+c%ROWCOUNT	var1
+1	1
+dup key caugth
+dup key caugth
+DROP PROCEDURE p1;
+drop table t1;
+#
+# Cursor declaration and row type declaration in same block
+#
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+insert into t1 values(1,'a');
+CREATE or replace PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT a FROM t1;
+rec1 cur1%ROWTYPE;
+BEGIN
+rec1.a:= 10;
+END;
+$$
+SHOW PROCEDURE CODE p1;
+Pos	Instruction
+0	cursor_copy_struct cur1 rec1@0
+1	set rec1@0 NULL
+2	cpush cur1@0
+3	set rec1.a@0["a"] 10
+4	cpop 1
+currentlty generate error ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+call p1;
+DROP PROCEDURE p1;
+drop table t1;
+CREATE or replace PROCEDURE p1
+AS
+a INT:=10;
+CURSOR cur1 IS SELECT a;
+rec1 cur1%ROWTYPE;
+CURSOR cur2 IS SELECT rec1.a + 1  "a";
+rec2 cur2%ROWTYPE;
+BEGIN
+OPEN cur1;
+FETCH cur1 INTO rec1;
+CLOSE cur1;
+SELECT rec1.a;
+open cur2;
+FETCH cur2 INTO rec2;
+CLOSE cur2;
+SELECT rec2.a;
+END;
+$$
+currentlty generate error ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+CALL p1();
+rec1.a
+10
+rec2.a
+11
+DROP PROCEDURE p1;
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
new file mode 100644
index 0000000..0afbd66
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
@@ -0,0 +1,269 @@
+SET sql_mode=ORACLE;
+
+--echo #
+--echo # MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
+--echo #
+
+--echo #
+--echo # Variable after cursor declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (a INT);
+insert into t1 values (1);
+insert into t1 values (2);
+DELIMITER $$;
+CREATE or replace PROCEDURE p1
+AS
+  CURSOR c IS SELECT a FROM t1;
+  var1 varchar(10);
+BEGIN
+  OPEN c;
+  fetch c into var1;
+  SELECT c%ROWCOUNT,var1;
+  close c;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Variable after condition declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE or replace PROCEDURE p1
+AS
+  dup_key CONDITION FOR SQLSTATE '23000';
+  var1 varchar(40);
+  CONTINUE HANDLER FOR dup_key
+  BEGIN
+    var1:='duplicate key in index';
+  END;
+BEGIN
+  var1:='';
+  insert into t1 values (1);
+  select var1;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Condition after cursor declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE or replace PROCEDURE p1
+AS
+  var1 varchar(40);
+  var2 integer;
+  CURSOR c IS SELECT col1 FROM t1;
+  dup_key CONDITION FOR SQLSTATE '23000';
+  CONTINUE HANDLER FOR dup_key
+  BEGIN
+    var1:='duplicate key in index';
+  END;
+BEGIN
+  var1:='';
+  insert into t1 values (1);
+  SELECT var1;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Cursor after handler declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE or replace PROCEDURE p1
+AS
+  var1 varchar(40);
+  var2 integer;
+  dup_key CONDITION FOR SQLSTATE '23000';
+  CONTINUE HANDLER FOR dup_key
+  BEGIN
+    var1:='duplicate key in index';
+  END;
+  CURSOR c IS SELECT col1 FROM t1;
+BEGIN
+  var1:='';
+  insert into t1 values (1);
+  SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Condition after handler declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE or replace PROCEDURE p1
+AS
+  var1 varchar(40);
+  var2 integer;
+  dup_key CONDITION FOR SQLSTATE '23000';
+  CURSOR c IS SELECT col1 FROM t1;
+  CONTINUE HANDLER FOR dup_key
+  BEGIN
+    var1:='duplicate key in index';
+  END;
+  divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+  var1:='';
+  insert into t1 values (1);
+  SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Variable after handler declaration
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+--error ER_PARSE_ERROR
+CREATE or replace PROCEDURE p1
+AS
+  var1 varchar(40);
+  var2 integer;
+  dup_key CONDITION FOR SQLSTATE '23000';
+  CURSOR c IS SELECT col1 FROM t1;
+  CONTINUE HANDLER FOR dup_key
+  BEGIN
+    var1:='duplicate key in index';
+  END;
+  divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+  var1:='';
+  insert into t1 values (1);
+  SELECT var1;
+END;
+$$
+DELIMITER ;$$
+drop table t1;
+
+--echo #
+--echo # Variable after cursor (inner block)
+--echo #
+
+CREATE OR REPLACE TABLE t1 (col1 INT);
+insert into t1 values (1);
+insert into t1 values (2);
+create unique index t1_col1 on t1 (col1);
+DELIMITER $$;
+CREATE or replace PROCEDURE p1
+AS
+  CURSOR c IS SELECT col1 FROM t1;
+  var1 varchar(40);
+BEGIN
+  OPEN c;
+  begin
+    declare
+      CURSOR c IS SELECT col1 FROM t1 where col1=2;
+      var2 integer;
+      dup_key CONDITION FOR SQLSTATE '23000';
+      CONTINUE HANDLER FOR dup_key
+      BEGIN
+        var1:='duplicate key in index';
+      END;
+    begin
+      OPEN c;
+      fetch c into var1;
+      SELECT 'inner cursor',var1;
+      insert into t1 values (2);
+      close c;
+    end;
+  end;
+  SELECT var1;
+  fetch c into var1;
+  SELECT c%ROWCOUNT,var1;
+  begin
+    insert into t1 values (2);
+  exception when 1062 then
+    begin
+      SELECT 'dup key caugth';
+    end;
+  end;
+  close c;
+END;
+$$
+DELIMITER ;$$
+CALL p1;
+DROP PROCEDURE p1;
+drop table t1;
+
+--echo #
+--echo # Cursor declaration and row type declaration in same block
+--echo #
+CREATE TABLE t1 (a INT, b VARCHAR(10));
+insert into t1 values(1,'a');
+delimiter $$;
+CREATE or replace PROCEDURE p1()
+AS
+CURSOR cur1 IS SELECT a FROM t1;
+rec1 cur1%ROWTYPE;
+BEGIN
+rec1.a:= 10;
+END;
+$$
+delimiter ;$$
+SHOW PROCEDURE CODE p1;
+--echo currentlty generate error ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+--disable_warnings
+call p1;
+--enable_warnings
+DROP PROCEDURE p1;
+drop table t1;
+delimiter $$;
+CREATE or replace PROCEDURE p1
+AS
+  a INT:=10;
+  CURSOR cur1 IS SELECT a;
+  rec1 cur1%ROWTYPE;
+  CURSOR cur2 IS SELECT rec1.a + 1  "a";
+  rec2 cur2%ROWTYPE;
+BEGIN
+  OPEN cur1;
+  FETCH cur1 INTO rec1;
+  CLOSE cur1;
+  SELECT rec1.a;
+  open cur2;
+  FETCH cur2 INTO rec2;
+  CLOSE cur2;
+  SELECT rec2.a;
+END;
+$$
+DELIMITER ;$$
+--echo currentlty generate error ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+--disable_warnings
+CALL p1();
+--enable_warnings
+DROP PROCEDURE p1;
\ No newline at end of file
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 745f982..453cf4c 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -595,6 +595,7 @@ sp_head::sp_head()
   m_backpatch.empty();
   m_backpatch_goto.empty();
   m_cont_backpatch.empty();
+  m_delayed_cpush.empty();
   m_lex.empty();
   my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
   my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key,
@@ -2459,6 +2460,49 @@ sp_head::do_cont_backpatch()
   }
 }
 
+
+bool
+sp_head::store_cursor_infos(THD *thd, sp_pcontext *spcont,
+                            const LEX_STRING name,
+                            sp_lex_cursor *cursor_stmt,
+                            sp_pcontext *param_ctx, uint ccount)
+{
+    ddc_t *ddc=(ddc_t *) thd->alloc(sizeof(ddc_t));
+    if (!ddc)
+      return true;
+
+    ddc->spcont= spcont;
+    ddc->param_ctx= param_ctx;
+    ddc->cursor_stmt= cursor_stmt;
+    ddc->current_cursor_count= ccount;
+    return m_delayed_cpush.push_back(ddc);
+}
+
+
+int
+sp_head::sp_push_cursors_declaration_instr(THD *thd)
+{
+  if (m_delayed_cpush.is_empty())
+    return 0;
+
+  List_iterator<ddc_t> li(m_delayed_cpush);
+  ddc_t *c;
+  sp_instr_cpush *i;
+  while ((c= li++))
+  {
+    i= new (thd->mem_root)
+         sp_instr_cpush(instructions(), c->spcont, c->cursor_stmt,
+                        c->current_cursor_count);
+    if (i == NULL || add_instr(i))
+    {
+      return 1;
+    }
+    li.remove();
+  }
+  return 0;
+}
+
+
 void
 sp_head::set_info(longlong created, longlong modified,
                   st_sp_chistics *chistics, sql_mode_t sql_mode)
diff --git a/sql/sp_head.h b/sql/sp_head.h
index ee11974..3337ce5 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -560,6 +560,13 @@ class sp_head :private Query_arena
   void
   do_cont_backpatch();
 
+  bool
+  store_cursor_infos(THD *thd, sp_pcontext *spcont, const LEX_STRING name,
+                     sp_lex_cursor *cursor_stmt, sp_pcontext *param_ctx,
+                     uint ccount);
+  int
+  sp_push_cursors_declaration_instr(THD *thd);
+
   char *name(uint *lenp = 0) const
   {
     if (lenp)
@@ -755,6 +762,19 @@ class sp_head :private Query_arena
   */
   HASH m_sptabs;
 
+  /**
+    We need a special list to add cursor declaration (cpush) after variables
+    and conditions declaration and before handler declaration
+  */
+  typedef struct
+  {
+    sp_pcontext *spcont;
+    sp_pcontext *param_ctx;
+    sp_lex_cursor *cursor_stmt;
+    uint current_cursor_count;
+  } ddc_t;
+  List<ddc_t> m_delayed_cpush;
+
   bool
   execute(THD *thd, bool merge_da_on_success);
 
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 27d0d4d..b01dd4a 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5687,23 +5687,30 @@ bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop)
 
 bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name,
                             sp_lex_cursor *cursor_stmt,
-                            sp_pcontext *param_ctx)
+                            sp_pcontext *param_ctx, bool delay_instr)
 {
   uint offp;
   sp_instr_cpush *i;
-
   if (spcont->find_cursor(name, &offp, true))
   {
     my_error(ER_SP_DUP_CURS, MYF(0), name.str);
     return true;
   }
   cursor_stmt->set_cursor_name(name);
-  i= new (thd->mem_root)
-       sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt,
-                      spcont->current_cursor_count());
-  return i == NULL ||
-         sphead->add_instr(i) ||
-         spcont->add_cursor(name, param_ctx, cursor_stmt);
+  uint ccount=spcont->current_cursor_count();
+
+  if (spcont->add_cursor(name, param_ctx, cursor_stmt))
+    return true;
+
+  if (delay_instr)
+    return sphead->store_cursor_infos(thd, spcont, name, cursor_stmt,
+                                      param_ctx, ccount);
+  else
+  {
+    i= new (thd->mem_root)
+         sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt, ccount);
+    return i == NULL || sphead->add_instr(i);
+  }
 }
 
 
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index f18a0a4..44befd1 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3154,7 +3154,14 @@ struct LEX: public Query_tables_list
 
   bool sp_declare_cursor(THD *thd, const LEX_STRING name,
                          class sp_lex_cursor *cursor_stmt,
-                         sp_pcontext *param_ctx);
+                         sp_pcontext *param_ctx, bool delay_instr);
+  bool sp_declare_cursor(THD *thd, const LEX_STRING name,
+                         class sp_lex_cursor *cursor_stmt,
+                         sp_pcontext *param_ctx)
+  {
+    return sp_declare_cursor(thd, name, cursor_stmt, param_ctx, false);
+  }
+
   bool sp_open_cursor(THD *thd, const LEX_STRING name,
                       List<sp_assignment_lex> *parameters);
   Item_splocal *create_item_for_sp_var(LEX_STRING name, sp_variable *spvar,
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index f6fc323..32b7fcc 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -1334,7 +1334,9 @@ END_OF_INPUT
 
 %type <num>  sp_decl_idents sp_handler_type sp_hcond_list
 %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
-%type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list
+%type <spblock> sp_decl_body_list opt_sp_decl_body_list
+%type <spblock> sp_decl_non_handler sp_decl_non_handler_list
+%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list
 %type <spblock_handlers> sp_block_statements_and_exceptions
 %type <sp_instr_addr> sp_instr_addr
 %type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
@@ -2572,12 +2574,37 @@ opt_sp_decl_body_list:
         ;
 
 sp_decl_body_list:
-          sp_decl_body ';' { $$= $1; }
-        | sp_decl_body_list sp_decl_body ';'
+          sp_decl_non_handler_list
           {
-            if (Lex->sp_declarations_join(&$$, $1, $2))
+            if (Lex->sphead->sp_push_cursors_declaration_instr(thd))
               MYSQL_YYABORT;
           }
+          opt_sp_decl_handler_list
+          {
+            $$.join($1, $3);
+          }
+        | sp_decl_handler_list
+        ;
+
+sp_decl_non_handler_list:
+          sp_decl_non_handler ';' { $$= $1; }
+        | sp_decl_non_handler_list sp_decl_non_handler ';'
+          {
+            $$.join($1, $2);
+          }
+        ;
+
+sp_decl_handler_list:
+          sp_decl_handler ';' { $$= $1; }
+        | sp_decl_handler_list sp_decl_handler ';'
+          {
+            $$.join($1, $2);
+          }
+        ;
+
+opt_sp_decl_handler_list:
+          /* Empty*/ { $$.init(); }
+        | sp_decl_handler_list
         ;
 
 qualified_column_ident:
@@ -2655,7 +2682,7 @@ type_or_rowtype:
         | ROWTYPE_SYM  { $$= 1; }
         ;
 
-sp_decl_body:
+sp_decl_non_handler:
           sp_decl_idents
           {
             Lex->sp_variable_declarations_init(thd, $1);
@@ -2709,18 +2736,6 @@ sp_decl_body:
             $$.vars= $$.hndlrs= $$.curs= 0;
             $$.conds= 1;
           }
-        | sp_handler_type HANDLER_SYM FOR_SYM
-          {
-            if (Lex->sp_handler_declaration_init(thd, $1))
-              MYSQL_YYABORT;
-          }
-          sp_hcond_list sp_proc_stmt
-          {
-            if (Lex->sp_handler_declaration_finalize(thd, $1))
-              MYSQL_YYABORT;
-            $$.vars= $$.conds= $$.curs= 0;
-            $$.hndlrs= 1;
-          }
         | CURSOR_SYM ident_directly_assignable
           {
             Lex->sp_block_init(thd);
@@ -2731,13 +2746,28 @@ sp_decl_body:
             sp_pcontext *param_ctx= Lex->spcont;
             if (Lex->sp_block_finalize(thd))
               MYSQL_YYABORT;
-            if (Lex->sp_declare_cursor(thd, $2, $6, param_ctx))
+            if (Lex->sp_declare_cursor(thd, $2, $6, param_ctx, true))
               MYSQL_YYABORT;
             $$.vars= $$.conds= $$.hndlrs= 0;
             $$.curs= 1;
           }
         ;
 
+sp_decl_handler:
+          sp_handler_type HANDLER_SYM FOR_SYM
+          {
+            if (Lex->sp_handler_declaration_init(thd, $1))
+              MYSQL_YYABORT;
+          }
+          sp_hcond_list sp_proc_stmt
+          {
+            if (Lex->sp_handler_declaration_finalize(thd, $1))
+              MYSQL_YYABORT;
+            $$.vars= $$.conds= $$.curs= 0;
+            $$.hndlrs= 1;
+          }
+        ;
+
 opt_parenthesized_cursor_formal_parameters:
           /* Empty */
         | '(' sp_fdparams ')'
@@ -3983,7 +4013,7 @@ sp_for_loop_bounds:
           {
             DBUG_ASSERT(Lex->sphead);
             LEX_STRING name= {C_STRING_WITH_LEN("[implicit_cursor]") };
-            if (Lex->sp_declare_cursor(thd, name, $4, NULL))
+            if (Lex->sp_declare_cursor(thd, name, $4, NULL, false))
               MYSQL_YYABORT;
             $$.m_direction= 1;
             if (!($$.m_index= new (thd->mem_root) sp_assignment_lex(thd, thd->lex)))
diff --git a/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result b/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result
index 85a4bd7..1e185f7 100644
--- a/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result
+++ b/mysql-test/suite/compat/oracle/r/sp-cursor-decl.result
@@ -98,7 +98,11 @@ insert into t1 values (1);
 SELECT var1;
 END;
 $$
-ERROR 42000: Cursor declaration after handler declaration
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'CURSOR c IS SELECT col1 FROM t1;
+BEGIN
+var1:='';
+insert into t1 values (1);
+SELE' at line 6
 drop table t1;
 #
 # Condition after handler declaration
@@ -123,7 +127,10 @@ insert into t1 values (1);
 SELECT var1;
 END;
 $$
-ERROR 42000: Variable or condition declaration after cursor or handler declaration
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 va' at line 7
 drop table t1;
 #
 # Variable after handler declaration
@@ -148,7 +155,10 @@ insert into t1 values (1);
 SELECT var1;
 END;
 $$
-ERROR 42000: Variable or condition declaration after cursor or handler declaration
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'divide_by_zero CONDITION FOR SQLSTATE '22012';
+BEGIN
+var1:='';
+insert into t1 va' at line 7
 drop table t1;
 #
 # Variable after cursor (inner block)
diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
index 2afe096..0afbd66 100644
--- a/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
+++ b/mysql-test/suite/compat/oracle/t/sp-cursor-decl.test
@@ -92,7 +92,7 @@ CREATE OR REPLACE TABLE t1 (col1 INT);
 insert into t1 values (1);
 create unique index t1_col1 on t1 (col1);
 DELIMITER $$;
---error ER_SP_CURSOR_AFTER_HANDLER
+--error ER_PARSE_ERROR
 CREATE or replace PROCEDURE p1
 AS
   var1 varchar(40);
@@ -120,7 +120,7 @@ CREATE OR REPLACE TABLE t1 (col1 INT);
 insert into t1 values (1);
 create unique index t1_col1 on t1 (col1);
 DELIMITER $$;
---error ER_SP_VARCOND_AFTER_CURSHNDLR
+--error ER_PARSE_ERROR
 CREATE or replace PROCEDURE p1
 AS
   var1 varchar(40);
@@ -149,7 +149,7 @@ CREATE OR REPLACE TABLE t1 (col1 INT);
 insert into t1 values (1);
 create unique index t1_col1 on t1 (col1);
 DELIMITER $$;
---error ER_SP_VARCOND_AFTER_CURSHNDLR
+--error ER_PARSE_ERROR
 CREATE or replace PROCEDURE p1
 AS
   var1 varchar(40);
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7715ba5..44befd1 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3318,23 +3318,6 @@ struct LEX: public Query_tables_list
     res->join(b1, b2);
     return false;
   }
-  bool sp_declarations_join_ora(Lex_spblock_st *res,
-                                const Lex_spblock_st b1,
-                                const Lex_spblock_st b2) const
-  {
-    if ((b2.vars || b2.conds) && (b1.hndlrs))
-    {
-      my_error(ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0));
-      return true;
-    }
-    if (b2.curs && b1.hndlrs)
-    {
-      my_error(ER_SP_CURSOR_AFTER_HANDLER, MYF(0));
-      return true;
-    }
-    res->join(b1, b2);
-    return false;
-  }
   bool sp_block_with_exceptions_finalize_declarations(THD *thd);
   bool sp_block_with_exceptions_finalize_executable_section(THD *thd,
                                                   uint executable_section_ip);
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index ddcdfdb..32b7fcc 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -1334,8 +1334,9 @@ END_OF_INPUT
 
 %type <num>  sp_decl_idents sp_handler_type sp_hcond_list
 %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
-%type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list
-%type <spblock> sp_decls_body_list sp_decl_handler
+%type <spblock> sp_decl_body_list opt_sp_decl_body_list
+%type <spblock> sp_decl_non_handler sp_decl_non_handler_list
+%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list
 %type <spblock_handlers> sp_block_statements_and_exceptions
 %type <sp_instr_addr> sp_instr_addr
 %type <sp_cursor_name_and_offset> sp_cursor_name_and_offset
@@ -2573,34 +2574,39 @@ opt_sp_decl_body_list:
         ;
 
 sp_decl_body_list:
-          sp_decls_body_list
+          sp_decl_non_handler_list
           {
             if (Lex->sphead->sp_push_cursors_declaration_instr(thd))
               MYSQL_YYABORT;
-            $$=$1;
           }
-
-sp_decls_body_list:
-          sp_decl_body ';' { $$= $1; }
-        | sp_decl_handler ';' { $$= $1; }
-        | sp_decls_body_list sp_decl_body ';'
+          opt_sp_decl_handler_list
           {
-            if (Lex->sp_declarations_join_ora(&$$, $1, $2))
-              MYSQL_YYABORT;
+            $$.join($1, $3);
           }
-        | sp_decls_body_list
+        | sp_decl_handler_list
+        ;
+
+sp_decl_non_handler_list:
+          sp_decl_non_handler ';' { $$= $1; }
+        | sp_decl_non_handler_list sp_decl_non_handler ';'
           {
-            // if any, push all cursors declarations before first handler
-            if (Lex->sphead->sp_push_cursors_declaration_instr(thd))
-              MYSQL_YYABORT;
+            $$.join($1, $2);
           }
-          sp_decl_handler ';'
+        ;
+
+sp_decl_handler_list:
+          sp_decl_handler ';' { $$= $1; }
+        | sp_decl_handler_list sp_decl_handler ';'
           {
-            if (Lex->sp_declarations_join_ora(&$$, $1, $3))
-              MYSQL_YYABORT;
+            $$.join($1, $2);
           }
         ;
 
+opt_sp_decl_handler_list:
+          /* Empty*/ { $$.init(); }
+        | sp_decl_handler_list
+        ;
+
 qualified_column_ident:
           sp_decl_ident '.' ident
           {
@@ -2676,7 +2682,7 @@ type_or_rowtype:
         | ROWTYPE_SYM  { $$= 1; }
         ;
 
-sp_decl_body:
+sp_decl_non_handler:
           sp_decl_idents
           {
             Lex->sp_variable_declarations_init(thd, $1);
@@ -2760,6 +2766,7 @@ sp_decl_handler:
             $$.vars= $$.conds= $$.curs= 0;
             $$.hndlrs= 1;
           }
+        ;
 
 opt_parenthesized_cursor_formal_parameters:
           /* Empty */

References