From 502e97f875d448bcf0c0be4b2cd2c83733150427 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Aug 2005 23:23:34 +0000 Subject: [PATCH 1/3] Fix for BUG#12228: SP cache code: * Cleanup SP Cache code, now SP Cache only deletes sp_head objects in sp_cache_flush_obsolete() invalidates all pointers to routines in the cache. * Use new SP Cache use contract in the code. There is no test case because it doesn't seem to be possible to cause thread races to end the same way they end in heavy-load test. This patch removes the crash in heavy test. mysql-test/r/type_bit.result: Drop the tables this test tries to create mysql-test/r/view.result: Drop function this test creates mysql-test/t/type_bit.test: Drop the tables this test tries to create mysql-test/t/view.test: Drop function this test creates sql/sp.cc: Fix for BUG#12228: When a routine is deleted/modified, invalidate all cached SPs in all threads. We need to do so because sp_lex_keeper::{prelocking_tables, query_tables_own_last} in one SP may depend on another SP sp_lex_keeper::m_lex is using. sql/sp_cache.cc: Fix for BUG#12228: * Move class sp_cache to here from sp_cache.h, document the functions. * sp_cache_insert, sp_cache_remove, sp_cache_invalidate and sp_cache_lookup must not delete sp_head* objects as they may be called during SP execution when sp_head objects are used. * Added sp_cache_flush_obsolete() function that may delete sp_head objects. * Removed sp_cache_remove as there is no need for it now - when we change one SP we should invalidate all other SPs, because sp_lex_keeper::{prelocking_tables, query_tables_own_last} from one SP depend on content of another SP (used in sp_lex_keeper::m_lex). sql/sp_cache.h: Fix for BUG#12228: * Move class sp_cache to sp_cache.cc it is not needed in .h file * Added comments sql/sql_parse.cc: Fix for BUG#12228: Call new sp_cache_flush_obsolete() function before running the query sql/sql_prepare.cc: Fix for BUG#12228: Call new sp_cache_flush_obsolete() function before preparing/executing a PS --- mysql-test/r/type_bit.result | 2 +- mysql-test/r/view.result | 2 + mysql-test/t/type_bit.test | 2 +- mysql-test/t/view.test | 4 + sql/sp.cc | 16 +-- sql/sp_cache.cc | 211 +++++++++++++++++++++++++---------- sql/sp_cache.h | 105 +++++------------ sql/sql_parse.cc | 3 +- sql/sql_prepare.cc | 5 + 9 files changed, 196 insertions(+), 154 deletions(-) diff --git a/mysql-test/r/type_bit.result b/mysql-test/r/type_bit.result index 4aa8587d6e1..c22ceba70d8 100644 --- a/mysql-test/r/type_bit.result +++ b/mysql-test/r/type_bit.result @@ -34,7 +34,7 @@ select 0 + b'1111111111111111'; select 0 + b'1000000000000001'; 0 + b'1000000000000001' 32769 -drop table if exists t1; +drop table if exists t1,t2; create table t1 (a bit(65)); ERROR 42000: Column length too big for column 'a' (max = 64); use BLOB or TEXT instead create table t1 (a bit(0)); diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index f6b5018cf3a..98020c7ec33 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1880,6 +1880,8 @@ test.v5 check error View 'test.v5' references invalid table(s) or column(s) or f test.v6 check status OK drop view v1, v2, v3, v4, v5, v6; drop table t2; +drop function if exists f1; +drop function if exists f2; CREATE TABLE t1 (col1 time); CREATE TABLE t2 (col1 time); CREATE TABLE t3 (col1 time); diff --git a/mysql-test/t/type_bit.test b/mysql-test/t/type_bit.test index fd5eb49858c..a56b697ea6b 100644 --- a/mysql-test/t/type_bit.test +++ b/mysql-test/t/type_bit.test @@ -16,7 +16,7 @@ select 0 + b'1111111111111111'; select 0 + b'1000000000000001'; --disable_warnings -drop table if exists t1; +drop table if exists t1,t2; --enable_warnings --error 1074 diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 9885566442f..cba2d75fb7c 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1711,6 +1711,10 @@ CHECK TABLE v1, v2, v3, v4, v5, v6; drop view v1, v2, v3, v4, v5, v6; drop table t2; +--disable_warnings +drop function if exists f1; +drop function if exists f2; +--enable_warnings CREATE TABLE t1 (col1 time); CREATE TABLE t2 (col1 time); CREATE TABLE t3 (col1 time); diff --git a/sql/sp.cc b/sql/sp.cc index 297e17de689..e875c30ab1b 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -986,13 +986,11 @@ int sp_drop_procedure(THD *thd, sp_name *name) { int ret; - bool found; DBUG_ENTER("sp_drop_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - found= sp_cache_remove(&thd->sp_proc_cache, name); ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); - if (!found && !ret) + if (!ret) sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -1002,13 +1000,11 @@ int sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; - bool found; DBUG_ENTER("sp_update_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - found= sp_cache_remove(&thd->sp_proc_cache, name); ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, chistics); - if (!found && !ret) + if (!ret) sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -1099,13 +1095,11 @@ int sp_drop_function(THD *thd, sp_name *name) { int ret; - bool found; DBUG_ENTER("sp_drop_function"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - found= sp_cache_remove(&thd->sp_func_cache, name); ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); - if (!found && !ret) + if (!ret) sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -1115,13 +1109,11 @@ int sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; - bool found; DBUG_ENTER("sp_update_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - found= sp_cache_remove(&thd->sp_func_cache, name); ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, chistics); - if (!found && !ret) + if (!ret) sp_cache_invalidate(); DBUG_RETURN(ret); } diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index c8f0ed6ba2d..135000d1443 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -24,17 +24,78 @@ static pthread_mutex_t Cversion_lock; static ulong Cversion = 0; -void -sp_cache_init() + +/* + Cache of stored routines. +*/ + +class sp_cache +{ +public: + ulong version; + + sp_cache(); + ~sp_cache(); + + inline void insert(sp_head *sp) + { + /* TODO: why don't we check return value? */ + my_hash_insert(&m_hashtable, (const byte *)sp); + } + + inline sp_head *lookup(char *name, uint namelen) + { + return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen); + } + +#ifdef NOT_USED + inline bool remove(char *name, uint namelen) + { + sp_head *sp= lookup(name, namelen); + if (sp) + { + hash_delete(&m_hashtable, (byte *)sp); + return TRUE; + } + return FALSE; + } +#endif + + inline void remove_all() + { + cleanup(); + init(); + } + +private: + void init(); + void cleanup(); + + /* All routines in this cache */ + HASH m_hashtable; +}; // class sp_cache + + +/* Initialize the SP caching once at startup */ + +void sp_cache_init() { pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST); } -void -sp_cache_clear(sp_cache **cp) + +/* + Clear the cache *cp and set *cp to NULL. + SYNOPSIS + sp_cache_clear() + cp Pointer to cache to clear + NOTE + This function doesn't invalidate other caches. +*/ + +void sp_cache_clear(sp_cache **cp) { sp_cache *c= *cp; - if (c) { delete c; @@ -42,86 +103,119 @@ sp_cache_clear(sp_cache **cp) } } -void -sp_cache_insert(sp_cache **cp, sp_head *sp) + +/* + Insert a routine into the cache. + + SYNOPSIS + sp_cache_insert() + cp The cache to put routine into + sp Routine to insert. +*/ + +void sp_cache_insert(sp_cache **cp, sp_head *sp) { sp_cache *c= *cp; if (! c) - c= new sp_cache(); - if (c) { ulong v; - + c= new sp_cache(); pthread_mutex_lock(&Cversion_lock); // LOCK v= Cversion; pthread_mutex_unlock(&Cversion_lock); // UNLOCK - - if (c->version < v) - { - if (*cp) - c->remove_all(); - c->version= v; - } + c->version= v; + } + if (c) + { + DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, sp->m_qname.str)); c->insert(sp); if (*cp == NULL) *cp= c; } } -sp_head * -sp_cache_lookup(sp_cache **cp, sp_name *name) + +/* + Look up a routine in the cache. + SYNOPSIS + sp_cache_lookup() + cp Cache to look into + name Name of rutine to find + + NOTE + An obsolete (but not more obsolete then since last + sp_cache_flush_obsolete call) routine may be returned. + + RETURN + The routine or + NULL if the routine not found. +*/ + +sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name) { - ulong v; sp_cache *c= *cp; - - if (! c) + if (!c) return NULL; - - pthread_mutex_lock(&Cversion_lock); // LOCK - v= Cversion; - pthread_mutex_unlock(&Cversion_lock); // UNLOCK - - if (c->version < v) - { - c->remove_all(); - c->version= v; - return NULL; - } return c->lookup(name->m_qname.str, name->m_qname.length); } -bool -sp_cache_remove(sp_cache **cp, sp_name *name) -{ - sp_cache *c= *cp; - bool found= FALSE; - - if (c) - { - ulong v; - - pthread_mutex_lock(&Cversion_lock); // LOCK - v= Cversion++; - pthread_mutex_unlock(&Cversion_lock); // UNLOCK - - if (c->version < v) - c->remove_all(); - else - found= c->remove(name->m_qname.str, name->m_qname.length); - c->version= v+1; - } - return found; -} - -void -sp_cache_invalidate() + +/* + Invalidate all routines in all caches. + + SYNOPSIS + sp_cache_invalidate() + + NOTE + This is called when a VIEW definition is modifed. We can't destroy sp_head + objects here as one may modify VIEW definitions from prelocking-free SPs. +*/ + +void sp_cache_invalidate() { + DBUG_PRINT("info",("sp_cache: invalidating")); pthread_mutex_lock(&Cversion_lock); // LOCK Cversion++; pthread_mutex_unlock(&Cversion_lock); // UNLOCK } + +/* + Remove out-of-date SPs from the cache. + + SYNOPSIS + sp_cache_flush_obsolete() + cp Cache to flush + + NOTE + This invalidates pointers to sp_head objects this thread uses. + In practice that means 'dont call this function when inside SP'. +*/ + +void sp_cache_flush_obsolete(sp_cache **cp) +{ + sp_cache *c= *cp; + if (c) + { + ulong v; + pthread_mutex_lock(&Cversion_lock); // LOCK + v= Cversion; + pthread_mutex_unlock(&Cversion_lock); // UNLOCK + if (c->version < v) + { + DBUG_PRINT("info",("sp_cache: deleting all functions")); + /* We need to delete all elements. */ + c->remove_all(); + c->version= v; + } + } +} + +/************************************************************************* + Internal functions + *************************************************************************/ + static byte * hash_get_key_for_sp_head(const byte *ptr, uint *plen, my_bool first) @@ -136,7 +230,6 @@ static void hash_free_sp_head(void *p) { sp_head *sp= (sp_head *)p; - delete sp; } diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 14b2db97f5f..402647db9ea 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -25,94 +25,39 @@ /* Stored procedures/functions cache. This is used as follows: * Each thread has its own cache. - * Each sp_head object is put into its thread cache before it is used, and + * Each sp_head object is put into its thread cache before it is used, and then remains in the cache until deleted. */ class sp_head; class sp_cache; -/* Initialize the SP caching once at startup */ -void sp_cache_init(); - -/* Clear the cache *cp and set *cp to NULL */ -void sp_cache_clear(sp_cache **cp); - -/* Insert an SP into cache. If 'cp' points to NULL, it's set to a new cache */ -void sp_cache_insert(sp_cache **cp, sp_head *sp); - -/* Lookup an SP in cache */ -sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); - -/* - Remove an SP from cache, and also bump the Cversion number so all other - caches are invalidated. - Returns true if something was removed. -*/ -bool sp_cache_remove(sp_cache **cp, sp_name *name); - -/* Invalidate all existing SP caches by bumping Cversion number. */ -void sp_cache_invalidate(); - - /* - * - * The cache class. Don't use this directly, use the C API above - * - */ + Cache usage scenarios: + 1. Application-wide init: + sp_cache_init(); -class sp_cache -{ -public: + 2. SP execution in thread: + 2.1 While holding sp_head* pointers: + + // look up a routine in the cache (no checks if it is up to date or not) + sp_cache_lookup(); + + sp_cache_insert(); + sp_cache_invalidate(); + + 2.2 When not holding any sp_head* pointers (at query end): + sp_cache_flush_obsolete(); + + 3. Before thread exit: + sp_cache_clear(); +*/ - ulong version; - - sp_cache(); - - ~sp_cache(); - - void - init(); - - void - cleanup(); - - inline void - insert(sp_head *sp) - { - my_hash_insert(&m_hashtable, (const byte *)sp); - } - - inline sp_head * - lookup(char *name, uint namelen) - { - return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen); - } - - inline bool - remove(char *name, uint namelen) - { - sp_head *sp= lookup(name, namelen); - - if (sp) - { - hash_delete(&m_hashtable, (byte *)sp); - return TRUE; - } - return FALSE; - } - - inline void - remove_all() - { - cleanup(); - init(); - } - -private: - - HASH m_hashtable; - -}; // class sp_cache +void sp_cache_init(); +void sp_cache_clear(sp_cache **cp); +void sp_cache_insert(sp_cache **cp, sp_head *sp); +sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); +void sp_cache_invalidate(); +void sp_cache_flush_obsolete(sp_cache **cp); #endif /* _SP_CACHE_H_ */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 677b3a98174..00463dc0a3b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5347,7 +5347,8 @@ void mysql_init_multi_delete(LEX *lex) void mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); - + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); mysql_init_query(thd, (uchar*) inBuf, length); if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 18707cc6c87..f8b19dac3d1 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -73,6 +73,7 @@ Long data handling: #include // for isspace() #include "sp_head.h" #include "sp.h" +#include "sp_cache.h" #ifdef EMBEDDED_LIBRARY /* include MYSQL_BIND headers */ #include @@ -1729,6 +1730,8 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, DBUG_ENTER("mysql_stmt_prepare"); DBUG_PRINT("prep_query", ("%s", packet)); + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); /* If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. @@ -1978,6 +1981,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_execute"); + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); packet+= 9; /* stmt_id + 5 bytes of flags */ statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); From 08cadd816e7fd92f6f56c3ba621aa81d47954fca Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 9 Aug 2005 10:09:11 +0000 Subject: [PATCH 2/3] BUG#12228: Pre-review fixes: Fix coding style, handle the case when we're out of memory. --- sql/sp_cache.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 135000d1443..1763432f2bc 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -111,6 +111,11 @@ void sp_cache_clear(sp_cache **cp) sp_cache_insert() cp The cache to put routine into sp Routine to insert. + + TODO: Perhaps it will be more straightforward if in case we returned an + error from this function when we couldn't allocate sp_cache. (right + now failure to put routine into cache will cause a 'SP not found' + error to be reported at some later time) */ void sp_cache_insert(sp_cache **cp, sp_head *sp) @@ -124,11 +129,13 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) pthread_mutex_lock(&Cversion_lock); // LOCK v= Cversion; pthread_mutex_unlock(&Cversion_lock); // UNLOCK - c->version= v; + if (c) + c->version= v; } if (c) { - DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, sp->m_qname.str)); + DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, + sp->m_qname.str)); c->insert(sp); if (*cp == NULL) *cp= c; From 6b9ec78be00d37b91d4de8564bc0103d83ddfb26 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 Aug 2005 21:17:02 +0000 Subject: [PATCH 3/3] BUG#12228: Post review fixes: Added test case, code cleanup. mysql-test/r/sp-threads.result: Testcase for BUG#12228 mysql-test/t/sp-threads.test: Testcase for BUG#12228 sql/sp_cache.cc: BUG#12228: Post-review fixes: small code cleanup sql/sp_cache.h: BUG#12228: Post-review fixes: fixed the comment sql/sql_parse.cc: BUG#12228: Post-review fixes: in mysql_parse, flush obsolete SPs from the caches only if the query hasn't been handled by the query cache. sql/sql_prepare.cc: BUG#12228: Post-review fixes: in mysql_stmt_prepare/execute, flush SP caches "closer to the execution" --- mysql-test/r/sp-threads.result | 25 ++++++++++++++++++++ mysql-test/t/sp-threads.test | 43 ++++++++++++++++++++++++++++++++++ sql/sp_cache.cc | 10 +++----- sql/sp_cache.h | 2 +- sql/sql_parse.cc | 4 ++-- sql/sql_prepare.cc | 9 +++---- 6 files changed, 79 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index 2f7e8021aa7..c516d7a643f 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -37,6 +37,7 @@ Id User Host db Command Time State Info # root localhost test Sleep # NULL # root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist +# root localhost test Sleep # NULL unlock tables; drop procedure bug9486; drop table t1, t2; @@ -64,3 +65,27 @@ insert into t1 (select f from v1); drop function bug11554; drop table t1; drop view v1; +drop procedure if exists p1; +drop procedure if exists p2; +create table t1 (s1 int)| +create procedure p1() select * from t1| +create procedure p2() +begin +insert into t1 values (1); +call p1(); +select * from t1; +end| +use test; +lock table t1 write; + call p2(); +use test; +drop procedure p1; +create procedure p1() select * from t1; +unlock tables; +s1 +1 +s1 +1 +drop procedure p1; +drop procedure p2; +drop table t1; diff --git a/mysql-test/t/sp-threads.test b/mysql-test/t/sp-threads.test index 4c192f3e96f..5e51c1034e0 100644 --- a/mysql-test/t/sp-threads.test +++ b/mysql-test/t/sp-threads.test @@ -5,6 +5,7 @@ connect (con1root,localhost,root,,); connect (con2root,localhost,root,,); +connect (con3root,localhost,root,,); connection con1root; use test; @@ -130,6 +131,48 @@ drop function bug11554; drop table t1; drop view v1; + +# BUG#12228 +--disable_warnings +drop procedure if exists p1; +drop procedure if exists p2; +--enable_warnings + +connection con1root; +delimiter |; +create table t1 (s1 int)| +create procedure p1() select * from t1| +create procedure p2() +begin + insert into t1 values (1); + call p1(); + select * from t1; +end| +delimiter ;| + +connection con2root; +use test; +lock table t1 write; + +connection con1root; +send call p2(); + +connection con3root; +use test; +drop procedure p1; +create procedure p1() select * from t1; + +connection con2root; +unlock tables; + +connection con1root; +# Crash will be here if we hit BUG#12228 +reap; + +drop procedure p1; +drop procedure p2; +drop table t1; + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 1763432f2bc..68e8dbb3252 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -122,19 +122,15 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) { sp_cache *c= *cp; - if (! c) + if (!c && (c= new sp_cache())) { - ulong v; - c= new sp_cache(); pthread_mutex_lock(&Cversion_lock); // LOCK - v= Cversion; + c->version= Cversion; pthread_mutex_unlock(&Cversion_lock); // UNLOCK - if (c) - c->version= v; } if (c) { - DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, + DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, sp->m_qname.str)); c->insert(sp); if (*cp == NULL) diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 402647db9ea..1021d17b9e2 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -46,7 +46,7 @@ class sp_cache; sp_cache_insert(); sp_cache_invalidate(); - 2.2 When not holding any sp_head* pointers (at query end): + 2.2 When not holding any sp_head* pointers: sp_cache_flush_obsolete(); 3. Before thread exit: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 00463dc0a3b..1a3f6f6f656 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5347,12 +5347,12 @@ void mysql_init_multi_delete(LEX *lex) void mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); mysql_init_query(thd, (uchar*) inBuf, length); if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { LEX *lex= thd->lex; + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); if (!yyparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f8b19dac3d1..4eb8c00ae16 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1730,8 +1730,6 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, DBUG_ENTER("mysql_stmt_prepare"); DBUG_PRINT("prep_query", ("%s", packet)); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); /* If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. @@ -1785,6 +1783,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, lex= thd->lex; lex->safe_to_cache_query= 0; + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); + error= yyparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(stmt); /* @@ -1981,8 +1982,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_execute"); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); packet+= 9; /* stmt_id + 5 bytes of flags */ statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); @@ -2063,6 +2062,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) thd->protocol= &thd->protocol_prep; // Switch to binary protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); mysql_execute_command(thd); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR);