summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <bell@sanja.is.com.ua>2005-11-23 01:28:32 +0200
committerunknown <bell@sanja.is.com.ua>2005-11-23 01:28:32 +0200
commitaa06123f83d764c94f52bb7d53673ea98fcc8c42 (patch)
treeace720b23945432863b582bf907eecdaa87fc638 /sql
parenta2c26aa7109182ede0ad1d06f53b46cc3b41e2c2 (diff)
parent164ce4c5cd8b18c6fe8f376ae7475622ce1cdb09 (diff)
downloadmariadb-git-aa06123f83d764c94f52bb7d53673ea98fcc8c42.tar.gz
Merge sanja.is.com.ua:/home/bell/mysql/bk/work-bug7-5.0
into sanja.is.com.ua:/home/bell/mysql/bk/work-merge-5.0 mysql-test/r/sp-error.result: Auto merged mysql-test/r/trigger.result: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/sp.test: Auto merged mysql-test/t/trigger.test: Auto merged sql/item_func.cc: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/sp_head.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_base.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged mysql-test/r/sp.result: merge sql/share/errmsg.txt: merge
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.cc11
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/set_var.cc5
-rw-r--r--sql/share/errmsg.txt4
-rw-r--r--sql/sp.cc319
-rw-r--r--sql/sp.h6
-rw-r--r--sql/sp_head.cc96
-rw-r--r--sql/sp_head.h28
-rw-r--r--sql/sql_base.cc5
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_parse.cc18
11 files changed, 309 insertions, 190 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c20a774e57e..ef3ebde74e5 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4690,10 +4690,16 @@ Item_func_sp::sp_result_field(void) const
{
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);
@@ -4919,7 +4925,8 @@ Item_func_sp::find_and_check_access(THD *thd, ulong want_access,
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;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 35032d1ec41..b5b95e48889 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4545,6 +4545,7 @@ enum options_mysqld
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,
@@ -5760,6 +5761,11 @@ The minimum value for this variable is 4096.",
(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.",
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 89ad00d7839..632c37d2296 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -261,6 +261,8 @@ sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size",
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);
@@ -631,6 +633,7 @@ sys_var *sys_variables[]=
&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,
@@ -896,6 +899,8 @@ struct show_var_st init_vars[]= {
{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},
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index d2da1d0a7a8..d0773e8af3b 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5361,7 +5361,7 @@ ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
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
@@ -5425,3 +5425,5 @@ ER_TRG_NO_DEFINER
eng "No definer attribute for trigger '%-.64s'.'%-.64s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger."
ER_OLD_FILE_FORMAT
eng "'%-.64s' has an old format, you should re-create the '%s' object(s)"
+ER_SP_RECURSION_LIMIT
+ eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.64s"
diff --git a/sql/sp.cc b/sql/sp.cc
index 6939f87b31a..861a41d8501 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -29,6 +29,11 @@ create_string(THD *thd, String *buf,
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,87 +382,77 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
close_proc_table(thd, &open_tables_state_backup);
table= 0;
- {
- String defstr;
- LEX *oldlex= thd->lex;
- sp_rcontext *save_spcont= thd->spcont;
- 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;
- thd->spcont= 0;
- 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 db_done;
- *sphp= thd->lex->sphead;
- (*sphp)->set_definer((char*) definer, (uint) strlen(definer));
- (*sphp)->set_info(created, modified, &chistics, sql_mode);
- (*sphp)->optimize();
- }
-db_done:
- thd->spcont= save_spcont;
- 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;
}
@@ -555,13 +550,12 @@ db_create_routine(THD *thd, int type, sp_head *sp)
store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
system_charset_info);
- if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
- !trust_function_creators && mysql_bin_log.is_open())
+ if (!trust_routine_creators && mysql_bin_log.is_open())
{
if (!sp->m_chistics->detistic)
{
/*
- Note that this test is not perfect; one could use
+ Note that for a _function_ this test is not enough; one could use
a non-deterministic read-only function in an update statement.
*/
enum enum_sp_data_access access=
@@ -903,45 +897,106 @@ err:
******************************************************************************/
/*
- 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));
+ 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(&thd->sp_proc_cache, name)) && !cache_only)
+ if ((sp= sp_cache_lookup(cp, name)))
{
- if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
- sp_cache_insert(&thd->sp_proc_cache, sp);
+ 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, 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)
{
@@ -959,8 +1014,10 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
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);
@@ -1028,7 +1085,8 @@ sp_show_create_procedure(THD *thd, sp_name *name)
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);
@@ -1054,42 +1112,6 @@ sp_show_status_procedure(THD *thd, const char *wild)
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)
{
@@ -1137,7 +1159,8 @@ sp_show_create_function(THD *thd, sp_name *name)
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);
@@ -1447,10 +1470,6 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
&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,
@@ -1465,8 +1484,6 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
else
sp_cache_insert(&thd->sp_proc_cache, sp);
}
- delete newlex;
- thd->lex= oldlex;
}
if (sp)
{
@@ -1589,30 +1606,30 @@ create_string(THD *thd, String *buf,
chistics->comment.length))
return FALSE;
- buf->append(STRING_WITH_LEN("CREATE "));
+ buf->append("CREATE ", 7);
if (type == TYPE_ENUM_FUNCTION)
- buf->append(STRING_WITH_LEN("FUNCTION "));
+ buf->append("FUNCTION ", 9);
else
- buf->append(STRING_WITH_LEN("PROCEDURE "));
+ buf->append("PROCEDURE ", 10);
append_identifier(thd, buf, name->m_name.str, name->m_name.length);
buf->append('(');
buf->append(params, paramslen);
buf->append(')');
if (type == TYPE_ENUM_FUNCTION)
{
- buf->append(STRING_WITH_LEN(" RETURNS "));
+ buf->append(" RETURNS ", 9);
buf->append(returns, returnslen);
}
buf->append('\n');
switch (chistics->daccess) {
case SP_NO_SQL:
- buf->append(STRING_WITH_LEN(" NO SQL\n"));
+ buf->append(" NO SQL\n");
break;
case SP_READS_SQL_DATA:
- buf->append(STRING_WITH_LEN(" READS SQL DATA\n"));
+ buf->append(" READS SQL DATA\n");
break;
case SP_MODIFIES_SQL_DATA:
- buf->append(STRING_WITH_LEN(" MODIFIES SQL DATA\n"));
+ buf->append(" MODIFIES SQL DATA\n");
break;
case SP_DEFAULT_ACCESS:
case SP_CONTAINS_SQL:
@@ -1620,12 +1637,12 @@ create_string(THD *thd, String *buf,
break;
}
if (chistics->detistic)
- buf->append(STRING_WITH_LEN(" DETERMINISTIC\n"));
+ buf->append(" DETERMINISTIC\n", 18);
if (chistics->suid == SP_IS_NOT_SUID)
- buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
+ buf->append(" SQL SECURITY INVOKER\n", 25);
if (chistics->comment.length)
{
- buf->append(STRING_WITH_LEN(" COMMENT "));
+ buf->append(" COMMENT ");
append_unescaped(buf, chistics->comment.str, chistics->comment.length);
buf->append('\n');
}
diff --git a/sql/sp.h b/sql/sp.h
index 933e5793e4c..7f314b8903e 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -36,7 +36,8 @@ int
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);
@@ -57,9 +58,6 @@ sp_show_create_procedure(THD *thd, sp_name *name);
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);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 601d41a0afe..a6e88c08789 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -478,7 +478,8 @@ sp_head::operator delete(void *ptr, size_t size)
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);
@@ -659,6 +660,7 @@ sp_head::create(THD *thd)
sp_head::~sp_head()
{
destroy();
+ delete m_next_cached_sp;
if (m_thd)
restore_thd_mem_root(m_thd);
}
@@ -885,6 +887,31 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
/*
+ 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.
@@ -913,37 +940,31 @@ int sp_head::execute(THD *thd)
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 &&
@@ -1118,6 +1139,29 @@ int sp_head::execute(THD *thd)
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);
}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 9addec706e7..6334bca0fc6 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -143,6 +143,32 @@ public:
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
@@ -266,6 +292,8 @@ public:
void optimize();
void opt_mark(uint ip);
+ void recursion_level_error();
+
inline sp_instr *
get_instr(uint i)
{
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index cfd06008690..b765f2f53e2 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1088,6 +1088,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
/* 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,
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e92bf3b2bec..ee24d9e904b 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -530,6 +530,7 @@ struct system_variables
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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 462d1d0fbcd..437e910d592 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3682,7 +3682,8 @@ end_with_restore_list:
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;
@@ -4216,7 +4217,8 @@ end_with_restore_list:
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);
@@ -4340,9 +4342,11 @@ end_with_restore_list:
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)
{
@@ -4418,9 +4422,11 @@ end_with_restore_list:
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)
{