2424#include "nodes/nodeFuncs.h"
2525#include "parser/parse_coerce.h"
2626#include "parser/parse_func.h"
27+ #include "storage/proc.h"
2728#include "tcop/utility.h"
2829#include "utils/builtins.h"
2930#include "utils/datum.h"
3031#include "utils/lsyscache.h"
32+ #include "utils/memutils.h"
3133#include "utils/snapmgr.h"
3234#include "utils/syscache.h"
3335
@@ -73,8 +75,17 @@ typedef struct execution_state
7375 * and linked to from the fn_extra field of the FmgrInfo struct.
7476 *
7577 * Note that currently this has only the lifespan of the calling query.
76- * Someday we might want to consider caching the parse/plan results longer
77- * than that.
78+ * Someday we should rewrite this code to use plancache.c to save parse/plan
79+ * results for longer than that.
80+ *
81+ * Physically, though, the data has the lifespan of the FmgrInfo that's used
82+ * to call the function, and there are cases (particularly with indexes)
83+ * where the FmgrInfo might survive across transactions. We cannot assume
84+ * that the parse/plan trees are good for longer than the (sub)transaction in
85+ * which parsing was done, so we must mark the record with the LXID/subxid of
86+ * its creation time, and regenerate everything if that's obsolete. To avoid
87+ * memory leakage when we do have to regenerate things, all the data is kept
88+ * in a sub-context of the FmgrInfo's fn_mcxt.
7889 */
7990typedef struct
8091{
@@ -105,6 +116,12 @@ typedef struct
105116 * track of where the original query boundaries are.
106117 */
107118List * func_state ;
119+
120+ MemoryContext fcontext ;/* memory context holding this struct and all
121+ * subsidiary data */
122+
123+ LocalTransactionId lxid ;/* lxid in which cache was made */
124+ SubTransactionId subxid ;/* subxid in which cache was made */
108125}SQLFunctionCache ;
109126
110127typedef SQLFunctionCache * SQLFunctionCachePtr ;
@@ -550,6 +567,8 @@ static void
550567init_sql_fcache (FmgrInfo * finfo ,Oid collation ,bool lazyEvalOK )
551568{
552569Oid foid = finfo -> fn_oid ;
570+ MemoryContext fcontext ;
571+ MemoryContext oldcontext ;
553572Oid rettype ;
554573HeapTuple procedureTuple ;
555574Form_pg_proc procedureStruct ;
@@ -561,7 +580,25 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
561580Datum tmp ;
562581bool isNull ;
563582
583+ /*
584+ * Create memory context that holds all the SQLFunctionCache data.It
585+ * must be a child of whatever context holds the FmgrInfo.
586+ */
587+ fcontext = AllocSetContextCreate (finfo -> fn_mcxt ,
588+ "SQL function data" ,
589+ ALLOCSET_DEFAULT_MINSIZE ,
590+ ALLOCSET_DEFAULT_INITSIZE ,
591+ ALLOCSET_DEFAULT_MAXSIZE );
592+
593+ oldcontext = MemoryContextSwitchTo (fcontext );
594+
595+ /*
596+ * Create the struct proper, link it to fcontext and fn_extra.Once this
597+ * is done, we'll be able to recover the memory after failure, even if the
598+ * FmgrInfo is long-lived.
599+ */
564600fcache = (SQLFunctionCachePtr )palloc0 (sizeof (SQLFunctionCache ));
601+ fcache -> fcontext = fcontext ;
565602finfo -> fn_extra = (void * )fcache ;
566603
567604/*
@@ -634,6 +671,11 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
634671 * sublist, for example if the last query rewrites to DO INSTEAD NOTHING.
635672 * (It might not be unreasonable to throw an error in such a case, but
636673 * this is the historical behavior and it doesn't seem worth changing.)
674+ *
675+ * Note: since parsing and planning is done in fcontext, we will generate
676+ * a lot of cruft that lives as long as the fcache does. This is annoying
677+ * but we'll not worry about it until the module is rewritten to use
678+ * plancache.c.
637679 */
638680raw_parsetree_list = pg_parse_query (fcache -> src );
639681
@@ -699,7 +741,13 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
699741fcache ,
700742lazyEvalOK );
701743
744+ /* Mark fcache with time of creation to show it's valid */
745+ fcache -> lxid = MyProc -> lxid ;
746+ fcache -> subxid = GetCurrentSubTransactionId ();
747+
702748ReleaseSysCache (procedureTuple );
749+
750+ MemoryContextSwitchTo (oldcontext );
703751}
704752
705753/* Start up execution of one execution_state node */
@@ -922,9 +970,9 @@ postquel_get_single_result(TupleTableSlot *slot,
922970Datum
923971fmgr_sql (PG_FUNCTION_ARGS )
924972{
925- MemoryContext oldcontext ;
926973SQLFunctionCachePtr fcache ;
927974ErrorContextCallback sqlerrcontext ;
975+ MemoryContext oldcontext ;
928976bool randomAccess ;
929977bool lazyEvalOK ;
930978bool is_first ;
@@ -935,13 +983,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
935983List * eslist ;
936984ListCell * eslc ;
937985
938- /*
939- * Switch to context in which the fcache lives. This ensures that
940- * parsetrees, plans, etc, will have sufficient lifetime. The
941- * sub-executor is responsible for deleting per-tuple information.
942- */
943- oldcontext = MemoryContextSwitchTo (fcinfo -> flinfo -> fn_mcxt );
944-
945986/*
946987 * Setup error traceback support for ereport()
947988 */
@@ -977,20 +1018,43 @@ fmgr_sql(PG_FUNCTION_ARGS)
9771018}
9781019
9791020/*
980- * Initialize fcache (build plans) if first time through.
1021+ * Initialize fcache (build plans) if first time through; or re-initialize
1022+ * if the cache is stale.
9811023 */
9821024fcache = (SQLFunctionCachePtr )fcinfo -> flinfo -> fn_extra ;
1025+
1026+ if (fcache != NULL )
1027+ {
1028+ if (fcache -> lxid != MyProc -> lxid ||
1029+ !SubTransactionIsActive (fcache -> subxid ))
1030+ {
1031+ /* It's stale; unlink and delete */
1032+ fcinfo -> flinfo -> fn_extra = NULL ;
1033+ MemoryContextDelete (fcache -> fcontext );
1034+ fcache = NULL ;
1035+ }
1036+ }
1037+
9831038if (fcache == NULL )
9841039{
9851040init_sql_fcache (fcinfo -> flinfo ,PG_GET_COLLATION (),lazyEvalOK );
9861041fcache = (SQLFunctionCachePtr )fcinfo -> flinfo -> fn_extra ;
9871042}
988- eslist = fcache -> func_state ;
1043+
1044+ /*
1045+ * Switch to context in which the fcache lives. This ensures that our
1046+ * tuplestore etc will have sufficient lifetime. The sub-executor is
1047+ * responsible for deleting per-tuple information.(XXX in the case of a
1048+ * long-lived FmgrInfo, this policy represents more memory leakage, but
1049+ * it's not entirely clear where to keep stuff instead.)
1050+ */
1051+ oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
9891052
9901053/*
9911054 * Find first unfinished query in function, and note whether it's the
9921055 * first query.
9931056 */
1057+ eslist = fcache -> func_state ;
9941058es = NULL ;
9951059is_first = true;
9961060foreach (eslc ,eslist )