24
24
#include "nodes/nodeFuncs.h"
25
25
#include "parser/parse_coerce.h"
26
26
#include "parser/parse_func.h"
27
+ #include "storage/proc.h"
27
28
#include "tcop/utility.h"
28
29
#include "utils/builtins.h"
29
30
#include "utils/datum.h"
30
31
#include "utils/lsyscache.h"
32
+ #include "utils/memutils.h"
31
33
#include "utils/snapmgr.h"
32
34
#include "utils/syscache.h"
33
35
@@ -73,8 +75,17 @@ typedef struct execution_state
73
75
* and linked to from the fn_extra field of the FmgrInfo struct.
74
76
*
75
77
* 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.
78
89
*/
79
90
typedef struct
80
91
{
@@ -105,6 +116,12 @@ typedef struct
105
116
* track of where the original query boundaries are.
106
117
*/
107
118
List * 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 */
108
125
} SQLFunctionCache ;
109
126
110
127
typedef SQLFunctionCache * SQLFunctionCachePtr ;
@@ -550,6 +567,8 @@ static void
550
567
init_sql_fcache (FmgrInfo * finfo , Oid collation , bool lazyEvalOK )
551
568
{
552
569
Oid foid = finfo -> fn_oid ;
570
+ MemoryContext fcontext ;
571
+ MemoryContext oldcontext ;
553
572
Oid rettype ;
554
573
HeapTuple procedureTuple ;
555
574
Form_pg_proc procedureStruct ;
@@ -561,7 +580,25 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
561
580
Datum tmp ;
562
581
bool isNull ;
563
582
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
+ */
564
600
fcache = (SQLFunctionCachePtr ) palloc0 (sizeof (SQLFunctionCache ));
601
+ fcache -> fcontext = fcontext ;
565
602
finfo -> fn_extra = (void * ) fcache ;
566
603
567
604
/*
@@ -634,6 +671,11 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
634
671
* sublist, for example if the last query rewrites to DO INSTEAD NOTHING.
635
672
* (It might not be unreasonable to throw an error in such a case, but
636
673
* 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.
637
679
*/
638
680
raw_parsetree_list = pg_parse_query (fcache -> src );
639
681
@@ -699,7 +741,13 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
699
741
fcache ,
700
742
lazyEvalOK );
701
743
744
+ /* Mark fcache with time of creation to show it's valid */
745
+ fcache -> lxid = MyProc -> lxid ;
746
+ fcache -> subxid = GetCurrentSubTransactionId ();
747
+
702
748
ReleaseSysCache (procedureTuple );
749
+
750
+ MemoryContextSwitchTo (oldcontext );
703
751
}
704
752
705
753
/* Start up execution of one execution_state node */
@@ -922,9 +970,9 @@ postquel_get_single_result(TupleTableSlot *slot,
922
970
Datum
923
971
fmgr_sql (PG_FUNCTION_ARGS )
924
972
{
925
- MemoryContext oldcontext ;
926
973
SQLFunctionCachePtr fcache ;
927
974
ErrorContextCallback sqlerrcontext ;
975
+ MemoryContext oldcontext ;
928
976
bool randomAccess ;
929
977
bool lazyEvalOK ;
930
978
bool is_first ;
@@ -935,13 +983,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
935
983
List * eslist ;
936
984
ListCell * eslc ;
937
985
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
-
945
986
/*
946
987
* Setup error traceback support for ereport()
947
988
*/
@@ -977,20 +1018,43 @@ fmgr_sql(PG_FUNCTION_ARGS)
977
1018
}
978
1019
979
1020
/*
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.
981
1023
*/
982
1024
fcache = (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
+
983
1038
if (fcache == NULL )
984
1039
{
985
1040
init_sql_fcache (fcinfo -> flinfo , PG_GET_COLLATION (), lazyEvalOK );
986
1041
fcache = (SQLFunctionCachePtr ) fcinfo -> flinfo -> fn_extra ;
987
1042
}
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 );
989
1052
990
1053
/*
991
1054
* Find first unfinished query in function, and note whether it's the
992
1055
* first query.
993
1056
*/
1057
+ eslist = fcache -> func_state ;
994
1058
es = NULL ;
995
1059
is_first = true;
996
1060
foreach (eslc , eslist )
0 commit comments