diff options
-rw-r--r-- | mysql-test/r/sp.result | 17 | ||||
-rw-r--r-- | mysql-test/r/trigger.result | 14 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 28 | ||||
-rw-r--r-- | mysql-test/t/trigger.test | 28 | ||||
-rw-r--r-- | sql/item.cc | 7 | ||||
-rw-r--r-- | sql/item.h | 9 | ||||
-rw-r--r-- | sql/protocol.cc | 38 | ||||
-rw-r--r-- | sql/protocol.h | 2 | ||||
-rw-r--r-- | sql/sp.cc | 6 | ||||
-rw-r--r-- | sql/sp_head.cc | 9 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 8 | ||||
-rw-r--r-- | sql/sql_cache.h | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 8 | ||||
-rw-r--r-- | sql/sql_class.h | 7 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 3 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 20 |
16 files changed, 180 insertions, 26 deletions
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 93332af21a9..29a4fbaa439 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3667,4 +3667,21 @@ 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/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index af99dea58b9..9fc62ed123f 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -763,3 +763,17 @@ ERROR HY000: Table 't3' was not locked with LOCK TABLES deallocate prepare stmt1; drop procedure p1; drop table t1, t2, t3; +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 5ad2b9287aa..022721c7794 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4291,6 +4291,9 @@ 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#7049: Stored procedure CALL errors are ignored @@ -4595,6 +4598,31 @@ call bug14845()| drop procedure bug14845| # +# 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 # #--disable_warnings diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 02d994128e2..daa6ac2e6fa 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -914,3 +914,31 @@ call p1(); deallocate prepare stmt1; drop procedure p1; drop table t1, t2, t3; + +# +# 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 94dea1a7dd7..b7983b13766 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -894,6 +894,7 @@ bool Item_splocal::is_null() Item * Item_splocal::this_item() { + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset); } @@ -901,12 +902,14 @@ 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); } Item * Item_splocal::this_const_item() const { + DBUG_ASSERT(owner == thd->spcont->owner); return thd->spcont->get_item(m_offset); } @@ -914,7 +917,11 @@ Item::Type Item_splocal::type() const { if (thd && 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 3c03cd23e2c..4201790e907 100644 --- a/sql/item.h +++ b/sql/item.h @@ -704,6 +704,8 @@ public: }; +class sp_head; + /* A reference to local SP variable (incl. reference to SP parameter), used in runtime. @@ -721,6 +723,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; THD *thd; diff --git a/sql/protocol.cc b/sql/protocol.cc index a165bac6d7e..0a1b42f5236 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -498,7 +498,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 } @@ -551,7 +551,7 @@ bool Protocol::send_fields(List<Item> *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; @@ -648,7 +648,7 @@ bool Protocol::send_fields(List<Item> *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 } @@ -732,14 +732,14 @@ bool Protocol::store(I_List<i_string>* 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]; @@ -773,7 +773,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 || @@ -790,7 +790,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 || @@ -805,7 +805,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 @@ -817,7 +817,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); @@ -831,7 +831,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); @@ -845,7 +845,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++; @@ -860,7 +860,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++; @@ -874,7 +874,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++; @@ -886,7 +886,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++; @@ -900,7 +900,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]; @@ -921,7 +921,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); @@ -944,7 +944,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++; @@ -963,7 +963,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++; @@ -1088,7 +1088,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 7228b4bc4ab..6939f87b31a 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_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; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 5161ad0f64b..601d41a0afe 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1175,6 +1175,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(octx, 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); @@ -1319,6 +1322,9 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) { // Create a temporary old context if (!(octx= new sp_rcontext(octx, 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 */ @@ -1330,6 +1336,9 @@ int sp_head::execute_procedure(THD *thd, List<Item> *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 cae5c5467c9..d54188aa96f 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(sp_rcontext *prev, 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 c52addf3995..82af1265a9d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1495,7 +1495,13 @@ int select_dumpvar::prepare(List<Item> &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 ed6f5732ca8..e92bf3b2bec 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2085,6 +2085,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 ee597e6b7b8..224fa332d67 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -816,6 +816,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, List_iterator_fast<LEX_STRING> it_definer(triggers-> definers_list); LEX *old_lex= thd->lex, lex; + sp_rcontext *save_spcont= thd->spcont; ulong save_sql_mode= thd->variables.sql_mode; thd->lex= &lex; @@ -831,6 +832,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) { /* @@ -919,6 +921,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 8c7bcc31689..1c191cc2dc2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2286,8 +2286,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); @@ -5868,7 +5872,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 } } ; @@ -7171,6 +7181,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; |