88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.109 2007/01/05 22:20:02 momjian Exp $
11+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.110 2007/01/30 22:05:12 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -93,6 +93,7 @@ static const ExceptionLabelMap exception_label_map[] = {
9393 */
9494static PLpgSQL_function * do_compile (FunctionCallInfo fcinfo ,
9595HeapTuple procTup ,
96+ PLpgSQL_function * function ,
9697PLpgSQL_func_hashkey * hashkey ,
9798bool forValidator );
9899static PLpgSQL_row * build_row_from_class (Oid classOid );
@@ -130,6 +131,7 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
130131Form_pg_proc procStruct ;
131132PLpgSQL_function * function ;
132133PLpgSQL_func_hashkey hashkey ;
134+ bool function_valid = false;
133135bool hashkey_valid = false;
134136
135137/*
@@ -148,6 +150,7 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
148150 */
149151function = (PLpgSQL_function * )fcinfo -> flinfo -> fn_extra ;
150152
153+ recheck :
151154if (!function )
152155{
153156/* Compute hashkey using function signature and actual arg types */
@@ -161,19 +164,43 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
161164if (function )
162165{
163166/* We have a compiled function, but is it still valid? */
164- if (!(function -> fn_xmin == HeapTupleHeaderGetXmin (procTup -> t_data )&&
165- function -> fn_cmin == HeapTupleHeaderGetCmin (procTup -> t_data )))
167+ if (function -> fn_xmin == HeapTupleHeaderGetXmin (procTup -> t_data )&&
168+ function -> fn_cmin == HeapTupleHeaderGetCmin (procTup -> t_data ))
169+ function_valid = true;
170+ else
166171{
167- /* Nope, drop the function and associated storage */
172+ /*
173+ * Nope, so remove it from hashtable and try to drop associated
174+ * storage (if not done already).
175+ */
168176delete_function (function );
169- function = NULL ;
177+ /*
178+ * If the function isn't in active use then we can overwrite the
179+ * func struct with new data, allowing any other existing fn_extra
180+ * pointers to make use of the new definition on their next use.
181+ * If it is in use then just leave it alone and make a new one.
182+ * (The active invocations will run to completion using the
183+ * previous definition, and then the cache entry will just be
184+ * leaked; doesn't seem worth adding code to clean it up, given
185+ * what a corner case this is.)
186+ *
187+ * If we found the function struct via fn_extra then it's possible
188+ * a replacement has already been made, so go back and recheck
189+ * the hashtable.
190+ */
191+ if (function -> use_count != 0 )
192+ {
193+ function = NULL ;
194+ if (!hashkey_valid )
195+ gotorecheck ;
196+ }
170197}
171198}
172199
173200/*
174201 * If the function wasn't found or was out-of-date, we have to compile it
175202 */
176- if (!function )
203+ if (!function_valid )
177204{
178205/*
179206 * Calculate hashkey if we didn't already; we'll need it to store the
@@ -186,7 +213,8 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
186213/*
187214 * Do the hard part.
188215 */
189- function = do_compile (fcinfo ,procTup ,& hashkey ,forValidator );
216+ function = do_compile (fcinfo ,procTup ,function ,
217+ & hashkey ,forValidator );
190218}
191219
192220ReleaseSysCache (procTup );
@@ -205,6 +233,9 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
205233/*
206234 * This is the slow part of plpgsql_compile().
207235 *
236+ * The passed-in "function" pointer is either NULL or an already-allocated
237+ * function struct to overwrite.
238+ *
208239 * While compiling a function, the CurrentMemoryContext is the
209240 * per-function memory context of the function we are compiling. That
210241 * means a palloc() will allocate storage with the same lifetime as
@@ -217,16 +248,19 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
217248 * switch into a short-term context before calling into the
218249 * backend. An appropriate context for performing short-term
219250 * allocations is the compile_tmp_cxt.
251+ *
252+ * NB: this code is not re-entrant. We assume that nothing we do here could
253+ * result in the invocation of another plpgsql function.
220254 */
221255static PLpgSQL_function *
222256do_compile (FunctionCallInfo fcinfo ,
223257HeapTuple procTup ,
258+ PLpgSQL_function * function ,
224259PLpgSQL_func_hashkey * hashkey ,
225260bool forValidator )
226261{
227262Form_pg_proc procStruct = (Form_pg_proc )GETSTRUCT (procTup );
228263int functype = CALLED_AS_TRIGGER (fcinfo ) ?T_TRIGGER :T_FUNCTION ;
229- PLpgSQL_function * function ;
230264Datum prosrcdatum ;
231265bool isnull ;
232266char * proc_source ;
@@ -292,18 +326,31 @@ do_compile(FunctionCallInfo fcinfo,
292326plpgsql_check_syntax = forValidator ;
293327
294328/*
295- * Create the new function node. We allocate the function and all of its
296- * compile-time storage (e.g. parse tree) in its own memory context. This
297- * allows us to reclaim the function's storage cleanly.
329+ * Create the new function struct, if not done already. The function
330+ * structs are never thrown away, so keep them in TopMemoryContext.
331+ */
332+ if (function == NULL )
333+ {
334+ function = (PLpgSQL_function * )
335+ MemoryContextAllocZero (TopMemoryContext ,sizeof (PLpgSQL_function ));
336+ }
337+ else
338+ {
339+ /* re-using a previously existing struct, so clear it out */
340+ memset (function ,0 ,sizeof (PLpgSQL_function ));
341+ }
342+ plpgsql_curr_compile = function ;
343+
344+ /*
345+ * All the rest of the compile-time storage (e.g. parse tree) is kept in
346+ * its own memory context, so it can be reclaimed easily.
298347 */
299348func_cxt = AllocSetContextCreate (TopMemoryContext ,
300349"PL/PgSQL function context" ,
301350ALLOCSET_DEFAULT_MINSIZE ,
302351ALLOCSET_DEFAULT_INITSIZE ,
303352ALLOCSET_DEFAULT_MAXSIZE );
304353compile_tmp_cxt = MemoryContextSwitchTo (func_cxt );
305- function = palloc0 (sizeof (* function ));
306- plpgsql_curr_compile = function ;
307354
308355function -> fn_name = pstrdup (NameStr (procStruct -> proname ));
309356function -> fn_oid = fcinfo -> flinfo -> fn_oid ;
@@ -1990,19 +2037,32 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
19902037}
19912038}
19922039
2040+ /*
2041+ * delete_function - clean up as much as possible of a stale function cache
2042+ *
2043+ * We can't release the PLpgSQL_function struct itself, because of the
2044+ * possibility that there are fn_extra pointers to it. We can release
2045+ * the subsidiary storage, but only if there are no active evaluations
2046+ * in progress. Otherwise we'll just leak that storage. Since the
2047+ * case would only occur if a pg_proc update is detected during a nested
2048+ * recursive call on the function, a leak seems acceptable.
2049+ *
2050+ * Note that this can be called more than once if there are multiple fn_extra
2051+ * pointers to the same function cache. Hence be careful not to do things
2052+ * twice.
2053+ */
19932054static void
19942055delete_function (PLpgSQL_function * func )
19952056{
1996- /* remove function from hash table */
2057+ /* remove function from hash table(might be done already) */
19972058plpgsql_HashTableDelete (func );
19982059
1999- /* release the function's storage */
2000- MemoryContextDelete (func -> fn_cxt );
2001-
2002- /*
2003- * Caller should be sure not to use passed-in pointer, as it now points to
2004- * pfree'd storage
2005- */
2060+ /* release the function's storage if safe and not done already */
2061+ if (func -> use_count == 0 && func -> fn_cxt )
2062+ {
2063+ MemoryContextDelete (func -> fn_cxt );
2064+ func -> fn_cxt = NULL ;
2065+ }
20062066}
20072067
20082068/* exported so we can call it from plpgsql_init() */
@@ -2063,10 +2123,17 @@ plpgsql_HashTableDelete(PLpgSQL_function *function)
20632123{
20642124plpgsql_HashEnt * hentry ;
20652125
2126+ /* do nothing if not in table */
2127+ if (function -> fn_hashkey == NULL )
2128+ return ;
2129+
20662130hentry = (plpgsql_HashEnt * )hash_search (plpgsql_HashTable ,
20672131 (void * )function -> fn_hashkey ,
20682132HASH_REMOVE ,
20692133NULL );
20702134if (hentry == NULL )
20712135elog (WARNING ,"trying to delete function that does not exist" );
2136+
2137+ /* remove back link, which no longer points to allocated storage */
2138+ function -> fn_hashkey = NULL ;
20722139}