@@ -57,6 +57,7 @@ static Variable *createVariableInternal(Package *package,
5757 text * name , Oid typid ,
5858 bool is_transactional );
5959static void removePackageInternal (Package * package );
60+ static void resetVariablesCache (bool with_package );
6061
6162/* Functions to work with transactional objects */
6263static void createSavepoint (TransObject * object , TransObjectType type );
@@ -67,7 +68,6 @@ static void copyValue(VarState *src, VarState *dest, Variable *destVar);
6768static void freeValue (VarState * varstate , Oid typid );
6869static void removeState (TransObject * object , TransObjectType type ,
6970 TransState * stateToDelete );
70- static void removeObject (TransObject * object , TransObjectType type );
7171static bool isObjectChangedInCurrentTrans (TransObject * object );
7272static bool isObjectChangedInUpperTrans (TransObject * object );
7373
@@ -80,6 +80,9 @@ static void makePackHTAB(Package *package, bool is_trans);
8080static inline ChangedObject * makeChangedObject (TransObject * object ,
8181 MemoryContext ctx );
8282
83+ /* Hook functions */
84+ static void variable_ExecutorEnd (QueryDesc * queryDesc );
85+
8386#define CHECK_ARGS_FOR_NULL () \
8487do { \
8588 if (fcinfo->argnull[0]) \
@@ -99,7 +102,19 @@ static MemoryContext ModuleContext = NULL;
99102static Package * LastPackage = NULL ;
100103/* Recent variable */
101104static Variable * LastVariable = NULL ;
105+ /* Recent row type id */
106+ static Oid LastTypeId = InvalidOid ;
107+
108+ /*
109+ * Cache sequentially search through hash table status. It is necessary for
110+ * clean up if hash_seq_term() wasn't called or if we didn't scan the whole
111+ * table. In this case we need to manually call hash_seq_term() within
112+ * variable_ExecutorEnd().
113+ */
114+ static HASH_SEQ_STATUS * LastHSeqStatus = NULL ;
102115
116+ /* Saved hook values for recall */
117+ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL ;
103118
104119/* This stack contains lists of changed variables and packages per each subxact level */
105120static dlist_head * changesStack = NULL ;
@@ -291,7 +306,7 @@ variable_insert(PG_FUNCTION_ARGS)
291306
292307 Oid tupType ;
293308 int32 tupTypmod ;
294- TupleDesc tupdesc ;
309+ TupleDesc tupdesc = NULL ;
295310 RecordVar * record ;
296311
297312 /* Checks */
@@ -360,23 +375,34 @@ variable_insert(PG_FUNCTION_ARGS)
360375 /* Insert a record */
361376 tupType = HeapTupleHeaderGetTypeId (rec );
362377 tupTypmod = HeapTupleHeaderGetTypMod (rec );
363- tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
364378
365379 record = & (GetActualValue (variable ).record );
366380 if (!record -> tupdesc )
367381 {
368382 /*
369383 * This is the first record for the var_name. Initialize record.
370384 */
385+ tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
371386 init_record (record , tupdesc , variable );
372387 }
373- else
388+ else if (LastTypeId == RECORDOID || !OidIsValid (LastTypeId ) ||
389+ LastTypeId != tupType )
390+ {
391+ /*
392+ * We need to check attributes of the new row if this is a transient
393+ * record type or if last record has different id.
394+ */
395+ tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
374396 check_attributes (variable , tupdesc );
397+ }
398+
399+ LastTypeId = tupType ;
375400
376401 insert_record (variable , rec );
377402
378403 /* Release resources */
379- ReleaseTupleDesc (tupdesc );
404+ if (tupdesc )
405+ ReleaseTupleDesc (tupdesc );
380406
381407 PG_FREE_IF_COPY (package_name , 0 );
382408 PG_FREE_IF_COPY (var_name , 1 );
@@ -396,7 +422,6 @@ variable_update(PG_FUNCTION_ARGS)
396422 bool res ;
397423 Oid tupType ;
398424 int32 tupTypmod ;
399- TupleDesc tupdesc ;
400425
401426 /* Checks */
402427 CHECK_ARGS_FOR_NULL ();
@@ -447,14 +472,22 @@ variable_update(PG_FUNCTION_ARGS)
447472 /* Update a record */
448473 tupType = HeapTupleHeaderGetTypeId (rec );
449474 tupTypmod = HeapTupleHeaderGetTypMod (rec );
450- tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
451475
452- check_attributes (variable , tupdesc );
476+ if (LastTypeId == RECORDOID || !OidIsValid (LastTypeId ) ||
477+ LastTypeId != tupType )
478+ {
479+ TupleDesc tupdesc = NULL ;
480+
481+ tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
482+ check_attributes (variable , tupdesc );
483+ ReleaseTupleDesc (tupdesc );
484+ }
485+
486+ LastTypeId = tupType ;
487+
453488 res = update_record (variable , rec );
454489
455490 /* Release resources */
456- ReleaseTupleDesc (tupdesc );
457-
458491 PG_FREE_IF_COPY (package_name , 0 );
459492 PG_FREE_IF_COPY (var_name , 1 );
460493
@@ -575,6 +608,8 @@ variable_select(PG_FUNCTION_ARGS)
575608 MemoryContextSwitchTo (oldcontext );
576609 PG_FREE_IF_COPY (package_name , 0 );
577610 PG_FREE_IF_COPY (var_name , 1 );
611+
612+ LastHSeqStatus = rstat ;
578613 }
579614
580615 funcctx = SRF_PERCALL_SETUP ();
@@ -592,6 +627,7 @@ variable_select(PG_FUNCTION_ARGS)
592627 }
593628 else
594629 {
630+ LastHSeqStatus = NULL ;
595631 pfree (rstat );
596632 SRF_RETURN_DONE (funcctx );
597633 }
@@ -867,8 +903,7 @@ remove_variable(PG_FUNCTION_ARGS)
867903 GetActualState (variable )-> is_valid = false;
868904 }
869905
870- /* Remove variable from cache */
871- LastVariable = NULL ;
906+ resetVariablesCache (false);
872907
873908 PG_FREE_IF_COPY (package_name , 0 );
874909 PG_FREE_IF_COPY (var_name , 1 );
@@ -904,9 +939,7 @@ remove_package(PG_FUNCTION_ARGS)
904939 errmsg ("unrecognized package \"%s\"" , key )));
905940 }
906941
907- /* Remove package and variable from cache */
908- LastPackage = NULL ;
909- LastVariable = NULL ;
942+ resetVariablesCache (true);
910943
911944 PG_FREE_IF_COPY (package_name , 0 );
912945 PG_RETURN_VOID ();
@@ -934,6 +967,20 @@ removePackageInternal(Package *package)
934967 GetActualState (package )-> is_valid = false;
935968}
936969
970+ /*
971+ * Reset cache variables to their default values. It is necessary to do in case
972+ * of some changes: removing, rollbacking, etc.
973+ */
974+ static void
975+ resetVariablesCache (bool with_package )
976+ {
977+ /* Remove package and variable from cache */
978+ if (with_package )
979+ LastPackage = NULL ;
980+ LastVariable = NULL ;
981+ LastTypeId = InvalidOid ;
982+ }
983+
937984/*
938985 * Remove all packages and variables.
939986 * Memory context will be released after committing.
@@ -955,9 +1002,7 @@ remove_packages(PG_FUNCTION_ARGS)
9551002 removePackageInternal (package );
9561003 }
9571004
958- /* Remove package and variable from cache */
959- LastPackage = NULL ;
960- LastVariable = NULL ;
1005+ resetVariablesCache (true);
9611006
9621007 PG_RETURN_VOID ();
9631008}
@@ -1157,6 +1202,8 @@ get_packages_stats(PG_FUNCTION_ARGS)
11571202 hash_seq_init (pstat , packagesHash );
11581203
11591204 funcctx -> user_fctx = pstat ;
1205+
1206+ LastHSeqStatus = pstat ;
11601207 }
11611208 else
11621209 funcctx -> user_fctx = NULL ;
@@ -1202,6 +1249,7 @@ get_packages_stats(PG_FUNCTION_ARGS)
12021249 }
12031250 else
12041251 {
1252+ LastHSeqStatus = NULL ;
12051253 pfree (pstat );
12061254 SRF_RETURN_DONE (funcctx );
12071255 }
@@ -1577,13 +1625,14 @@ copyValue(VarState *src, VarState *dest, Variable *destVar)
15771625static void
15781626freeValue (VarState * varstate , Oid typid )
15791627{
1580- if (typid == RECORDOID )
1628+ if (typid == RECORDOID && varstate -> value . record . hctx )
15811629 {
15821630 /* All records will be freed */
15831631 MemoryContextDelete (varstate -> value .record .hctx );
15841632 }
15851633 else if (varstate -> value .scalar .typbyval == false &&
1586- varstate -> value .scalar .is_null == false)
1634+ varstate -> value .scalar .is_null == false &&
1635+ varstate -> value .scalar .value )
15871636 {
15881637 pfree (DatumGetPointer (varstate -> value .scalar .value ));
15891638 }
@@ -1602,7 +1651,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete
16021651 pfree (stateToDelete );
16031652}
16041653
1605- static void
1654+ /* Remove package or variable (either transactional or regular) */
1655+ void
16061656removeObject (TransObject * object , TransObjectType type )
16071657{
16081658 bool found ;
@@ -1623,7 +1673,9 @@ removeObject(TransObject *object, TransObjectType type)
16231673 hash = packagesHash ;
16241674 }
16251675 else
1626- hash = ((Variable * ) object )-> package -> varHashTransact ;
1676+ hash = ((Variable * ) object )-> is_transactional ?
1677+ ((Variable * ) object )-> package -> varHashTransact :
1678+ ((Variable * ) object )-> package -> varHashRegular ;
16271679
16281680 /* Remove all object's states */
16291681 while (!dlist_is_empty (& object -> states ))
@@ -1632,8 +1684,7 @@ removeObject(TransObject *object, TransObjectType type)
16321684 /* Remove object from hash table */
16331685 hash_search (hash , object -> name , HASH_REMOVE , & found );
16341686
1635- LastPackage = NULL ;
1636- LastVariable = NULL ;
1687+ resetVariablesCache (true);
16371688}
16381689
16391690/*
@@ -2004,8 +2055,7 @@ processChanges(Action action)
20042055 MemoryContextDelete (ModuleContext );
20052056 packagesHash = NULL ;
20062057 ModuleContext = NULL ;
2007- LastPackage = NULL ;
2008- LastVariable = NULL ;
2058+ resetVariablesCache (true);
20092059 changesStack = NULL ;
20102060 changesStackContext = NULL ;
20112061 }
@@ -2065,6 +2115,23 @@ pgvTransCallback(XactEvent event, void *arg)
20652115 }
20662116}
20672117
2118+ /*
2119+ * ExecutorEnd hook: clean up hash table sequential scan status
2120+ */
2121+ static void
2122+ variable_ExecutorEnd (QueryDesc * queryDesc )
2123+ {
2124+ if (LastHSeqStatus )
2125+ {
2126+ hash_seq_term (LastHSeqStatus );
2127+ LastHSeqStatus = NULL ;
2128+ }
2129+ if (prev_ExecutorEnd )
2130+ prev_ExecutorEnd (queryDesc );
2131+ else
2132+ standard_ExecutorEnd (queryDesc );
2133+ }
2134+
20682135/*
20692136 * Register callback function when module starts
20702137 */
@@ -2073,6 +2140,10 @@ _PG_init(void)
20732140{
20742141 RegisterXactCallback (pgvTransCallback , NULL );
20752142 RegisterSubXactCallback (pgvSubTransCallback , NULL );
2143+
2144+ /* Install hooks. */
2145+ prev_ExecutorEnd = ExecutorEnd_hook ;
2146+ ExecutorEnd_hook = variable_ExecutorEnd ;
20762147}
20772148
20782149/*
@@ -2083,4 +2154,5 @@ _PG_fini(void)
20832154{
20842155 UnregisterXactCallback (pgvTransCallback , NULL );
20852156 UnregisterSubXactCallback (pgvSubTransCallback , NULL );
2157+ ExecutorEnd_hook = prev_ExecutorEnd ;
20862158}
0 commit comments