@@ -57,6 +57,7 @@ static Variable *createVariableInternal(Package *package,
57
57
text * name , Oid typid ,
58
58
bool is_transactional );
59
59
static void removePackageInternal (Package * package );
60
+ static void resetVariablesCache (bool with_package );
60
61
61
62
/* Functions to work with transactional objects */
62
63
static void createSavepoint (TransObject * object , TransObjectType type );
@@ -67,7 +68,6 @@ static void copyValue(VarState *src, VarState *dest, Variable *destVar);
67
68
static void freeValue (VarState * varstate , Oid typid );
68
69
static void removeState (TransObject * object , TransObjectType type ,
69
70
TransState * stateToDelete );
70
- static void removeObject (TransObject * object , TransObjectType type );
71
71
static bool isObjectChangedInCurrentTrans (TransObject * object );
72
72
static bool isObjectChangedInUpperTrans (TransObject * object );
73
73
@@ -80,6 +80,9 @@ static void makePackHTAB(Package *package, bool is_trans);
80
80
static inline ChangedObject * makeChangedObject (TransObject * object ,
81
81
MemoryContext ctx );
82
82
83
+ /* Hook functions */
84
+ static void variable_ExecutorEnd (QueryDesc * queryDesc );
85
+
83
86
#define CHECK_ARGS_FOR_NULL () \
84
87
do { \
85
88
if (fcinfo->argnull[0]) \
@@ -99,7 +102,19 @@ static MemoryContext ModuleContext = NULL;
99
102
static Package * LastPackage = NULL ;
100
103
/* Recent variable */
101
104
static 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 ;
102
115
116
+ /* Saved hook values for recall */
117
+ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL ;
103
118
104
119
/* This stack contains lists of changed variables and packages per each subxact level */
105
120
static dlist_head * changesStack = NULL ;
@@ -291,7 +306,7 @@ variable_insert(PG_FUNCTION_ARGS)
291
306
292
307
Oid tupType ;
293
308
int32 tupTypmod ;
294
- TupleDesc tupdesc ;
309
+ TupleDesc tupdesc = NULL ;
295
310
RecordVar * record ;
296
311
297
312
/* Checks */
@@ -360,23 +375,34 @@ variable_insert(PG_FUNCTION_ARGS)
360
375
/* Insert a record */
361
376
tupType = HeapTupleHeaderGetTypeId (rec );
362
377
tupTypmod = HeapTupleHeaderGetTypMod (rec );
363
- tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
364
378
365
379
record = & (GetActualValue (variable ).record );
366
380
if (!record -> tupdesc )
367
381
{
368
382
/*
369
383
* This is the first record for the var_name. Initialize record.
370
384
*/
385
+ tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
371
386
init_record (record , tupdesc , variable );
372
387
}
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 );
374
396
check_attributes (variable , tupdesc );
397
+ }
398
+
399
+ LastTypeId = tupType ;
375
400
376
401
insert_record (variable , rec );
377
402
378
403
/* Release resources */
379
- ReleaseTupleDesc (tupdesc );
404
+ if (tupdesc )
405
+ ReleaseTupleDesc (tupdesc );
380
406
381
407
PG_FREE_IF_COPY (package_name , 0 );
382
408
PG_FREE_IF_COPY (var_name , 1 );
@@ -396,7 +422,6 @@ variable_update(PG_FUNCTION_ARGS)
396
422
bool res ;
397
423
Oid tupType ;
398
424
int32 tupTypmod ;
399
- TupleDesc tupdesc ;
400
425
401
426
/* Checks */
402
427
CHECK_ARGS_FOR_NULL ();
@@ -447,14 +472,22 @@ variable_update(PG_FUNCTION_ARGS)
447
472
/* Update a record */
448
473
tupType = HeapTupleHeaderGetTypeId (rec );
449
474
tupTypmod = HeapTupleHeaderGetTypMod (rec );
450
- tupdesc = lookup_rowtype_tupdesc (tupType , tupTypmod );
451
475
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
+
453
488
res = update_record (variable , rec );
454
489
455
490
/* Release resources */
456
- ReleaseTupleDesc (tupdesc );
457
-
458
491
PG_FREE_IF_COPY (package_name , 0 );
459
492
PG_FREE_IF_COPY (var_name , 1 );
460
493
@@ -575,6 +608,8 @@ variable_select(PG_FUNCTION_ARGS)
575
608
MemoryContextSwitchTo (oldcontext );
576
609
PG_FREE_IF_COPY (package_name , 0 );
577
610
PG_FREE_IF_COPY (var_name , 1 );
611
+
612
+ LastHSeqStatus = rstat ;
578
613
}
579
614
580
615
funcctx = SRF_PERCALL_SETUP ();
@@ -592,6 +627,7 @@ variable_select(PG_FUNCTION_ARGS)
592
627
}
593
628
else
594
629
{
630
+ LastHSeqStatus = NULL ;
595
631
pfree (rstat );
596
632
SRF_RETURN_DONE (funcctx );
597
633
}
@@ -867,8 +903,7 @@ remove_variable(PG_FUNCTION_ARGS)
867
903
GetActualState (variable )-> is_valid = false;
868
904
}
869
905
870
- /* Remove variable from cache */
871
- LastVariable = NULL ;
906
+ resetVariablesCache (false);
872
907
873
908
PG_FREE_IF_COPY (package_name , 0 );
874
909
PG_FREE_IF_COPY (var_name , 1 );
@@ -904,9 +939,7 @@ remove_package(PG_FUNCTION_ARGS)
904
939
errmsg ("unrecognized package \"%s\"" , key )));
905
940
}
906
941
907
- /* Remove package and variable from cache */
908
- LastPackage = NULL ;
909
- LastVariable = NULL ;
942
+ resetVariablesCache (true);
910
943
911
944
PG_FREE_IF_COPY (package_name , 0 );
912
945
PG_RETURN_VOID ();
@@ -934,6 +967,20 @@ removePackageInternal(Package *package)
934
967
GetActualState (package )-> is_valid = false;
935
968
}
936
969
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
+
937
984
/*
938
985
* Remove all packages and variables.
939
986
* Memory context will be released after committing.
@@ -955,9 +1002,7 @@ remove_packages(PG_FUNCTION_ARGS)
955
1002
removePackageInternal (package );
956
1003
}
957
1004
958
- /* Remove package and variable from cache */
959
- LastPackage = NULL ;
960
- LastVariable = NULL ;
1005
+ resetVariablesCache (true);
961
1006
962
1007
PG_RETURN_VOID ();
963
1008
}
@@ -1157,6 +1202,8 @@ get_packages_stats(PG_FUNCTION_ARGS)
1157
1202
hash_seq_init (pstat , packagesHash );
1158
1203
1159
1204
funcctx -> user_fctx = pstat ;
1205
+
1206
+ LastHSeqStatus = pstat ;
1160
1207
}
1161
1208
else
1162
1209
funcctx -> user_fctx = NULL ;
@@ -1202,6 +1249,7 @@ get_packages_stats(PG_FUNCTION_ARGS)
1202
1249
}
1203
1250
else
1204
1251
{
1252
+ LastHSeqStatus = NULL ;
1205
1253
pfree (pstat );
1206
1254
SRF_RETURN_DONE (funcctx );
1207
1255
}
@@ -1577,13 +1625,14 @@ copyValue(VarState *src, VarState *dest, Variable *destVar)
1577
1625
static void
1578
1626
freeValue (VarState * varstate , Oid typid )
1579
1627
{
1580
- if (typid == RECORDOID )
1628
+ if (typid == RECORDOID && varstate -> value . record . hctx )
1581
1629
{
1582
1630
/* All records will be freed */
1583
1631
MemoryContextDelete (varstate -> value .record .hctx );
1584
1632
}
1585
1633
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 )
1587
1636
{
1588
1637
pfree (DatumGetPointer (varstate -> value .scalar .value ));
1589
1638
}
@@ -1602,7 +1651,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete
1602
1651
pfree (stateToDelete );
1603
1652
}
1604
1653
1605
- static void
1654
+ /* Remove package or variable (either transactional or regular) */
1655
+ void
1606
1656
removeObject (TransObject * object , TransObjectType type )
1607
1657
{
1608
1658
bool found ;
@@ -1623,7 +1673,9 @@ removeObject(TransObject *object, TransObjectType type)
1623
1673
hash = packagesHash ;
1624
1674
}
1625
1675
else
1626
- hash = ((Variable * ) object )-> package -> varHashTransact ;
1676
+ hash = ((Variable * ) object )-> is_transactional ?
1677
+ ((Variable * ) object )-> package -> varHashTransact :
1678
+ ((Variable * ) object )-> package -> varHashRegular ;
1627
1679
1628
1680
/* Remove all object's states */
1629
1681
while (!dlist_is_empty (& object -> states ))
@@ -1632,8 +1684,7 @@ removeObject(TransObject *object, TransObjectType type)
1632
1684
/* Remove object from hash table */
1633
1685
hash_search (hash , object -> name , HASH_REMOVE , & found );
1634
1686
1635
- LastPackage = NULL ;
1636
- LastVariable = NULL ;
1687
+ resetVariablesCache (true);
1637
1688
}
1638
1689
1639
1690
/*
@@ -2004,8 +2055,7 @@ processChanges(Action action)
2004
2055
MemoryContextDelete (ModuleContext );
2005
2056
packagesHash = NULL ;
2006
2057
ModuleContext = NULL ;
2007
- LastPackage = NULL ;
2008
- LastVariable = NULL ;
2058
+ resetVariablesCache (true);
2009
2059
changesStack = NULL ;
2010
2060
changesStackContext = NULL ;
2011
2061
}
@@ -2065,6 +2115,23 @@ pgvTransCallback(XactEvent event, void *arg)
2065
2115
}
2066
2116
}
2067
2117
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
+
2068
2135
/*
2069
2136
* Register callback function when module starts
2070
2137
*/
@@ -2073,6 +2140,10 @@ _PG_init(void)
2073
2140
{
2074
2141
RegisterXactCallback (pgvTransCallback , NULL );
2075
2142
RegisterSubXactCallback (pgvSubTransCallback , NULL );
2143
+
2144
+ /* Install hooks. */
2145
+ prev_ExecutorEnd = ExecutorEnd_hook ;
2146
+ ExecutorEnd_hook = variable_ExecutorEnd ;
2076
2147
}
2077
2148
2078
2149
/*
@@ -2083,4 +2154,5 @@ _PG_fini(void)
2083
2154
{
2084
2155
UnregisterXactCallback (pgvTransCallback , NULL );
2085
2156
UnregisterSubXactCallback (pgvSubTransCallback , NULL );
2157
+ ExecutorEnd_hook = prev_ExecutorEnd ;
2086
2158
}
0 commit comments