@@ -57,6 +57,7 @@ static Variable *createVariableInternal(Package *package,
5757text * name ,Oid typid ,
5858bool 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 ,
6970TransState * 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 ,
8181MemoryContext ctx );
8282
83+ /* Hook functions */
84+ static void variable_ExecutorEnd (QueryDesc * queryDesc );
85+
8386#define CHECK_ARGS_FOR_NULL () \
8487do { \
8588if (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
292307Oid tupType ;
293308int32 tupTypmod ;
294- TupleDesc tupdesc ;
309+ TupleDesc tupdesc = NULL ;
295310RecordVar * record ;
296311
297312/* Checks */
@@ -360,23 +375,34 @@ variable_insert(PG_FUNCTION_ARGS)
360375/* Insert a record */
361376tupType = HeapTupleHeaderGetTypeId (rec );
362377tupTypmod = HeapTupleHeaderGetTypMod (rec );
363- tupdesc = lookup_rowtype_tupdesc (tupType ,tupTypmod );
364378
365379record = & (GetActualValue (variable ).record );
366380if (!record -> tupdesc )
367381{
368382/*
369383 * This is the first record for the var_name. Initialize record.
370384 */
385+ tupdesc = lookup_rowtype_tupdesc (tupType ,tupTypmod );
371386init_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 );
374396check_attributes (variable ,tupdesc );
397+ }
398+
399+ LastTypeId = tupType ;
375400
376401insert_record (variable ,rec );
377402
378403/* Release resources */
379- ReleaseTupleDesc (tupdesc );
404+ if (tupdesc )
405+ ReleaseTupleDesc (tupdesc );
380406
381407PG_FREE_IF_COPY (package_name ,0 );
382408PG_FREE_IF_COPY (var_name ,1 );
@@ -396,7 +422,6 @@ variable_update(PG_FUNCTION_ARGS)
396422bool res ;
397423Oid tupType ;
398424int32 tupTypmod ;
399- TupleDesc tupdesc ;
400425
401426/* Checks */
402427CHECK_ARGS_FOR_NULL ();
@@ -447,14 +472,22 @@ variable_update(PG_FUNCTION_ARGS)
447472/* Update a record */
448473tupType = HeapTupleHeaderGetTypeId (rec );
449474tupTypmod = 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+
453488res = update_record (variable ,rec );
454489
455490/* Release resources */
456- ReleaseTupleDesc (tupdesc );
457-
458491PG_FREE_IF_COPY (package_name ,0 );
459492PG_FREE_IF_COPY (var_name ,1 );
460493
@@ -575,6 +608,8 @@ variable_select(PG_FUNCTION_ARGS)
575608MemoryContextSwitchTo (oldcontext );
576609PG_FREE_IF_COPY (package_name ,0 );
577610PG_FREE_IF_COPY (var_name ,1 );
611+
612+ LastHSeqStatus = rstat ;
578613}
579614
580615funcctx = SRF_PERCALL_SETUP ();
@@ -592,6 +627,7 @@ variable_select(PG_FUNCTION_ARGS)
592627}
593628else
594629{
630+ LastHSeqStatus = NULL ;
595631pfree (rstat );
596632SRF_RETURN_DONE (funcctx );
597633}
@@ -867,8 +903,7 @@ remove_variable(PG_FUNCTION_ARGS)
867903GetActualState (variable )-> is_valid = false;
868904}
869905
870- /* Remove variable from cache */
871- LastVariable = NULL ;
906+ resetVariablesCache (false);
872907
873908PG_FREE_IF_COPY (package_name ,0 );
874909PG_FREE_IF_COPY (var_name ,1 );
@@ -904,9 +939,7 @@ remove_package(PG_FUNCTION_ARGS)
904939errmsg ("unrecognized package \"%s\"" ,key )));
905940}
906941
907- /* Remove package and variable from cache */
908- LastPackage = NULL ;
909- LastVariable = NULL ;
942+ resetVariablesCache (true);
910943
911944PG_FREE_IF_COPY (package_name ,0 );
912945PG_RETURN_VOID ();
@@ -934,6 +967,20 @@ removePackageInternal(Package *package)
934967GetActualState (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)
9551002removePackageInternal (package );
9561003}
9571004
958- /* Remove package and variable from cache */
959- LastPackage = NULL ;
960- LastVariable = NULL ;
1005+ resetVariablesCache (true);
9611006
9621007PG_RETURN_VOID ();
9631008}
@@ -1157,6 +1202,8 @@ get_packages_stats(PG_FUNCTION_ARGS)
11571202hash_seq_init (pstat ,packagesHash );
11581203
11591204funcctx -> user_fctx = pstat ;
1205+
1206+ LastHSeqStatus = pstat ;
11601207}
11611208else
11621209funcctx -> user_fctx = NULL ;
@@ -1202,6 +1249,7 @@ get_packages_stats(PG_FUNCTION_ARGS)
12021249}
12031250else
12041251{
1252+ LastHSeqStatus = NULL ;
12051253pfree (pstat );
12061254SRF_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 */
15831631MemoryContextDelete (varstate -> value .record .hctx );
15841632}
15851633else 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{
15881637pfree (DatumGetPointer (varstate -> value .scalar .value ));
15891638}
@@ -1602,7 +1651,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete
16021651pfree (stateToDelete );
16031652}
16041653
1605- static void
1654+ /* Remove package or variable (either transactional or regular) */
1655+ void
16061656removeObject (TransObject * object ,TransObjectType type )
16071657{
16081658bool found ;
@@ -1623,7 +1673,9 @@ removeObject(TransObject *object, TransObjectType type)
16231673hash = packagesHash ;
16241674}
16251675else
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 */
16291681while (!dlist_is_empty (& object -> states ))
@@ -1632,8 +1684,7 @@ removeObject(TransObject *object, TransObjectType type)
16321684/* Remove object from hash table */
16331685hash_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)
20042055MemoryContextDelete (ModuleContext );
20052056packagesHash = NULL ;
20062057ModuleContext = NULL ;
2007- LastPackage = NULL ;
2008- LastVariable = NULL ;
2058+ resetVariablesCache (true);
20092059changesStack = NULL ;
20102060changesStackContext = 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{
20742141RegisterXactCallback (pgvTransCallback ,NULL );
20752142RegisterSubXactCallback (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{
20842155UnregisterXactCallback (pgvTransCallback ,NULL );
20852156UnregisterSubXactCallback (pgvSubTransCallback ,NULL );
2157+ ExecutorEnd_hook = prev_ExecutorEnd ;
20862158}