List:Internals« Previous MessageNext Message »
From:antony Date:September 8 2005 2:49pm
Subject:bk commit into 5.0 tree (acurtis:1.1958) BUG#10100
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of antony. When antony 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.1958 05/09/08 15:49:40 acurtis@stripped +11 -0
  Bug#10100
    "function (and stored procedure?) recursivity problem"
    Permit recursion, tests for bug/feature

  sql/sql_class.h
    1.263 05/09/08 15:47:41 acurtis@stripped +1 -0
    new sp_recursion_levels 

  sql/sp_head.h
    1.68 05/09/08 15:47:41 acurtis@stripped +3 -0
    new sp_head variables - m_recursion_level, m_next_cached_sp

  sql/sp_head.cc
    1.180 05/09/08 15:47:41 acurtis@stripped +2 -1
    initialize new sp_head variables

  sql/sp.cc
    1.93 05/09/08 15:47:41 acurtis@stripped +137 -88
    refactor code for recursion.
    temporary LEX on stack, not on heap.

  sql/set_var.cc
    1.138 05/09/08 15:47:40 acurtis@stripped +4 -0
    sp_recursion_levels system variable

  sql/mysqld.cc
    1.492 05/09/08 15:47:40 acurtis@stripped +6 -0
    recursion-levels option

  sql/item_func.cc
    1.249 05/09/08 15:47:40 acurtis@stripped +3 -1
    changes for recursion

  mysql-test/t/variables.test
    1.49 05/09/08 15:47:39 acurtis@stripped +4 -0
    tests for recursion

  mysql-test/t/sp.test
    1.144 05/09/08 15:47:39 acurtis@stripped +47 -0
    tests for recursion

  mysql-test/r/variables.result
    1.73 05/09/08 15:47:39 acurtis@stripped +8 -0
    tests for recursion

  mysql-test/r/sp.result
    1.149 05/09/08 15:47:39 acurtis@stripped +53 -0
    tests for recursion

# 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:	acurtis
# Host:	ltantony.xiphis.org
# Root:	/usr/home/antony/work2/p3-bug10100.1

--- 1.248/sql/item_func.cc	2005-09-03 00:13:09 +01:00
+++ 1.249/sql/item_func.cc	2005-09-08 15:47:40 +01:00
@@ -4704,7 +4704,9 @@
   st_sp_security_context save_ctx;
 #endif
 
-  if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE)))
+  if ((!m_sp || (m_sp->m_flags & sp_head::IS_INVOKED) ||
+      (thd->variables.sp_recursion_levels > m_sp->m_recursion_level)) && 
+      !(m_sp= sp_find_function(thd, m_name, TRUE)))
   {
     my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
     goto error;

--- 1.491/sql/mysqld.cc	2005-09-01 19:25:13 +01:00
+++ 1.492/sql/mysqld.cc	2005-09-08 15:47:40 +01:00
@@ -4422,6 +4422,7 @@
   OPT_OPTIMIZER_PRUNE_LEVEL,
   OPT_UPDATABLE_VIEWS_WITH_LIMIT,
   OPT_SP_AUTOMATIC_PRIVILEGES,
+  OPT_SP_RECURSION_LEVELS,
   OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
   OPT_ENABLE_LARGE_PAGES,
   OPT_TIMED_MUTEXES,
@@ -5612,6 +5613,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},
+  {"recursion-levels", OPT_SP_RECURSION_LEVELS,
+   "Stored procedure recursion level",
+   (gptr*) &global_system_variables.sp_recursion_levels,
+   (gptr*) &max_system_variables.sp_recursion_levels, 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.262/sql/sql_class.h	2005-09-02 14:21:08 +01:00
+++ 1.263/sql/sql_class.h	2005-09-08 15:47:41 +01:00
@@ -528,6 +528,7 @@
   ulong completion_type;
   /* Determines which non-standard SQL behaviour should be enabled */
   ulong sql_mode;
+  ulong sp_recursion_levels;
   /* check of key presence in updatable view */
   ulong updatable_views_with_limit;
   ulong default_week_format;

--- 1.72/mysql-test/r/variables.result	2005-09-02 20:41:58 +01:00
+++ 1.73/mysql-test/r/variables.result	2005-09-08 15:47:39 +01:00
@@ -351,6 +351,14 @@
 set global server_id=100;
 set global slow_launch_time=100;
 set sort_buffer_size=100;
+set @@sp_recursion_levels=10;
+select @@sp_recursion_levels;
+@@sp_recursion_levels
+10
+set @@sp_recursion_levels=0;
+select @@sp_recursion_levels;
+@@sp_recursion_levels
+0
 set sql_auto_is_null=1;
 select @@sql_auto_is_null;
 @@sql_auto_is_null

--- 1.48/mysql-test/t/variables.test	2005-09-02 20:40:59 +01:00
+++ 1.49/mysql-test/t/variables.test	2005-09-08 15:47:39 +01:00
@@ -237,6 +237,10 @@
 set global server_id=100;
 set global slow_launch_time=100;
 set sort_buffer_size=100;
+set @@sp_recursion_levels=10;
+select @@sp_recursion_levels;
+set @@sp_recursion_levels=0;
+select @@sp_recursion_levels;
 set sql_auto_is_null=1;
 select @@sql_auto_is_null;
 set @@sql_auto_is_null=0;

--- 1.137/sql/set_var.cc	2005-09-02 20:40:59 +01:00
+++ 1.138/sql/set_var.cc	2005-09-08 15:47:40 +01:00
@@ -362,6 +362,8 @@
 				       &SV::table_type);
 sys_var_thd_storage_engine sys_storage_engine("storage_engine",
 				       &SV::table_type);
+sys_var_thd_ulong	sys_sp_recursion_levels("sp_recursion_levels",
+						&SV::sp_recursion_levels);
 #ifdef HAVE_REPLICATION
 sys_var_sync_binlog_period sys_sync_binlog_period("sync_binlog", &sync_binlog_period);
 sys_var_thd_ulong	sys_sync_replication("sync_replication",
@@ -682,6 +684,7 @@
   &sys_sql_warnings,
   &sys_sql_notes,
   &sys_storage_engine,
+  &sys_sp_recursion_levels,
 #ifdef HAVE_REPLICATION
   &sys_sync_binlog_period,
   &sys_sync_replication,
@@ -972,6 +975,7 @@
   {"sql_notes",               (char*) &sys_sql_notes,               SHOW_BOOL},
   {"sql_warnings",            (char*) &sys_sql_warnings,            SHOW_BOOL},
   {sys_storage_engine.name,   (char*) &sys_storage_engine,          SHOW_SYS},
+  {sys_sp_recursion_levels.name, (char*) &sys_sp_recursion_levels,  SHOW_SYS},
 #ifdef HAVE_REPLICATION
   {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period,     SHOW_SYS},
 #endif

--- 1.148/mysql-test/r/sp.result	2005-08-27 11:29:29 +01:00
+++ 1.149/mysql-test/r/sp.result	2005-09-08 15:47:39 +01:00
@@ -3193,4 +3193,57 @@
 return f1;
 end|
 drop function bug9048|
+drop function if exists bug10100f|
+drop procedure if exists bug10100p|
+drop procedure if exists bug10100t|
+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|
+set @@sp_recursion_levels=4|
+select @@sp_recursion_levels|
+@@sp_recursion_levels
+4
+select bug10100f(3)|
+bug10100f(3)
+6
+select bug10100f(4)|
+bug10100f(4)
+24
+select bug10100f(5)|
+bug10100f(5)
+120
+select bug10100f(6)|
+ERROR HY000: Recursive stored routines are not allowed.
+call bug10100t(5)|
+res
+120
+set @@sp_recursion_levels=0|
+select @@sp_recursion_levels|
+@@sp_recursion_levels
+0
+select bug10100f(5)|
+ERROR HY000: Recursive stored routines are not allowed.
+call bug10100t(5)|
+ERROR HY000: Recursive stored routines are not allowed.
+drop function bug10100f|
+drop procedure bug10100p|
+drop procedure bug10100t|
 drop table t1,t2;

--- 1.143/mysql-test/t/sp.test	2005-09-01 19:02:04 +01:00
+++ 1.144/mysql-test/t/sp.test	2005-09-08 15:47:39 +01:00
@@ -4044,6 +4044,53 @@
 drop function bug9048|
 
 #
+# 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|
+--enable_warnings
+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|
+set @@sp_recursion_levels=4|
+select @@sp_recursion_levels|
+select bug10100f(3)|
+select bug10100f(4)|
+select bug10100f(5)|
+--error 1424
+select bug10100f(6)|
+call bug10100t(5)|
+set @@sp_recursion_levels=0|
+select @@sp_recursion_levels|
+--error 1424
+select bug10100f(5)|
+--error 1424
+call bug10100t(5)|
+drop function bug10100f|
+drop procedure bug10100p|
+drop procedure bug10100t|
+
+#
 # BUG#NNNN: New bug synopsis
 #
 #--disable_warnings

--- 1.92/sql/sp.cc	2005-09-02 14:21:07 +01:00
+++ 1.93/sql/sp.cc	2005-09-08 15:47:41 +01:00
@@ -29,6 +29,14 @@
 	      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);
+static sp_head *
+sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
+                bool cache_only);
 
 /*
  *
@@ -377,83 +385,90 @@
   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;
+  {
+    /* 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= newlex.value_list;
+    char *tmpfsc= newlex.found_semicolon;
+
+    lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length());
+    newlex.value_list= tmpvals;
+    newlex.found_semicolon= tmpfsc;
   }
 
- 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;
 }
 
 
@@ -924,13 +939,8 @@
   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)
-  {
-    if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
-      sp_cache_insert(&thd->sp_proc_cache, sp);
-  }
-
+  sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &thd->sp_proc_cache,
+                      cache_only);
   DBUG_RETURN(sp);
 }
 
@@ -1072,12 +1082,57 @@
   sp_head *sp;
   DBUG_ENTER("sp_find_function");
   DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+  sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, &thd->sp_func_cache, 
+                      cache_only);
+  DBUG_RETURN(sp);
+}
+
+
+static sp_head *
+sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
+                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 (!(sp= sp_cache_lookup(cp, name)) && !cache_only)
   {
-    if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) == SP_OK)
-      sp_cache_insert(&thd->sp_func_cache, sp);
+    if (db_find_routine(thd, type, name, &sp) == SP_OK)
+      sp_cache_insert(cp, sp);
+  }
+  else
+  {
+    for (sp_head *sp2= sp; sp2; sp2= sp2->m_next_cached_sp)
+    {
+      if (sp2->m_recursion_level >= thd->variables.sp_recursion_levels)
+        DBUG_RETURN(sp);
+      if (!(sp2->m_flags & sp_head::IS_INVOKED))
+        DBUG_RETURN(sp2);
+      if (sp2->m_next_cached_sp)
+        continue;
+
+      /* Last iteration, need to create a new sp instance */
+      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, &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)
+      {
+        sp2->m_next_cached_sp= sp;
+        sp->m_recursion_level= 1 + sp2->m_recursion_level;
+      }
+    }
   }
   DBUG_RETURN(sp);
 }
@@ -1385,10 +1440,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,
@@ -1403,8 +1454,6 @@
         else
           sp_cache_insert(&thd->sp_proc_cache, sp);
       }
-      delete newlex;
-      thd->lex= oldlex;
     }
     if (sp)
     {

--- 1.179/sql/sp_head.cc	2005-09-03 00:13:09 +01:00
+++ 1.180/sql/sp_head.cc	2005-09-08 15:47:41 +01:00
@@ -395,7 +395,7 @@
 
 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(NULL)
 {
   extern byte *
     sp_table_key(const byte *ptr, uint *plen, my_bool first);
@@ -573,6 +573,7 @@
 sp_head::~sp_head()
 {
   destroy();
+  delete m_next_cached_sp;
   if (m_thd)
     restore_thd_mem_root(m_thd);
 }

--- 1.67/sql/sp_head.h	2005-09-03 00:13:09 +01:00
+++ 1.68/sql/sp_head.h	2005-09-08 15:47:41 +01:00
@@ -138,6 +138,9 @@
   LEX_STRING m_definer_host;
   longlong m_created;
   longlong m_modified;
+  ulong m_recursion_level;
+  sp_head *m_next_cached_sp;
+  
   /*
     Set containing names of stored routines used by this routine.
     Note that unlike elements of similar set for statement elements of this
Thread
bk commit into 5.0 tree (acurtis:1.1958) BUG#10100antony8 Sep