List:Internals« Previous MessageNext Message »
From:sanja Date:November 22 2005 11:11pm
Subject:bk commit into 5.0 tree (bell:1.1978) BUG#10100
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of bell. When bell does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1978 05/11/23 01:11:19 bell@stripped +22 -0
  Recursion support made for SP (BUG#10100).

  sql/sql_parse.cc
    1.510 05/11/23 01:11:09 bell@stripped +12 -6
    sp_find_function() and sp_find_procedure() joined to sp_find_routine()
      function as it was mentioned in TODO.

  sql/sql_class.h
    1.273 05/11/23 01:11:09 bell@stripped +1 -0
    max_sp_recursion_depth variable added.

  sql/sql_base.cc
    1.318 05/11/23 01:11:09 bell@stripped +5 -0
    open_table() consume a lot of stack space so we check free stack space before it.

  sql/sp_head.h
    1.74 05/11/23 01:11:09 bell@stripped +28 -0
    New sp_head variables added to support inst of instances of SP
      for recursion and pointer on ths first free to use instance.

  sql/sp_head.cc
    1.194 05/11/23 01:11:08 bell@stripped +70 -26
    Initialization of new sp_head fields to get correct list of instances
      contained one instance only.
    Stack requirement for SP instruction is increased.
    Stack free space is checked before mem root initialisation to avoid
      memory leak.
    Pointer to the free instance management added before and after
      SP execution.

  sql/sp.h
    1.30 05/11/23 01:11:08 bell@stripped +2 -4
    sp_find_function() and sp_find_procedure() joined to sp_find_routine()
      function as it was mentioned in TODO.

  sql/sp.cc
    1.97 05/11/23 01:11:08 bell@stripped +157 -135
    sp_find_function() and sp_find_procedure() joined to sp_find_routine()
      function as it was mentioned in TODO.
    Temory LEX is allocated on a stack, not on a heap.
    Recursion support added for stored procedures.

  sql/share/errmsg.txt
    1.55 05/11/23 01:11:07 bell@stripped +3 -1
    An error message changed.
    An error message added.

  sql/set_var.cc
    1.143 05/11/23 01:11:06 bell@stripped +5 -0
    max_sp_recursion_depth variable added.

  sql/mysqld.cc
    1.517 05/11/23 01:11:06 bell@stripped +6 -0
    max_sp_recursion_depth variable added.

  sql/item_func.cc
    1.266 05/11/23 01:11:06 bell@stripped +9 -2
    sp_find_function() and sp_find_procedure() joined to sp_find_routine()
      function as it was mentioned in TODO.

  mysql-test/t/variables.test
    1.51 05/11/23 01:11:06 bell@stripped +4 -0
    Test of max_sp_recursion_depth variable.

  mysql-test/t/trigger.test
    1.28 05/11/23 01:11:06 bell@stripped +4 -0
    Check that triggers are not affected by this patch.

  mysql-test/t/sp.test
    1.163 05/11/23 01:11:05 bell@stripped +154 -0
    Tests for recursion.

  mysql-test/t/sp-error.test
    1.93 05/11/23 01:11:05 bell@stripped +4 -3
    Error messages changed.
    Test of bug11394() made with allowed recursion.

  mysql-test/t/sp-dynamic.test
    1.2 05/11/23 01:11:05 bell@stripped +14 -3
    The test expanded for case of allowed/disalowed recursion.

  mysql-test/r/variables.result
    1.76 05/11/23 01:11:05 bell@stripped +8 -0
    Test of max_sp_recursion_depth variable.

  mysql-test/r/trigger.result
    1.21 05/11/23 01:11:05 bell@stripped +3 -0
    Check that triggers are not affected by this patch.

  mysql-test/r/sp.result
    1.170 05/11/23 01:11:05 bell@stripped +185 -0
    Tests for recursion.

  mysql-test/r/sp-error.result
    1.89 05/11/23 01:11:05 bell@stripped +6 -3
    Error messages changed.
    Test of bug11394() made with allowed recursion.

  mysql-test/r/sp-dynamic.result
    1.2 05/11/23 01:11:05 bell@stripped +12 -3
    The test expanded for case of allowed/disalowed recursion.

  client/mysqltest.c
    1.177 05/11/23 01:11:04 bell@stripped +23 -17
    An expected error messages hiding from the log if disable_result_log is in force.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	bell
# Host:	sanja.is.com.ua
# Root:	/home/bell/mysql/bk/work-bug7-5.0

--- 1.265/sql/item_func.cc	2005-11-03 09:05:27 +02:00
+++ 1.266/sql/item_func.cc	2005-11-23 01:11:06 +02:00
@@ -4690,10 +4690,16 @@
 {
   Field *field;
   DBUG_ENTER("Item_func_sp::sp_result_field");
+  DBUG_PRINT("info", ("sp: %s, flags: %x, level: %lu",
+                      (m_sp ? "YES" : "NO"),
+                      (m_sp ? m_sp->m_flags : (uint)0),
+                      (m_sp ? m_sp->m_recursion_level : (ulong)0)));
 
   if (!m_sp)
   {
-    if (!(m_sp= sp_find_function(current_thd, m_name, TRUE)))
+    THD *thd= current_thd;
+    if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
+                                &thd->sp_func_cache, TRUE)))
     {
       my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
       DBUG_RETURN(0);
@@ -4925,7 +4931,8 @@
   bool res= TRUE;
 
   *save= 0;                                     // Safety if error
-  if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE)))
+  if (! m_sp && ! (m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
+                                         &thd->sp_func_cache, TRUE)))
   {
     my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
     goto error;

--- 1.516/sql/mysqld.cc	2005-11-04 10:03:27 +02:00
+++ 1.517/sql/mysqld.cc	2005-11-23 01:11:06 +02:00
@@ -4543,6 +4543,7 @@
   OPT_OPTIMIZER_PRUNE_LEVEL,
   OPT_UPDATABLE_VIEWS_WITH_LIMIT,
   OPT_SP_AUTOMATIC_PRIVILEGES,
+  OPT_MAX_SP_RECURSION_DEPTH,
   OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
   OPT_ENABLE_LARGE_PAGES,
   OPT_TIMED_MUTEXES,
@@ -5745,6 +5746,11 @@
    (gptr*) &global_system_variables.read_buff_size,
    (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
    128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0},
+  {"max_sp_recursion_depth", OPT_MAX_SP_RECURSION_DEPTH,
+   "Maximum stored procedure recursion depth. (discussed with docs).",
+   (gptr*) &global_system_variables.max_sp_recursion_depth,
+   (gptr*) &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG,
+   OPT_ARG, 0, 0, 255, 0, 1, 0 },
 #ifdef HAVE_REPLICATION
   {"relay_log_purge", OPT_RELAY_LOG_PURGE,
    "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.",

--- 1.317/sql/sql_base.cc	2005-11-03 16:28:10 +02:00
+++ 1.318/sql/sql_base.cc	2005-11-23 01:11:09 +02:00
@@ -1087,6 +1087,11 @@
   /* find a unused table in the open table cache */
   if (refresh)
     *refresh=0;
+
+  /* an open table operation needs a lot of the stack space */
+  if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char *)&alias))
+    return 0;
+
   if (thd->killed)
     DBUG_RETURN(0);
   key_length= (uint) (strmov(strmov(key, table_list->db)+1,

--- 1.272/sql/sql_class.h	2005-11-04 15:12:19 +02:00
+++ 1.273/sql/sql_class.h	2005-11-23 01:11:09 +02:00
@@ -534,6 +534,7 @@
   ulong completion_type;
   /* Determines which non-standard SQL behaviour should be enabled */
   ulong sql_mode;
+  ulong max_sp_recursion_depth;
   /* check of key presence in updatable view */
   ulong updatable_views_with_limit;
   ulong default_week_format;

--- 1.509/sql/sql_parse.cc	2005-11-04 11:54:45 +02:00
+++ 1.510/sql/sql_parse.cc	2005-11-23 01:11:09 +02:00
@@ -3694,7 +3694,8 @@
     if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
       break;
 #ifdef HAVE_DLOPEN
-    if (sp_find_function(thd, lex->spname))
+    if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                        &thd->sp_func_cache, FALSE))
     {
       my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
       goto error;
@@ -4228,7 +4229,8 @@
         By this moment all needed SPs should be in cache so no need to look 
         into DB. 
       */
-      if (!(sp= sp_find_procedure(thd, lex->spname, TRUE)))
+      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                                &thd->sp_proc_cache, TRUE)))
       {
 	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
                  lex->spname->m_qname.str);
@@ -4364,9 +4366,11 @@
 
       memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
       if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
-	sp= sp_find_procedure(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                            &thd->sp_proc_cache, FALSE);
       else
-	sp= sp_find_function(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                            &thd->sp_func_cache, FALSE);
       mysql_reset_errors(thd, 0);
       if (! sp)
       {
@@ -4435,9 +4439,11 @@
       char *db, *name;
 
       if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
-	sp= sp_find_procedure(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+                            &thd->sp_proc_cache, FALSE);
       else
-	sp= sp_find_function(thd, lex->spname);
+        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+                            &thd->sp_func_cache, FALSE);
       mysql_reset_errors(thd, 0);
       if (sp)
       {

--- 1.54/sql/share/errmsg.txt	2005-11-06 08:41:30 +02:00
+++ 1.55/sql/share/errmsg.txt	2005-11-23 01:11:07 +02:00
@@ -5361,7 +5361,7 @@
 ER_NO_DEFAULT_FOR_VIEW_FIELD
         eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value"
 ER_SP_NO_RECURSION
-        eng "Recursive stored routines are not allowed."
+        eng "Recursive stored functions and triggers are not allowed."
 ER_TOO_BIG_SCALE 42000 S1009
         eng "Too big scale %d specified for column '%-.64s'. Maximum is %d."
 ER_TOO_BIG_PRECISION 42000 S1009
@@ -5421,3 +5421,5 @@
 	eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
 ER_SP_BAD_VAR_SHADOW 42000
 	eng "Variable '%-.64s' must be quoted with `...`, or renamed"
+ER_SP_RECURSION_LIMIT
+        eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.64s"

--- 1.20/mysql-test/r/trigger.result	2005-09-03 02:13:09 +03:00
+++ 1.21/mysql-test/r/trigger.result	2005-11-23 01:11:05 +02:00
@@ -703,8 +703,11 @@
 for each row insert into t2 values (new.f1+1);
 create trigger t2_ai after insert on t2
 for each row insert into t1 values (new.f2+1);
+set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth;
+set @@max_sp_recursion_depth=100;
 insert into t1 values (1);
 ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
+set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS;
 select * from t1;
 f1
 1

--- 1.27/mysql-test/t/trigger.test	2005-09-15 02:56:03 +03:00
+++ 1.28/mysql-test/t/trigger.test	2005-11-23 01:11:06 +02:00
@@ -743,8 +743,12 @@
   for each row insert into t2 values (new.f1+1);
 create trigger t2_ai after insert on t2
   for each row insert into t1 values (new.f2+1);
+# Allow SP resursion to be show that it has not influence here
+set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth;
+set @@max_sp_recursion_depth=100;
 --error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
 insert into t1 values (1);
+set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS;
 select * from t1;
 select * from t2;
 drop trigger t1_ai;

--- 1.1/mysql-test/r/sp-dynamic.result	2005-09-03 02:13:10 +03:00
+++ 1.2/mysql-test/r/sp-dynamic.result	2005-11-23 01:11:05 +02:00
@@ -33,6 +33,8 @@
 execute stmt;
 end|
 prepare stmt from "call p1()"|
+set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth|
+set @@max_sp_recursion_depth=100|
 execute stmt|
 ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
 execute stmt|
@@ -40,11 +42,18 @@
 execute stmt|
 ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
 call p1()|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
+call p1()|
+ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
+call p1()|
+ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
+set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS|
+call p1()|
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
 call p1()|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
 call p1()|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
 drop procedure p1|
 create procedure p1()
 begin

--- 1.1/mysql-test/t/sp-dynamic.test	2005-09-03 02:13:11 +03:00
+++ 1.2/mysql-test/t/sp-dynamic.test	2005-11-23 01:11:05 +02:00
@@ -26,18 +26,29 @@
   execute stmt;
 end|
 prepare stmt from "call p1()"|
+# Allow SP resursion to be show that it has not influence here
+set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth|
+set @@max_sp_recursion_depth=100|
 --error ER_PS_NO_RECURSION
 execute stmt|
 --error ER_PS_NO_RECURSION
 execute stmt|
 --error ER_PS_NO_RECURSION
 execute stmt|
---error ER_SP_NO_RECURSION 
+--error ER_PS_NO_RECURSION 
 call p1()|
---error ER_SP_NO_RECURSION 
+--error ER_PS_NO_RECURSION 
 call p1()|
---error ER_SP_NO_RECURSION 
+--error ER_PS_NO_RECURSION
+call p1()|
+set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS|
+--error ER_SP_RECURSION_LIMIT 
+call p1()|
+--error ER_SP_RECURSION_LIMIT
+call p1()|
+--error ER_SP_RECURSION_LIMIT
 call p1()|
+
 drop procedure p1|
 #
 # C. Create/drop a stored procedure in Dynamic SQL.

--- 1.75/mysql-test/r/variables.result	2005-10-19 23:47:02 +03:00
+++ 1.76/mysql-test/r/variables.result	2005-11-23 01:11:05 +02:00
@@ -351,6 +351,14 @@
 set global server_id=100;
 set global slow_launch_time=100;
 set sort_buffer_size=100;
+set @@max_sp_recursion_depth=10;
+select @@max_sp_recursion_depth;
+@@max_sp_recursion_depth
+10
+set @@max_sp_recursion_depth=0;
+select @@max_sp_recursion_depth;
+@@max_sp_recursion_depth
+0
 set sql_auto_is_null=1;
 select @@sql_auto_is_null;
 @@sql_auto_is_null

--- 1.50/mysql-test/t/variables.test	2005-10-19 23:47:02 +03:00
+++ 1.51/mysql-test/t/variables.test	2005-11-23 01:11:06 +02:00
@@ -237,6 +237,10 @@
 set global server_id=100;
 set global slow_launch_time=100;
 set sort_buffer_size=100;
+set @@max_sp_recursion_depth=10;
+select @@max_sp_recursion_depth;
+set @@max_sp_recursion_depth=0;
+select @@max_sp_recursion_depth;
 set sql_auto_is_null=1;
 select @@sql_auto_is_null;
 set @@sql_auto_is_null=0;

--- 1.142/sql/set_var.cc	2005-09-21 18:42:26 +03:00
+++ 1.143/sql/set_var.cc	2005-11-23 01:11:06 +02:00
@@ -258,6 +258,8 @@
                                                fix_max_relay_log_size);
 sys_var_thd_ulong	sys_max_sort_length("max_sort_length",
 					    &SV::max_sort_length);
+sys_var_thd_ulong	sys_max_sp_recursion_depth("max_sp_recursion_depth",
+                                                   &SV::max_sp_recursion_depth);
 sys_var_max_user_conn   sys_max_user_connections("max_user_connections");
 sys_var_thd_ulong	sys_max_tmp_tables("max_tmp_tables",
 					   &SV::max_tmp_tables);
@@ -628,6 +630,7 @@
   &sys_max_relay_log_size,
   &sys_max_seeks_for_key,
   &sys_max_sort_length,
+  &sys_max_sp_recursion_depth,
   &sys_max_tmp_tables,
   &sys_max_user_connections,
   &sys_max_write_lock_count,
@@ -892,6 +895,8 @@
   {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size,    SHOW_SYS},
   {sys_max_seeks_for_key.name,  (char*) &sys_max_seeks_for_key,	    SHOW_SYS},
   {sys_max_sort_length.name,	(char*) &sys_max_sort_length,	    SHOW_SYS},
+  {sys_max_sp_recursion_depth.name,
+    (char*) &sys_max_sp_recursion_depth, SHOW_SYS},
   {sys_max_tmp_tables.name,	(char*) &sys_max_tmp_tables,	    SHOW_SYS},
   {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS},
   {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS},

--- 1.176/client/mysqltest.c	2005-11-01 18:40:49 +02:00
+++ 1.177/client/mysqltest.c	2005-11-23 01:11:04 +02:00
@@ -3314,20 +3314,23 @@
         ((q->expected_errno[i].type == ERR_SQLSTATE) &&
          (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0)))
     {
-      if (q->expected_errors == 1)
+      if (!disable_result_log)
       {
-        /* Only log error if there is one possible error */
-        dynstr_append_mem(ds, "ERROR ", 6);
-        replace_dynstr_append(ds, err_sqlstate);
-        dynstr_append_mem(ds, ": ", 2);
-        replace_dynstr_append(ds, err_error);
-        dynstr_append_mem(ds,"\n",1);
+        if (q->expected_errors == 1)
+        {
+          /* Only log error if there is one possible error */
+          dynstr_append_mem(ds, "ERROR ", 6);
+          replace_dynstr_append(ds, err_sqlstate);
+          dynstr_append_mem(ds, ": ", 2);
+          replace_dynstr_append(ds, err_error);
+          dynstr_append_mem(ds,"\n",1);
+        }
+        /* Don't log error if we may not get an error */
+        else if (q->expected_errno[0].type == ERR_SQLSTATE ||
+                 (q->expected_errno[0].type == ERR_ERRNO &&
+                  q->expected_errno[0].code.errnum != 0))
+          dynstr_append(ds,"Got one of the listed errors\n");
       }
-      /* Don't log error if we may not get an error */
-      else if (q->expected_errno[0].type == ERR_SQLSTATE ||
-               (q->expected_errno[0].type == ERR_ERRNO &&
-                q->expected_errno[0].code.errnum != 0))
-        dynstr_append(ds,"Got one of the listed errors\n");
       /* OK */
       DBUG_RETURN(0);
     }
@@ -3335,11 +3338,14 @@
 
   DBUG_PRINT("info",("i: %d  expected_errors: %d", i, q->expected_errors));
 
-  dynstr_append_mem(ds, "ERROR ",6);
-  replace_dynstr_append(ds, err_sqlstate);
-  dynstr_append_mem(ds, ": ", 2);
-  replace_dynstr_append(ds, err_error);
-  dynstr_append_mem(ds, "\n", 1);
+  if (!disable_result_log)
+  {
+    dynstr_append_mem(ds, "ERROR ",6);
+    replace_dynstr_append(ds, err_sqlstate);
+    dynstr_append_mem(ds, ": ", 2);
+    replace_dynstr_append(ds, err_error);
+    dynstr_append_mem(ds, "\n", 1);
+  }
 
   if (i)
   {

--- 1.88/mysql-test/r/sp-error.result	2005-10-25 12:02:41 +03:00
+++ 1.89/mysql-test/r/sp-error.result	2005-11-23 01:11:05 +02:00
@@ -708,7 +708,7 @@
 end if;
 end|
 select bug11394(2)|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: Recursive stored functions and triggers are not allowed.
 drop function bug11394|
 create function bug11394_1(i int) returns int
 begin
@@ -719,7 +719,7 @@
 end if;
 end|
 select bug11394_1(2)|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: Recursive stored functions and triggers are not allowed.
 drop function bug11394_1|
 create function bug11394_2(i int) returns int return i|
 select bug11394_2(bug11394_2(10))|
@@ -733,7 +733,10 @@
 end if;
 end|
 call bug11394(2, 1)|
-ERROR HY000: Recursive stored routines are not allowed.
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine bug11394
+set @@max_sp_recursion_depth=10|
+call bug11394(2, 1)|
+set @@max_sp_recursion_depth=default|
 drop procedure bug11394|
 CREATE PROCEDURE BUG_12490() HELP CONTENTS;
 ERROR 0A000: HELP is not allowed in stored procedures

--- 1.169/mysql-test/r/sp.result	2005-11-03 13:20:07 +02:00
+++ 1.170/mysql-test/r/sp.result	2005-11-23 01:11:05 +02:00
@@ -3617,4 +3617,189 @@
 drop table t3, t4|
 drop procedure bug14210|
 set @@session.max_heap_table_size=default|
+drop function if exists bug10100f|
+drop procedure if exists bug10100p|
+drop procedure if exists bug10100t|
+drop procedure if exists bug10100pt|
+drop procedure if exists bug10100pv|
+drop procedure if exists bug10100pd|
+drop procedure if exists bug10100pc|
+create function bug10100f(prm int) returns int
+begin
+if prm > 1 then
+return prm * bug10100f(prm - 1);
+end if;
+return 1;
+end|
+create procedure bug10100p(prm int, inout res int)
+begin
+set res = res * prm;
+if prm > 1 then
+call bug10100p(prm - 1, res);  
+end if;
+end|
+create procedure bug10100t(prm int)
+begin
+declare res int;
+set res = 1;
+call bug10100p(prm, res);
+select res;
+end|
+create table t3 (a int)|
+insert into t3 values (0)|
+create view v1 as select a from t3;
+create procedure bug10100pt(level int, lim int)
+begin
+if level < lim then
+update t3 set a=level;
+FLUSH TABLES;
+call bug10100pt(level+1, lim);
+else
+select * from t3;
+end if;
+end|
+create procedure bug10100pv(level int, lim int)
+begin
+if level < lim then
+update v1 set a=level;
+FLUSH TABLES;
+call bug10100pv(level+1, lim);
+else
+select * from v1;
+end if;
+end|
+prepare stmt2 from "select * from t3;";
+create procedure bug10100pd(level int, lim int)
+begin
+if level < lim then
+select level;
+prepare stmt1 from "update t3 set a=a+2";
+execute stmt1;
+FLUSH TABLES;
+execute stmt1;
+FLUSH TABLES;
+execute stmt1;
+FLUSH TABLES;
+deallocate prepare stmt1;
+execute stmt2;
+select * from t3;
+call bug10100pd(level+1, lim);
+else
+execute stmt2;
+end if;
+end|
+create procedure bug10100pc(level int, lim int)
+begin
+declare lv int;
+declare c cursor for select a from t3;
+open c;
+if level < lim then
+select level;
+fetch c into lv;
+select lv;
+update t3 set a=level+lv;
+FLUSH TABLES;
+call bug10100pc(level+1, lim);
+else
+select * from t3;
+end if;
+close c;
+end|
+set @@max_sp_recursion_depth=4|
+select @@max_sp_recursion_depth|
+@@max_sp_recursion_depth
+4
+select bug10100f(3)|
+ERROR HY000: Recursive stored functions and triggers are not allowed.
+select bug10100f(6)|
+ERROR HY000: Recursive stored functions and triggers are not allowed.
+call bug10100t(5)|
+res
+120
+call bug10100pt(1,5)|
+a
+4
+call bug10100pv(1,5)|
+a
+4
+update t3 set a=1|
+call bug10100pd(1,5)|
+level
+1
+a
+7
+a
+7
+level
+2
+a
+13
+a
+13
+level
+3
+a
+19
+a
+19
+level
+4
+a
+25
+a
+25
+a
+25
+select * from t3|
+a
+25
+update t3 set a=1|
+call bug10100pc(1,5)|
+level
+1
+lv
+1
+level
+2
+lv
+2
+level
+3
+lv
+4
+level
+4
+lv
+7
+a
+11
+select * from t3|
+a
+11
+set @@max_sp_recursion_depth=0|
+select @@max_sp_recursion_depth|
+@@max_sp_recursion_depth
+0
+select bug10100f(5)|
+ERROR HY000: Recursive stored functions and triggers are not allowed.
+call bug10100t(5)|
+ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine bug10100p
+set @@max_sp_recursion_depth=255|
+set @var=1|
+call bug10100p(255, @var)|
+call bug10100pt(1,255)|
+call bug10100pv(1,255)|
+call bug10100pd(1,255)|
+call bug10100pc(1,255)|
+set @@max_sp_recursion_depth=0|
+deallocate prepare stmt2|
+drop function bug10100f|
+drop procedure bug10100p|
+drop procedure bug10100t|
+drop procedure bug10100pt|
+drop procedure bug10100pv|
+drop procedure bug10100pd|
+drop procedure bug10100pc|
+drop view v1|
+drop table t3|
 drop table t1,t2;

--- 1.92/mysql-test/t/sp-error.test	2005-10-25 12:02:41 +03:00
+++ 1.93/mysql-test/t/sp-error.test	2005-11-23 01:11:05 +02:00
@@ -1044,10 +1044,11 @@
     call bug11394(i - 1,(select 1));
   end if;
 end|
-# Again if we allow recursion for stored procedures (without 
-# additional efforts) the following statement will crash the server.
---error 1424
+--error ER_SP_RECURSION_LIMIT
 call bug11394(2, 1)|
+set @@max_sp_recursion_depth=10|
+call bug11394(2, 1)|
+set @@max_sp_recursion_depth=default|
 drop procedure bug11394|
 delimiter ;|
 

--- 1.162/mysql-test/t/sp.test	2005-11-03 13:20:07 +02:00
+++ 1.163/mysql-test/t/sp.test	2005-11-23 01:11:05 +02:00
@@ -4542,6 +4542,160 @@
 set @@session.max_heap_table_size=default|
 
 #
+# BUG#10100: function (and stored procedure?) recursivity problem
+#
+--disable_warnings
+drop function if exists bug10100f|
+drop procedure if exists bug10100p|
+drop procedure if exists bug10100t|
+drop procedure if exists bug10100pt|
+drop procedure if exists bug10100pv|
+drop procedure if exists bug10100pd|
+drop procedure if exists bug10100pc|
+--enable_warnings
+# routines with simple recursion
+create function bug10100f(prm int) returns int
+begin
+  if prm > 1 then
+    return prm * bug10100f(prm - 1);
+  end if;
+  return 1;
+end|
+create procedure bug10100p(prm int, inout res int)
+begin
+  set res = res * prm;
+  if prm > 1 then
+    call bug10100p(prm - 1, res);  
+  end if;
+end|
+create procedure bug10100t(prm int)
+begin
+  declare res int;
+  set res = 1;
+  call bug10100p(prm, res);
+  select res;
+end|
+
+# a procedure which use tables and recursion
+create table t3 (a int)|
+insert into t3 values (0)|
+create view v1 as select a from t3;
+create procedure bug10100pt(level int, lim int)
+begin
+  if level < lim then
+    update t3 set a=level;
+    FLUSH TABLES;
+    call bug10100pt(level+1, lim);
+  else
+    select * from t3;
+  end if;
+end|
+# view & recursion
+create procedure bug10100pv(level int, lim int)
+begin
+  if level < lim then
+    update v1 set a=level;
+    FLUSH TABLES;
+    call bug10100pv(level+1, lim);
+  else
+    select * from v1;
+  end if;
+end|
+# dynamic sql & recursion
+prepare stmt2 from "select * from t3;";
+create procedure bug10100pd(level int, lim int)
+begin
+  if level < lim then
+    select level;
+    prepare stmt1 from "update t3 set a=a+2";
+    execute stmt1;
+    FLUSH TABLES;
+    execute stmt1;
+    FLUSH TABLES;
+    execute stmt1;
+    FLUSH TABLES;
+    deallocate prepare stmt1;
+    execute stmt2;
+    select * from t3;
+    call bug10100pd(level+1, lim);
+  else
+    execute stmt2;
+  end if;
+end|
+# cursor & recursion
+create procedure bug10100pc(level int, lim int)
+begin
+  declare lv int;
+  declare c cursor for select a from t3;
+  open c;
+  if level < lim then
+    select level;
+    fetch c into lv;
+    select lv;
+    update t3 set a=level+lv;
+    FLUSH TABLES;
+    call bug10100pc(level+1, lim);
+  else
+    select * from t3;
+  end if;
+  close c;
+end|
+
+set @@max_sp_recursion_depth=4|
+select @@max_sp_recursion_depth|
+-- error ER_SP_NO_RECURSION
+select bug10100f(3)|
+-- error ER_SP_NO_RECURSION
+select bug10100f(6)|
+call bug10100t(5)|
+call bug10100pt(1,5)|
+call bug10100pv(1,5)|
+update t3 set a=1|
+call bug10100pd(1,5)|
+select * from t3|
+update t3 set a=1|
+call bug10100pc(1,5)|
+select * from t3|
+set @@max_sp_recursion_depth=0|
+select @@max_sp_recursion_depth|
+-- error ER_SP_NO_RECURSION
+select bug10100f(5)|
+-- error ER_SP_RECURSION_LIMIT
+call bug10100t(5)|
+
+#end of the stack checking
+set @@max_sp_recursion_depth=255|
+set @var=1|
+#disable log because error about stack overrun contains numbers which
+#depend on a system
+-- disable_result_log
+-- error ER_STACK_OVERRUN_NEED_MORE
+call bug10100p(255, @var)|
+-- error ER_STACK_OVERRUN_NEED_MORE
+call bug10100pt(1,255)|
+-- error ER_STACK_OVERRUN_NEED_MORE
+call bug10100pv(1,255)|
+-- error ER_STACK_OVERRUN_NEED_MORE
+call bug10100pd(1,255)|
+-- error ER_STACK_OVERRUN_NEED_MORE
+call bug10100pc(1,255)|
+-- enable_result_log
+set @@max_sp_recursion_depth=0|
+
+deallocate prepare stmt2|
+
+drop function bug10100f|
+drop procedure bug10100p|
+drop procedure bug10100t|
+drop procedure bug10100pt|
+drop procedure bug10100pv|
+drop procedure bug10100pd|
+drop procedure bug10100pc|
+drop view v1|
+drop table t3|
+
+
+#
 # BUG#NNNN: New bug synopsis
 #
 #--disable_warnings

--- 1.96/sql/sp.cc	2005-10-07 03:37:20 +03:00
+++ 1.97/sql/sp.cc	2005-11-23 01:11:08 +02:00
@@ -29,6 +29,11 @@
 	      const char *returns, ulong returnslen,
 	      const char *body, ulong bodylen,
 	      st_sp_chistics *chistics);
+static int
+db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
+                ulong sql_mode, const char *params, const char *returns,
+                const char *body, st_sp_chistics &chistics,
+                const char *definer, longlong created, longlong modified);
 
 /*
  *
@@ -377,83 +382,77 @@
   close_proc_table(thd, &open_tables_state_backup);
   table= 0;
 
-  {
-    String defstr;
-    LEX *oldlex= thd->lex;
-    char olddb[128];
-    bool dbchanged;
-    enum enum_sql_command oldcmd= thd->lex->sql_command;
-    ulong old_sql_mode= thd->variables.sql_mode;
-    ha_rows select_limit= thd->variables.select_limit;
-
-    thd->variables.sql_mode= sql_mode;
-    thd->variables.select_limit= HA_POS_ERROR;
-
-    defstr.set_charset(system_charset_info);
-    if (!create_string(thd, &defstr,
-		       type,
-		       name,
-		       params, strlen(params),
-		       returns, strlen(returns),
-		       body, strlen(body),
-		       &chistics))
-    {
-      ret= SP_INTERNAL_ERROR;
-      goto done;
-    }
+  ret= db_load_routine(thd, type, name, sphp,
+                       sql_mode, params, returns, body, chistics,
+                       definer, created, modified);
+                       
+ done:
+  if (table)
+    close_proc_table(thd, &open_tables_state_backup);
+  DBUG_RETURN(ret);
+}
 
-    dbchanged= FALSE;
-    if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb),
-			    1, &dbchanged)))
-      goto done;
 
-    {
-      /* This is something of a kludge. We need to initialize some fields
-       * in thd->lex (the unit and master stuff), and the easiest way to
-       * do it is, is to call mysql_init_query(), but this unfortunately
-       * resets teh value_list where we keep the CALL parameters. So we
-       * copy the list and then restore it. (... and found_semicolon too).
-       */
-      List<Item> tmpvals= thd->lex->value_list;
-      char *tmpfsc= thd->lex->found_semicolon;
-
-      lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length());
-      thd->lex->value_list= tmpvals;
-      thd->lex->found_semicolon= tmpfsc;
-    }
+static int
+db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
+                ulong sql_mode, const char *params, const char *returns,
+                const char *body, st_sp_chistics &chistics,
+                const char *definer, longlong created, longlong modified)
+{
+  LEX *oldlex= thd->lex, newlex;
+  String defstr;
+  char olddb[128];
+  bool dbchanged;
+  ulong old_sql_mode= thd->variables.sql_mode;
+  ha_rows select_limit= thd->variables.select_limit;
+  int ret= SP_INTERNAL_ERROR;
+
+  thd->variables.sql_mode= sql_mode;
+  thd->variables.select_limit= HA_POS_ERROR;
+
+  thd->lex= &newlex;
+  newlex.current_select= NULL;
+
+  defstr.set_charset(system_charset_info);
+  if (!create_string(thd, &defstr,
+		     type,
+		     name,
+		     params, strlen(params),
+		     returns, strlen(returns),
+		     body, strlen(body),
+		     &chistics))
+    goto end;
 
-    if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
-    {
-      LEX *newlex= thd->lex;
-      sp_head *sp= newlex->sphead;
+  dbchanged= FALSE;
+  if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb),
+			  1, &dbchanged)))
+    goto end;
 
-      if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
-	goto done;
-      if (sp)
-      {
-	delete sp;
-	newlex->sphead= NULL;
-      }
-      ret= SP_PARSE_ERROR;
-    }
-    else
-    {
-      if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
-	goto done;
-      *sphp= thd->lex->sphead;
-      (*sphp)->set_info((char *)definer, (uint)strlen(definer),
-			created, modified, &chistics, sql_mode);
-      (*sphp)->optimize();
-    }
-    thd->lex->sql_command= oldcmd;
-    thd->variables.sql_mode= old_sql_mode;
-    thd->variables.select_limit= select_limit;
-  }
+  lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length());
 
- done:
-  if (table)
-    close_proc_table(thd, &open_tables_state_backup);
-  DBUG_RETURN(ret);
+  if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL)
+  {
+    sp_head *sp= newlex.sphead;
+
+    if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
+      goto end;
+    delete sp;
+    ret= SP_PARSE_ERROR;
+  }
+  else
+  {
+    if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
+      goto end;
+    *sphp= newlex.sphead;
+    (*sphp)->set_info((char *)definer, (uint)strlen(definer),
+		      created, modified, &chistics, sql_mode);
+    (*sphp)->optimize();
+  }
+  thd->variables.sql_mode= old_sql_mode;
+  thd->variables.select_limit= select_limit;
+end:  
+  thd->lex= oldlex;
+  return ret;
 }
 
 
@@ -898,45 +897,106 @@
 ******************************************************************************/
 
 /*
-  Obtain object representing stored procedure by its name from
+  Obtain object representing stored procedure/function by its name from
   stored procedures cache and looking into mysql.proc if needed.
 
   SYNOPSIS
-    sp_find_procedure()
+    sp_find_routine()
       thd        - thread context
+      type       - type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
       name       - name of procedure
+      cp         - hash to look routine in
       cache_only - if true perform cache-only lookup
                    (Don't look in mysql.proc).
 
-  TODO
-    We should consider merging of sp_find_procedure() and
-    sp_find_function() into one sp_find_routine() function
-    (the same applies to other similarly paired functions).
-
   RETURN VALUE
     Non-0 pointer to sp_head object for the procedure, or
     0 - in case of error.
 */
 
 sp_head *
-sp_find_procedure(THD *thd, sp_name *name, bool cache_only)
+sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
+                bool cache_only)
 {
   sp_head *sp;
-  DBUG_ENTER("sp_find_procedure");
-  DBUG_PRINT("enter", ("name: %.*s.%.*s",
-		       name->m_db.length, name->m_db.str,
-		       name->m_name.length, name->m_name.str));
-
-  if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)) && !cache_only)
+  ulong depth= (type == TYPE_ENUM_PROCEDURE ?
+                thd->variables.max_sp_recursion_depth :
+                0);
+
+  DBUG_ENTER("sp_find_routine");
+  DBUG_PRINT("enter", ("name:  %.*s.%.*s, type: %d, cache only %d",
+                       name->m_db.length, name->m_db.str,
+                       name->m_name.length, name->m_name.str,
+                       type, cache_only));
+
+  if ((sp= sp_cache_lookup(cp, name)))
+  {
+    ulong level;
+    DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp));
+    if (sp->m_first_free_instance)
+    {
+      DBUG_PRINT("info", ("first free: 0x%lx, level: %lu, flags %x",
+                          (ulong)sp->m_first_free_instance,
+                          sp->m_first_free_instance->m_recursion_level,
+                          sp->m_first_free_instance->m_flags));
+      DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED));
+      if (sp->m_first_free_instance->m_recursion_level > depth)
+      {
+        sp->recursion_level_error();
+        DBUG_RETURN(0);
+      }
+      DBUG_RETURN(sp->m_first_free_instance);
+    }
+    level= sp->m_last_cached_sp->m_recursion_level + 1;
+    if (level > depth)
+    {
+      sp->recursion_level_error();
+      DBUG_RETURN(0);
+    }
+    {
+      sp_head *new_sp;
+      const char *returns= "";
+      char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+      String retstr(64);
+      strxmov(definer, sp->m_definer_user.str, "@",
+                       sp->m_definer_host.str, NullS);
+      if (type == TYPE_ENUM_FUNCTION)
+      {
+        sp_returns_type(thd, retstr, sp);
+        returns= retstr.ptr();
+      }
+      if (db_load_routine(thd, type, name, &new_sp,
+                          sp->m_sql_mode, sp->m_params.str, returns,
+                          sp->m_body.str, *sp->m_chistics, definer,
+                          sp->m_created, sp->m_modified) == SP_OK)
+      {
+        sp->m_last_cached_sp->m_next_cached_sp= new_sp;
+        new_sp->m_recursion_level= level;
+        new_sp->m_first_instance= sp;
+        sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
+        DBUG_PRINT("info", ("added level: 0x%lx, level: %lu, flags %x",
+                          (ulong)new_sp, new_sp->m_recursion_level,
+                          new_sp->m_flags));
+        DBUG_RETURN(new_sp);
+      }
+      DBUG_RETURN(0);
+    }
+  }
+  if (!cache_only)
   {
-    if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
-      sp_cache_insert(&thd->sp_proc_cache, sp);
+    if (db_find_routine(thd, type, name, &sp) == SP_OK)
+    {
+      sp_cache_insert(cp, sp);
+      DBUG_PRINT("info", ("added new: 0x%lx, level: %lu, flags %x",
+                          (ulong)sp, sp->m_recursion_level,
+                          sp->m_flags));
+    }
   }
-
   DBUG_RETURN(sp);
 }
 
 
+
 int
 sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
 {
@@ -954,8 +1014,10 @@
     lex_name.str= thd->strmake(table->table_name, lex_name.length);
     name= new sp_name(lex_db, lex_name);
     name->init_qname(thd);
-    if (sp_find_procedure(thd, name) != NULL ||
-        sp_find_function(thd, name) != NULL)
+    if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
+                        &thd->sp_proc_cache, FALSE) != NULL ||
+        sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
+                        &thd->sp_func_cache, FALSE) != NULL)
     {
       if (any)
         DBUG_RETURN(1);
@@ -1023,7 +1085,8 @@
   DBUG_ENTER("sp_show_create_procedure");
   DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
 
-  if ((sp= sp_find_procedure(thd, name)))
+  if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
+                           &thd->sp_proc_cache, FALSE)))
   {
     int ret= sp->show_create_procedure(thd);
 
@@ -1049,42 +1112,6 @@
   FUNCTION
 ******************************************************************************/
 
-/*
-  Obtain object representing stored function by its name from
-  stored functions cache and looking into mysql.proc if needed.
-
-  SYNOPSIS
-    sp_find_function()
-      thd        - thread context
-      name       - name of function
-      cache_only - if true perform cache-only lookup
-                   (Don't look in mysql.proc).
-
-  NOTE
-    See TODO section for sp_find_procedure().
-
-  RETURN VALUE
-    Non-0 pointer to sp_head object for the function, or
-    0 - in case of error.
-*/
-
-sp_head *
-sp_find_function(THD *thd, sp_name *name, bool cache_only)
-{
-  sp_head *sp;
-  DBUG_ENTER("sp_find_function");
-  DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
-  if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name)) &&
-      !cache_only)
-  {
-    if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) == SP_OK)
-      sp_cache_insert(&thd->sp_func_cache, sp);
-  }
-  DBUG_RETURN(sp);
-}
-
-
 int
 sp_create_function(THD *thd, sp_head *sp)
 {
@@ -1132,7 +1159,8 @@
   DBUG_ENTER("sp_show_create_function");
   DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
 
-  if ((sp= sp_find_function(thd, name)))
+  if ((sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
+                           &thd->sp_func_cache, FALSE)))
   {
     int ret= sp->show_create_function(thd);
 
@@ -1442,10 +1470,6 @@
                               &thd->sp_func_cache : &thd->sp_proc_cache),
                               &name)))
     {
-      LEX *oldlex= thd->lex;
-      LEX *newlex= new st_lex;
-      thd->lex= newlex;
-      newlex->current_select= NULL;
       name.m_name.str= strchr(name.m_qname.str, '.');
       name.m_db.length= name.m_name.str - name.m_qname.str;
       name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
@@ -1460,8 +1484,6 @@
         else
           sp_cache_insert(&thd->sp_proc_cache, sp);
       }
-      delete newlex;
-      thd->lex= oldlex;
     }
     if (sp)
     {

--- 1.29/sql/sp.h	2005-09-15 02:56:03 +03:00
+++ 1.30/sql/sp.h	2005-11-23 01:11:08 +02:00
@@ -36,7 +36,8 @@
 sp_drop_db_routines(THD *thd, char *db);
 
 sp_head *
-sp_find_procedure(THD *thd, sp_name *name, bool cache_only = 0);
+sp_find_routine(THD *thd, int type, sp_name *name,
+                sp_cache **cp, bool cache_only);
 
 int
 sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
@@ -56,9 +57,6 @@
 
 int
 sp_show_status_procedure(THD *thd, const char *wild);
-
-sp_head *
-sp_find_function(THD *thd, sp_name *name, bool cache_only = 0);
 
 int
 sp_create_function(THD *thd, sp_head *sp);

--- 1.193/sql/sp_head.cc	2005-10-21 14:07:50 +03:00
+++ 1.194/sql/sp_head.cc	2005-11-23 01:11:08 +02:00
@@ -437,7 +437,8 @@
 
 sp_head::sp_head()
   :Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
-   m_flags(0), m_returns_cs(NULL)
+   m_flags(0), m_returns_cs(NULL), m_recursion_level(0), m_next_cached_sp(0),
+   m_first_instance(this), m_first_free_instance(this), m_last_cached_sp(this)
 {
   extern byte *
     sp_table_key(const byte *ptr, uint *plen, my_bool first);
@@ -615,6 +616,7 @@
 sp_head::~sp_head()
 {
   destroy();
+  delete m_next_cached_sp;
   if (m_thd)
     restore_thd_mem_root(m_thd);
 }
@@ -841,6 +843,31 @@
 
 
 /*
+  Return appropriate error about recursion limit reaching
+
+  SYNOPSIS
+    sp_head::recursion_level_error()
+
+  NOTE
+    For functions and triggers we return error about prohibited recursion.
+    For stored procedures we return about reaching recursion limit.
+*/
+
+void sp_head::recursion_level_error()
+{
+  if (m_type == TYPE_ENUM_PROCEDURE)
+  {
+    THD *thd= current_thd;
+    my_error(ER_SP_RECURSION_LIMIT, MYF(0),
+             thd->variables.max_sp_recursion_depth,
+             m_name);
+  }
+  else
+    my_error(ER_SP_NO_RECURSION, MYF(0));
+}
+
+
+/*
   Execute the routine. The main instruction jump loop is there 
   Assume the parameters already set.
   
@@ -869,37 +896,31 @@
   Item_change_list old_change_list;
   String old_packet;
 
-  /* init per-instruction memroot */
-  init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
-
-
   /* Use some extra margin for possible SP recursion and functions */
-  if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb))
+  if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet))
   {
     DBUG_RETURN(-1);
   }
 
-  if (m_flags & IS_INVOKED)
-  {
-    /*
-      We have to disable recursion for stored routines since in
-      many cases LEX structure and many Item's can't be used in
-      reentrant way now.
-
-      TODO: We can circumvent this problem by using separate
-      sp_head instances for each recursive invocation.
-
-      NOTE: Theoretically arguments of procedure can be evaluated
-      before its invocation so there should be no problem with
-      recursion. But since we perform cleanup for CALL statement
-      as for any other statement only after its execution, its LEX
-      structure is not reusable for recursive calls. Thus we have
-      to prohibit recursion for stored procedures too.
-    */
-    my_error(ER_SP_NO_RECURSION, MYF(0));
-    DBUG_RETURN(-1);
-  }
+  /* init per-instruction memroot */
+  init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+
+  DBUG_ASSERT(!(m_flags & IS_INVOKED));
   m_flags|= IS_INVOKED;
+  m_first_instance->m_first_free_instance= m_next_cached_sp;
+  DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x",
+                      (ulong)m_first_instance, this, m_next_cached_sp,
+                      m_next_cached_sp->m_recursion_level,
+                      m_next_cached_sp->m_flags));
+  /*
+    Check that if there are not any instances after this one then
+    pointer to the last instance points on this instance or if there are
+    some instances after this one then recursion level of next instance
+    greater then recursion level of current instance on 1
+  */
+  DBUG_ASSERT((m_next_cached_sp == 0 &&
+               m_first_instance->m_last_cached_sp == this) ||
+              (m_recursion_level + 1 == m_next_cached_sp->m_recursion_level));
 
   dbchanged= FALSE;
   if (m_db.length &&
@@ -1074,6 +1095,29 @@
       ret= mysql_change_db(thd, olddb, 1);
   }
   m_flags&= ~IS_INVOKED;
+  DBUG_PRINT("info", ("first free for 0x%lx --: 0x%lx->0x%lx, level: %lu, flags %x",
+                      (ulong)m_first_instance,
+                      m_first_instance->m_first_free_instance, this,
+                      m_recursion_level, m_flags));
+  /*
+    Check that we have one of following:
+
+    1) there are not free instances which means that this instance is last
+    in the list of instances (pointer to the last instance point on it and
+    ther are not other instances after this one in the list)
+
+    2) There are some free instances which mean that first free instance
+    should go just after this one and recursion level of that free instance
+    should be on 1 more then recursion leven of this instance.
+  */
+  DBUG_ASSERT((m_first_instance->m_first_free_instance == 0 &&
+               this == m_first_instance->m_last_cached_sp &&
+               m_next_cached_sp == 0) ||
+              (m_first_instance->m_first_free_instance != 0 &&
+               m_first_instance->m_first_free_instance == m_next_cached_sp &&
+               m_first_instance->m_first_free_instance->m_recursion_level ==
+               m_recursion_level + 1));
+  m_first_instance->m_first_free_instance= this;
   DBUG_RETURN(ret);
 }
 

--- 1.73/sql/sp_head.h	2005-09-22 23:46:49 +03:00
+++ 1.74/sql/sp_head.h	2005-11-23 01:11:09 +02:00
@@ -140,6 +140,32 @@
   LEX_STRING m_definer_host;
   longlong m_created;
   longlong m_modified;
+  /* Recursion level of the current SP instance. The levels are numbered from 0 */
+  ulong m_recursion_level;
+  /*
+    A list of diferent recursion level instances for the same procedure.
+    For every recursion level we have a sp_head instance. This instances
+    connected in the list. The list ordered by increasing recursion level
+    (m_recursion_level).
+  */
+  sp_head *m_next_cached_sp;
+  /*
+    Pointer to the first element of the above list
+  */
+  sp_head *m_first_instance;
+  /*
+    Pointer to the first free (non-INVOKED) routine in the list of
+    cached instances for this SP. This pointer is set only for the first
+    SP in the list of instences (see above m_first_cached_sp pointer).
+    The pointer equal to 0 if we have no free instances.
+    For non-first instance value of this pointer meanless (point to itself);
+  */
+  sp_head *m_first_free_instance;
+  /*
+    Pointer to the last element in the list of instances of the SP.
+    For non-first instance value of this pointer meanless (point to itself);
+  */
+  sp_head *m_last_cached_sp;
   /*
     Set containing names of stored routines used by this routine.
     Note that unlike elements of similar set for statement elements of this
@@ -261,6 +287,8 @@
 
   void optimize();
   void opt_mark(uint ip);
+
+  void recursion_level_error();
 
   inline sp_instr *
   get_instr(uint i)
Thread
bk commit into 5.0 tree (bell:1.1978) BUG#10100sanja23 Nov