summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/sp.result17
-rw-r--r--mysql-test/r/trigger.result14
-rw-r--r--mysql-test/t/sp.test28
-rw-r--r--mysql-test/t/trigger.test28
-rw-r--r--sql/item.cc7
-rw-r--r--sql/item.h9
-rw-r--r--sql/protocol.cc38
-rw-r--r--sql/protocol.h2
-rw-r--r--sql/sp.cc6
-rw-r--r--sql/sp_head.cc9
-rw-r--r--sql/sp_rcontext.h8
-rw-r--r--sql/sql_cache.h2
-rw-r--r--sql/sql_class.cc8
-rw-r--r--sql/sql_class.h7
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_yacc.yy20
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;