From 2bcd68973bb3ae278399a69b4642e76368edd31e Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 23 Nov 2005 00:50:37 +0200 Subject: [PATCH 1/4] Fix for BUG#13549 "Server crash with nested stored procedures if inner routine has more local variables than outer one, and one of its last variables was used as argument to NOT operator". THD::spcont was non-0 when we were parsing stored routine/trigger definition during execution of another stored routine. This confused methods of Item_splocal and forced them use wrong runtime context. Fix ensures that we always have THD::spcont equal to zero during routine/trigger body parsing. This also allows to avoid problems with errors which occur during parsing and SQL exception handlers. --- mysql-test/r/sp.result | 17 +++++++++++++++++ mysql-test/r/trigger.result | 14 ++++++++++++++ mysql-test/t/sp.test | 27 ++++++++++++++++++++++++++ mysql-test/t/trigger.test | 28 +++++++++++++++++++++++++++ sql/item.cc | 7 ++++++- sql/item.h | 9 +++++++++ sql/protocol.cc | 38 ++++++++++++++++++------------------- sql/protocol.h | 2 +- sql/sp.cc | 6 +++++- sql/sp_head.cc | 9 +++++++++ sql/sp_rcontext.h | 8 ++++++++ sql/sql_cache.h | 2 +- sql/sql_class.cc | 8 +++++++- sql/sql_class.h | 7 +++++++ sql/sql_trigger.cc | 3 +++ sql/sql_yacc.yy | 20 ++++++++++++++++--- 16 files changed, 178 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 1b8cde6d3db..bb20f77baa9 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3435,4 +3435,21 @@ Table Create Table tm1 CREATE TEMPORARY TABLE `tm1` ( `spv1` decimal(6,3) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop procedure bug12589_1| +drop procedure bug12589_2| +drop procedure bug12589_3| +drop procedure if exists bug13549_1| +drop procedure if exists bug13549_2| +CREATE PROCEDURE `bug13549_2`() +begin +call bug13549_1(); +end| +CREATE PROCEDURE `bug13549_1`() +begin +declare done int default 0; +set done= not done; +end| +CALL bug13549_2()| +drop procedure bug13549_2| +drop procedure bug13549_1| drop table t1,t2; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index b305691fa18..73f498d4133 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -738,3 +738,17 @@ f1 1 drop trigger t1_bi; drop tables t1, t2; +create table t1 (a int); +drop procedure if exists p2; +CREATE PROCEDURE `p2`() +begin +insert into t1 values (1); +end// +create trigger trg before insert on t1 for each row +begin +declare done int default 0; +set done= not done; +end// +CALL p2(); +drop procedure p2; +drop table t1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index e16e7456056..8c70bc9f6b1 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4313,7 +4313,34 @@ call bug12589_1()| # No warnings here call bug12589_2()| call bug12589_3()| +drop procedure bug12589_1| +drop procedure bug12589_2| +drop procedure bug12589_3| +# +# BUG#13549 "Server crash with nested stored procedures". +# Server should not crash when during execution of stored procedure +# we have to parse trigger/function definition and this new trigger/ +# function has more local variables declared than invoking stored +# procedure and last of these variables is used in argument of NOT +# operator. +# +--disable_warnings +drop procedure if exists bug13549_1| +drop procedure if exists bug13549_2| +--enable_warnings +CREATE PROCEDURE `bug13549_2`() +begin + call bug13549_1(); +end| +CREATE PROCEDURE `bug13549_1`() +begin + declare done int default 0; + set done= not done; +end| +CALL bug13549_2()| +drop procedure bug13549_2| +drop procedure bug13549_1| # # BUG#NNNN: New bug synopsis diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index cd79eb82ace..e66f092e695 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -875,3 +875,31 @@ drop function f1; drop view v1; drop table t1, t2, t3; --enable_parsing + +# +# BUG#13549 "Server crash with nested stored procedures". +# Server should not crash when during execution of stored procedure +# we have to parse trigger/function definition and this new trigger/ +# function has more local variables declared than invoking stored +# procedure and last of these variables is used in argument of NOT +# operator. +# +create table t1 (a int); +--disable_warnings +drop procedure if exists p2; +--enable_warnings +DELIMITER //; +CREATE PROCEDURE `p2`() +begin + insert into t1 values (1); +end// +create trigger trg before insert on t1 for each row +begin + declare done int default 0; + set done= not done; +end// +DELIMITER ;// +CALL p2(); +drop procedure p2; +drop table t1; + diff --git a/sql/item.cc b/sql/item.cc index 966dbbaec53..7a8ce37ce63 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -868,7 +868,7 @@ Item * Item_splocal::this_item() { THD *thd= current_thd; - + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset); } @@ -876,6 +876,7 @@ Item_splocal::this_item() Item ** Item_splocal::this_item_addr(THD *thd, Item **addr) { + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item_addr(m_offset); } @@ -884,6 +885,7 @@ Item_splocal::this_const_item() const { THD *thd= current_thd; + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset); } @@ -893,7 +895,10 @@ Item_splocal::type() const THD *thd= current_thd; if (thd->spcont) + { + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset)->type(); + } return NULL_ITEM; // Anything but SUBSELECT_ITEM } diff --git a/sql/item.h b/sql/item.h index a8f013f60d4..516cb05c2a2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -703,6 +703,8 @@ public: }; +class sp_head; + /* A reference to local SP variable (incl. reference to SP parameter), used in runtime. @@ -720,6 +722,13 @@ class Item_splocal : public Item uint m_offset; public: +#ifndef DBUG_OFF + /* + Routine to which this Item_splocal belongs. Used for checking if correct + runtime context is used for variable handling. + */ + sp_head *owner; +#endif LEX_STRING m_name; /* diff --git a/sql/protocol.cc b/sql/protocol.cc index ade94a483a8..490f27ab548 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -494,7 +494,7 @@ void Protocol::init(THD *thd_arg) thd=thd_arg; packet= &thd->packet; convert= &thd->convert_buffer; -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_types= 0; #endif } @@ -547,7 +547,7 @@ bool Protocol::send_fields(List *list, uint flags) (void) my_net_write(&thd->net, buff,(uint) (pos-buff)); } -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_types= (enum_field_types*) thd->alloc(sizeof(field_types) * list->elements); uint count= 0; @@ -644,7 +644,7 @@ bool Protocol::send_fields(List *list, uint flags) item->send(&prot, &tmp); // Send default value if (prot.write()) break; /* purecov: inspected */ -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_types[count++]= field.type; #endif } @@ -728,14 +728,14 @@ bool Protocol::store(I_List* str_list) void Protocol_simple::prepare_for_resend() { packet->length(0); -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_pos= 0; #endif } bool Protocol_simple::store_null() { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_pos++; #endif char buff[1]; @@ -769,7 +769,7 @@ bool Protocol::store_string_aux(const char *from, uint length, bool Protocol_simple::store(const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || field_types[field_pos] == MYSQL_TYPE_BIT || @@ -786,7 +786,7 @@ bool Protocol_simple::store(const char *from, uint length, CHARSET_INFO *fromcs) { CHARSET_INFO *tocs= this->thd->variables.character_set_results; -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || field_types[field_pos] == MYSQL_TYPE_BIT || @@ -801,7 +801,7 @@ bool Protocol_simple::store(const char *from, uint length, bool Protocol_simple::store_tiny(longlong from) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY); field_pos++; #endif @@ -813,7 +813,7 @@ bool Protocol_simple::store_tiny(longlong from) bool Protocol_simple::store_short(longlong from) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_YEAR || field_types[field_pos] == MYSQL_TYPE_SHORT); @@ -827,7 +827,7 @@ bool Protocol_simple::store_short(longlong from) bool Protocol_simple::store_long(longlong from) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_INT24 || field_types[field_pos] == MYSQL_TYPE_LONG); @@ -841,7 +841,7 @@ bool Protocol_simple::store_long(longlong from) bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_LONGLONG); field_pos++; @@ -856,7 +856,7 @@ bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag) bool Protocol_simple::store_decimal(const my_decimal *d) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); field_pos++; @@ -870,7 +870,7 @@ bool Protocol_simple::store_decimal(const my_decimal *d) bool Protocol_simple::store(float from, uint32 decimals, String *buffer) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_FLOAT); field_pos++; @@ -882,7 +882,7 @@ bool Protocol_simple::store(float from, uint32 decimals, String *buffer) bool Protocol_simple::store(double from, uint32 decimals, String *buffer) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DOUBLE); field_pos++; @@ -896,7 +896,7 @@ bool Protocol_simple::store(Field *field) { if (field->is_null()) return store_null(); -#ifndef DEBUG_OFF +#ifndef DBUG_OFF field_pos++; #endif char buff[MAX_FIELD_WIDTH]; @@ -917,7 +917,7 @@ bool Protocol_simple::store(Field *field) bool Protocol_simple::store(TIME *tm) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DATETIME || field_types[field_pos] == MYSQL_TYPE_TIMESTAMP); @@ -940,7 +940,7 @@ bool Protocol_simple::store(TIME *tm) bool Protocol_simple::store_date(TIME *tm) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DATE); field_pos++; @@ -959,7 +959,7 @@ bool Protocol_simple::store_date(TIME *tm) bool Protocol_simple::store_time(TIME *tm) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TIME); field_pos++; @@ -1084,7 +1084,7 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag) bool Protocol_prep::store_decimal(const my_decimal *d) { -#ifndef DEBUG_OFF +#ifndef DBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); field_pos++; diff --git a/sql/protocol.h b/sql/protocol.h index c00bbba4cc9..8d9da5774b2 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -31,7 +31,7 @@ protected: String *packet; String *convert; uint field_pos; -#ifndef DEBUG_OFF +#ifndef DBUG_OFF enum enum_field_types *field_types; #endif uint field_count; diff --git a/sql/sp.cc b/sql/sp.cc index 4f7b544f5c7..4a2da1bb8e6 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -380,6 +380,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { 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; @@ -422,6 +423,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) thd->lex->found_semicolon= tmpfsc; } + thd->spcont= 0; if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) { LEX *newlex= thd->lex; @@ -439,12 +441,14 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) else { if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) - goto done; + goto db_done; *sphp= thd->lex->sphead; (*sphp)->set_info((char *)definer, (uint)strlen(definer), 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; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 671acbc2a0c..3e25544839f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1112,6 +1112,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) // QQ Should have some error checking here? (types, etc...) if (!(nctx= new sp_rcontext(csize, hmax, cmax))) goto end; +#ifndef DBUG_OFF + nctx->owner= this; +#endif for (i= 0 ; i < argcount ; i++) { sp_pvar_t *pvar = m_pcont->find_pvar(i); @@ -1256,6 +1259,9 @@ int sp_head::execute_procedure(THD *thd, List *args) { // Create a temporary old context if (!(octx= new sp_rcontext(csize, hmax, cmax))) DBUG_RETURN(-1); +#ifndef DBUG_OFF + octx->owner= 0; +#endif thd->spcont= octx; /* set callers_arena to thd, for upper-level function to work */ @@ -1267,6 +1273,9 @@ int sp_head::execute_procedure(THD *thd, List *args) thd->spcont= save_spcont; DBUG_RETURN(-1); } +#ifndef DBUG_OFF + nctx->owner= this; +#endif if (csize > 0 || hmax > 0 || cmax > 0) { diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 22fa4f6e865..2988793083e 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -66,6 +66,14 @@ class sp_rcontext : public Sql_alloc */ Query_arena *callers_arena; +#ifndef DBUG_OFF + /* + Routine to which this Item_splocal belongs. Used for checking if correct + runtime context is used for variable handling. + */ + sp_head *owner; +#endif + sp_rcontext(uint fsize, uint hmax, uint cmax); ~sp_rcontext() diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 123d16b606d..69a0d6cd05d 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -410,7 +410,7 @@ protected: /* The following functions are only used when debugging - We don't protect these with ifndef DEBUG_OFF to not have to recompile + We don't protect these with ifndef DBUG_OFF to not have to recompile everything if we want to add checks of the cache at some places. */ void wreck(uint line, const char *message); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2917626ff35..32bbb456689 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1490,7 +1490,13 @@ int select_dumpvar::prepare(List &list, SELECT_LEX_UNIT *u) { my_var *mv= gl++; if (mv->local) - (void)local_vars.push_back(new Item_splocal(mv->s, mv->offset)); + { + Item_splocal *var; + (void)local_vars.push_back(var= new Item_splocal(mv->s, mv->offset)); +#ifndef DEBUG_OFF + var->owner= mv->owner; +#endif + } else { Item_func_set_user_var *var= new Item_func_set_user_var(mv->s, item); diff --git a/sql/sql_class.h b/sql/sql_class.h index 7cbfc19123f..56cb606558d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2075,6 +2075,13 @@ public: class my_var : public Sql_alloc { public: LEX_STRING s; +#ifndef DEBUG_OFF + /* + Routine to which this Item_splocal belongs. Used for checking if correct + runtime context is used for variable handling. + */ + sp_head *owner; +#endif bool local; uint offset; enum_field_types type; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index df8de59508d..27bee87c012 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -646,6 +646,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, char *trg_name_buff; List_iterator_fast itm(triggers->definition_modes_list); LEX *old_lex= thd->lex, lex; + sp_rcontext *save_spcont= thd->spcont; ulong save_sql_mode= thd->variables.sql_mode; thd->lex= &lex; @@ -660,6 +661,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->variables.sql_mode= (ulong)*trg_sql_mode; lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); + thd->spcont= 0; if (yyparse((void *)thd) || thd->is_fatal_error) { /* @@ -712,6 +714,7 @@ err_with_lex_cleanup: // QQ: anything else ? lex_end(&lex); thd->lex= old_lex; + thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; thd->db= save_db.str; thd->db_length= save_db.length; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f28fbe5c803..298c282d625 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2351,8 +2351,12 @@ sp_case: ivar.str= (char *)"_tmp_"; ivar.length= 5; - Item *var= (Item*) new Item_splocal(ivar, - ctx->current_pvars()-1); + Item_splocal *var= new Item_splocal(ivar, + ctx->current_pvars()-1); +#ifndef DEBUG_OFF + if (var) + var->owner= sp; +#endif Item *expr= new Item_func_eq(var, $2); i= new sp_instr_jump_if_not(ip, ctx, expr, lex); @@ -5925,7 +5929,13 @@ select_var_ident: YYABORT; else { - ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset,t->type)); + my_var *var; + ((select_dumpvar *)lex->result)-> + var_list.push_back(var= new my_var($1,1,t->offset,t->type)); +#ifndef DEBUG_OFF + if (var) + var->owner= lex->sphead; +#endif } } ; @@ -7224,6 +7234,10 @@ simple_ident: Item_splocal *splocal; splocal= new Item_splocal($1, spv->offset, lex->tok_start_prev - lex->sphead->m_tmp_query); +#ifndef DEBUG_OFF + if (splocal) + splocal->owner= lex->sphead; +#endif $$ = (Item*) splocal; lex->variables_used= 1; lex->safe_to_cache_query=0; From 7bd691f11e961060bb0a665936f6712cb7d7bb22 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 23 Nov 2005 01:11:19 +0200 Subject: [PATCH 2/4] Recursion support made for SP (BUG#10100). --- client/mysqltest.c | 40 +++-- mysql-test/r/sp-dynamic.result | 15 +- mysql-test/r/sp-error.result | 9 +- mysql-test/r/sp.result | 185 +++++++++++++++++++++ mysql-test/r/trigger.result | 3 + mysql-test/r/variables.result | 8 + mysql-test/t/sp-dynamic.test | 17 +- mysql-test/t/sp-error.test | 7 +- mysql-test/t/sp.test | 154 +++++++++++++++++ mysql-test/t/trigger.test | 4 + mysql-test/t/variables.test | 4 + sql/item_func.cc | 11 +- sql/mysqld.cc | 6 + sql/set_var.cc | 5 + sql/share/errmsg.txt | 4 +- sql/sp.cc | 292 ++++++++++++++++++--------------- sql/sp.h | 6 +- sql/sp_head.cc | 100 +++++++---- sql/sp_head.h | 28 ++++ sql/sql_base.cc | 5 + sql/sql_class.h | 1 + sql/sql_parse.cc | 18 +- 22 files changed, 717 insertions(+), 205 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 58c0928c36d..f6d28101fa0 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3314,20 +3314,23 @@ static int handle_error(const char *query, struct st_query *q, ((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 @@ static int handle_error(const char *query, struct st_query *q, 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) { diff --git a/mysql-test/r/sp-dynamic.result b/mysql-test/r/sp-dynamic.result index 8fe469431cc..cf07f540608 100644 --- a/mysql-test/r/sp-dynamic.result +++ b/mysql-test/r/sp-dynamic.result @@ -33,6 +33,8 @@ begin 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 @@ ERROR HY000: The prepared statement contains a stored routine call that refers t 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: 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: 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 +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 limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1 +call p1()| +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 diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 50ff7ea264a..ae539e1654c 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -708,7 +708,7 @@ return (i in (100, 200, bug11394(i-1), 400)); 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 @@ return (select bug11394_1(i-1)); 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 @@ call bug11394(i - 1,(select 1)); 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 diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index d50e6dd3751..1665143d6d2 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3617,4 +3617,189 @@ count(*) 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; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index b305691fa18..66f7f682cec 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -703,8 +703,11 @@ create trigger t1_ai after insert on t1 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 diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 3ecc48620b1..df180218a09 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -351,6 +351,14 @@ set global rpl_recovery_rank=100; 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 diff --git a/mysql-test/t/sp-dynamic.test b/mysql-test/t/sp-dynamic.test index e9816ee3ef0..5416f5931ff 100644 --- a/mysql-test/t/sp-dynamic.test +++ b/mysql-test/t/sp-dynamic.test @@ -26,18 +26,29 @@ begin 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. diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e2343cd905c..c79f2294baf 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1044,10 +1044,11 @@ begin 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 ;| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index eaf69c0ab03..bff5a9496b6 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4541,6 +4541,160 @@ drop table t3, t4| drop procedure bug14210| 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 # diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index cd79eb82ace..c17bee34130 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -743,8 +743,12 @@ create trigger t1_ai after insert on t1 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; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index f23cc0152c1..2d0e2dbc9c9 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -237,6 +237,10 @@ set global rpl_recovery_rank=100; 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; diff --git a/sql/item_func.cc b/sql/item_func.cc index f467981540b..7ff59478e1f 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); @@ -4925,7 +4931,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 acad378353b..79e4f0a6ca9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4543,6 +4543,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, @@ -5745,6 +5746,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 8cf7311265c..79443a05bfe 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -258,6 +258,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); @@ -628,6 +630,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, @@ -892,6 +895,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 f85bda90e81..8a2334f0033 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 @@ -5421,3 +5421,5 @@ ER_NO_REFERENCED_ROW_2 23000 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" diff --git a/sql/sp.cc b/sql/sp.cc index 8386c5d58a2..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,79 +382,10 @@ 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; - 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; - } - - 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 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; - } - - if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) - { - LEX *newlex= thd->lex; - sp_head *sp= newlex->sphead; - - 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; - } - + 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); @@ -457,6 +393,69 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) } +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; + + dbchanged= FALSE; + if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), + 1, &dbchanged))) + goto end; + + lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); + + 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; +} + + static void sp_returns_type(THD *thd, String &result, sp_head *sp) { @@ -898,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); - if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)) && !cache_only) + 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))) { - 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) { @@ -954,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); @@ -1023,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); @@ -1049,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) { @@ -1132,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); @@ -1442,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, @@ -1460,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) { 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 abc66ce0b21..cb0413947df 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -437,7 +437,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); @@ -615,6 +616,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); } @@ -840,6 +842,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. @@ -869,37 +896,31 @@ int sp_head::execute(THD *thd) Item_change_list old_change_list; String old_packet; + /* Use some extra margin for possible SP recursion and functions */ + if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet)) + { + DBUG_RETURN(-1); + } + /* 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)) - { - 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); - } + 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 @@ 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 ed0f3987e01..b9de19c0927 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -140,6 +140,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 @@ -262,6 +288,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 1e7fce9001f..70960dbc25e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1087,6 +1087,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 2d1880a6d9d..dec24121fee 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -534,6 +534,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 4bbca55f6ba..4d926e62afe 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3694,7 +3694,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; @@ -4228,7 +4229,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); @@ -4364,9 +4366,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) { @@ -4435,9 +4439,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) { From e6b04edbb119c621781615df84d9ddd2f20ba897 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 23 Nov 2005 01:29:25 +0200 Subject: [PATCH 3/4] merge --- sql/sp.cc | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/sql/sp.cc b/sql/sp.cc index 861a41d8501..f98795c54a5 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -400,6 +400,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, const char *definer, longlong created, longlong modified) { LEX *oldlex= thd->lex, newlex; + sp_rcontext *save_spcont= ;thd->spcont; String defstr; char olddb[128]; bool dbchanged; @@ -430,6 +431,9 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); + (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); + (*sphp)->set_info(created, modified, &chistics, sql_mode); + thd->spcont= 0; if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; @@ -442,12 +446,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, else { if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) - goto end; + goto db_end; *sphp= newlex.sphead; + (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); (*sphp)->set_info((char *)definer, (uint)strlen(definer), created, modified, &chistics, sql_mode); (*sphp)->optimize(); } +db_end: + thd->spcont= save_spcont; thd->variables.sql_mode= old_sql_mode; thd->variables.select_limit= select_limit; end: @@ -550,12 +557,13 @@ 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 (!trust_routine_creators && mysql_bin_log.is_open()) + if ((sp->m_type == TYPE_ENUM_FUNCTION) && + !trust_function_creators && mysql_bin_log.is_open()) { if (!sp->m_chistics->detistic) { /* - Note that for a _function_ this test is not enough; one could use + Note that this test is not perfect; one could use a non-deterministic read-only function in an update statement. */ enum enum_sp_data_access access= @@ -1606,30 +1614,30 @@ create_string(THD *thd, String *buf, chistics->comment.length)) return FALSE; - buf->append("CREATE ", 7); + buf->append(STRING_WITH_LEN("CREATE ")); if (type == TYPE_ENUM_FUNCTION) - buf->append("FUNCTION ", 9); + buf->append(STRING_WITH_LEN("FUNCTION ")); else - buf->append("PROCEDURE ", 10); + buf->append(STRING_WITH_LEN("PROCEDURE ")); 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(" RETURNS ", 9); + buf->append(STRING_WITH_LEN(" RETURNS ")); buf->append(returns, returnslen); } buf->append('\n'); switch (chistics->daccess) { case SP_NO_SQL: - buf->append(" NO SQL\n"); + buf->append(STRING_WITH_LEN(" NO SQL\n")); break; case SP_READS_SQL_DATA: - buf->append(" READS SQL DATA\n"); + buf->append(STRING_WITH_LEN(" READS SQL DATA\n")); break; case SP_MODIFIES_SQL_DATA: - buf->append(" MODIFIES SQL DATA\n"); + buf->append(STRING_WITH_LEN(" MODIFIES SQL DATA\n")); break; case SP_DEFAULT_ACCESS: case SP_CONTAINS_SQL: @@ -1637,12 +1645,12 @@ create_string(THD *thd, String *buf, break; } if (chistics->detistic) - buf->append(" DETERMINISTIC\n", 18); + buf->append(STRING_WITH_LEN(" DETERMINISTIC\n")); if (chistics->suid == SP_IS_NOT_SUID) - buf->append(" SQL SECURITY INVOKER\n", 25); + buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n")); if (chistics->comment.length) { - buf->append(" COMMENT "); + buf->append(STRING_WITH_LEN(" COMMENT ")); append_unescaped(buf, chistics->comment.str, chistics->comment.length); buf->append('\n'); } From 83d692da888281576b7f7dd4fee1faa5be7eff68 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Wed, 23 Nov 2005 02:49:44 +0200 Subject: [PATCH 4/4] postmerge fix --- mysql-test/r/sp.result | 134 ++++++++++++++++++++--------------------- sql/item.cc | 1 - sql/sp.cc | 12 ++-- sql/sql_parse.cc | 6 +- sql/sql_trigger.cc | 1 + 5 files changed, 76 insertions(+), 78 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index b1216f94245..c3b4039d12d 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3417,6 +3417,9 @@ Table Create Table tm1 CREATE TEMPORARY TABLE `tm1` ( `spv1` decimal(6,3) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop procedure bug12589_1| +drop procedure bug12589_2| +drop procedure bug12589_3| drop table if exists t3| drop procedure if exists bug7049_1| drop procedure if exists bug7049_2| @@ -3617,6 +3620,70 @@ count(*) drop table t3, t4| drop procedure bug14210| set @@session.max_heap_table_size=default| +drop function if exists bug14723| +drop procedure if exists bug14723| +/*!50003 create function bug14723() +returns bigint(20) +main_loop: begin +return 42; +end */;; +show create function bug14723;; +Function sql_mode Create Function +bug14723 CREATE FUNCTION `bug14723`() RETURNS bigint(20) +main_loop: begin +return 42; +end +select bug14723();; +bug14723() +42 +/*!50003 create procedure bug14723() +main_loop: begin +select 42; +end */;; +show create procedure bug14723;; +Procedure sql_mode Create Procedure +bug14723 CREATE PROCEDURE `bug14723`() +main_loop: begin +select 42; +end +call bug14723();; +42 +42 +drop function bug14723| +drop procedure bug14723| +create procedure bug14845() +begin +declare a char(255); +declare done int default 0; +declare c cursor for select count(*) from t1 where 1 = 0; +declare continue handler for sqlstate '02000' set done = 1; +open c; +repeat +fetch c into a; +if not done then +select a; +end if; +until done end repeat; +close c; +end| +call bug14845()| +a +0 +drop procedure bug14845| +drop procedure if exists bug13549_1| +drop procedure if exists bug13549_2| +CREATE PROCEDURE `bug13549_2`() +begin +call bug13549_1(); +end| +CREATE PROCEDURE `bug13549_1`() +begin +declare done int default 0; +set done= not done; +end| +CALL bug13549_2()| +drop procedure bug13549_2| +drop procedure bug13549_1| drop function if exists bug10100f| drop procedure if exists bug10100p| drop procedure if exists bug10100t| @@ -3802,71 +3869,4 @@ drop procedure bug10100pd| drop procedure bug10100pc| drop view v1| drop table t3| -drop function if exists bug14723| -drop procedure if exists bug14723| -/*!50003 create function bug14723() -returns bigint(20) -main_loop: begin -return 42; -end */;; -show create function bug14723;; -Function sql_mode Create Function -bug14723 CREATE FUNCTION `bug14723`() RETURNS bigint(20) -main_loop: begin -return 42; -end -select bug14723();; -bug14723() -42 -/*!50003 create procedure bug14723() -main_loop: begin -select 42; -end */;; -show create procedure bug14723;; -Procedure sql_mode Create Procedure -bug14723 CREATE PROCEDURE `bug14723`() -main_loop: begin -select 42; -end -call bug14723();; -42 -42 -drop function bug14723| -drop procedure bug14723| -create procedure bug14845() -begin -declare a char(255); -declare done int default 0; -declare c cursor for select count(*) from t1 where 1 = 0; -declare continue handler for sqlstate '02000' set done = 1; -open c; -repeat -fetch c into a; -if not done then -select a; -end if; -until done end repeat; -close c; -end| -call bug14845()| -a -0 -drop procedure bug14845| -drop procedure bug12589_1| -drop procedure bug12589_2| -drop procedure bug12589_3| -drop procedure if exists bug13549_1| -drop procedure if exists bug13549_2| -CREATE PROCEDURE `bug13549_2`() -begin -call bug13549_1(); -end| -CREATE PROCEDURE `bug13549_1`() -begin -declare done int default 0; -set done= not done; -end| -CALL bug13549_2()| -drop procedure bug13549_2| -drop procedure bug13549_1| drop table t1,t2; diff --git a/sql/item.cc b/sql/item.cc index b7983b13766..6d5855cd0ca 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -921,7 +921,6 @@ Item_splocal::type() const DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset)->type(); } - } return NULL_ITEM; // Anything but SUBSELECT_ITEM } diff --git a/sql/sp.cc b/sql/sp.cc index f98795c54a5..8991cc78b5e 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -400,7 +400,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, const char *definer, longlong created, longlong modified) { LEX *oldlex= thd->lex, newlex; - sp_rcontext *save_spcont= ;thd->spcont; + sp_rcontext *save_spcont= thd->spcont; String defstr; char olddb[128]; bool dbchanged; @@ -431,8 +431,6 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); - (*sphp)->set_info(created, modified, &chistics, sql_mode); thd->spcont= 0; if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) { @@ -446,18 +444,16 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, else { if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) - goto db_end; + goto end; *sphp= newlex.sphead; (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); - (*sphp)->set_info((char *)definer, (uint)strlen(definer), - created, modified, &chistics, sql_mode); + (*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->optimize(); } -db_end: +end: thd->spcont= save_spcont; thd->variables.sql_mode= old_sql_mode; thd->variables.select_limit= select_limit; -end: thd->lex= oldlex; return ret; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 437e910d592..d06cceba77b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4566,9 +4566,11 @@ end_with_restore_list: goto error; } if (lex->sql_command == SQLCOM_SHOW_PROC_CODE) - 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); if (!sp || !sp->show_routine_code(thd)) { /* We don't distinguish between errors for now */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 224fa332d67..296b55679a3 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -913,6 +913,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->db= save_db.str; thd->db_length= save_db.length; thd->lex= old_lex; + thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; DBUG_RETURN(0);