@@ -3754,6 +3754,10 @@ typedef struct AfterTriggerEventList
3754
3754
* end of the list, so it is relatively easy to discard them. The event
3755
3755
* list chunks themselves are stored in event_cxt.
3756
3756
*
3757
+ * prolonged_tuplestored is a list of transition table tuplestores whose
3758
+ * life are prolonged to the end of the outmost query instead of each nested
3759
+ * query.
3760
+ *
3757
3761
* query_depth is the current depth of nested AfterTriggerBeginQuery calls
3758
3762
* (-1 when the stack is empty).
3759
3763
*
@@ -3819,6 +3823,7 @@ typedef struct AfterTriggersData
3819
3823
SetConstraintState state ; /* the active S C state */
3820
3824
AfterTriggerEventList events ; /* deferred-event list */
3821
3825
MemoryContext event_cxt ; /* memory context for events, if any */
3826
+ List * prolonged_tuplestores ; /* list of prolonged tuplestores */
3822
3827
3823
3828
/* per-query-level data: */
3824
3829
AfterTriggersQueryData * query_stack ; /* array of structs shown below */
@@ -3854,6 +3859,7 @@ struct AfterTriggersTableData
3854
3859
bool closed ; /* true when no longer OK to add tuples */
3855
3860
bool before_trig_done ; /* did we already queue BS triggers? */
3856
3861
bool after_trig_done ; /* did we already queue AS triggers? */
3862
+ bool prolonged ; /* are transition tables prolonged? */
3857
3863
AfterTriggerEventList after_trig_events ; /* if so, saved list pointer */
3858
3864
3859
3865
/*
@@ -3903,6 +3909,7 @@ static void TransitionTableAddTuple(EState *estate,
3903
3909
TupleTableSlot * original_insert_tuple ,
3904
3910
Tuplestorestate * tuplestore );
3905
3911
static void AfterTriggerFreeQuery (AfterTriggersQueryData * qs );
3912
+ static void release_or_prolong_tuplestore (Tuplestorestate * ts , bool prolonged );
3906
3913
static SetConstraintState SetConstraintStateCreate (int numalloc );
3907
3914
static SetConstraintState SetConstraintStateCopy (SetConstraintState origstate );
3908
3915
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state ,
@@ -4782,6 +4789,45 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
4782
4789
}
4783
4790
4784
4791
4792
+ /*
4793
+ * SetTransitionTablePreserved
4794
+ *
4795
+ * Prolong lifespan of transition tables corresponding specified relid and
4796
+ * command type to the end of the outmost query instead of each nested query.
4797
+ * This enables to use nested AFTER trigger's transition tables from outer
4798
+ * query's triggers. Currently, only immediate incremental view maintenance
4799
+ * uses this.
4800
+ */
4801
+ void
4802
+ SetTransitionTablePreserved (Oid relid , CmdType cmdType )
4803
+ {
4804
+ AfterTriggersTableData * table ;
4805
+ AfterTriggersQueryData * qs ;
4806
+ bool found = false;
4807
+ ListCell * lc ;
4808
+
4809
+ /* Check state, like AfterTriggerSaveEvent. */
4810
+ if (afterTriggers .query_depth < 0 )
4811
+ elog (ERROR , "SetTransitionTablePreserved() called outside of query" );
4812
+
4813
+ qs = & afterTriggers .query_stack [afterTriggers .query_depth ];
4814
+
4815
+ foreach (lc , qs -> tables )
4816
+ {
4817
+ table = (AfterTriggersTableData * ) lfirst (lc );
4818
+ if (table -> relid == relid && table -> cmdType == cmdType &&
4819
+ table -> closed )
4820
+ {
4821
+ table -> prolonged = true;
4822
+ found = true;
4823
+ }
4824
+ }
4825
+
4826
+ if (!found )
4827
+ elog (ERROR ,"could not find table with OID %d and command type %d" , relid , cmdType );
4828
+ }
4829
+
4830
+
4785
4831
/*
4786
4832
* GetAfterTriggersTableData
4787
4833
*
@@ -4992,6 +5038,7 @@ AfterTriggerBeginXact(void)
4992
5038
*/
4993
5039
afterTriggers .firing_counter = (CommandId ) 1 ; /* mustn't be 0 */
4994
5040
afterTriggers .query_depth = -1 ;
5041
+ afterTriggers .prolonged_tuplestores = NIL ;
4995
5042
4996
5043
/*
4997
5044
* Verify that there is no leftover state remaining. If these assertions
@@ -5152,19 +5199,19 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
5152
5199
ts = table -> old_upd_tuplestore ;
5153
5200
table -> old_upd_tuplestore = NULL ;
5154
5201
if (ts )
5155
- tuplestore_end (ts );
5202
+ release_or_prolong_tuplestore (ts , table -> prolonged );
5156
5203
ts = table -> new_upd_tuplestore ;
5157
5204
table -> new_upd_tuplestore = NULL ;
5158
5205
if (ts )
5159
- tuplestore_end (ts );
5206
+ release_or_prolong_tuplestore (ts , table -> prolonged );
5160
5207
ts = table -> old_del_tuplestore ;
5161
5208
table -> old_del_tuplestore = NULL ;
5162
5209
if (ts )
5163
- tuplestore_end (ts );
5210
+ release_or_prolong_tuplestore (ts , table -> prolonged );
5164
5211
ts = table -> new_ins_tuplestore ;
5165
5212
table -> new_ins_tuplestore = NULL ;
5166
5213
if (ts )
5167
- tuplestore_end (ts );
5214
+ release_or_prolong_tuplestore (ts , table -> prolonged );
5168
5215
if (table -> storeslot )
5169
5216
{
5170
5217
TupleTableSlot * slot = table -> storeslot ;
@@ -5181,6 +5228,34 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
5181
5228
*/
5182
5229
qs -> tables = NIL ;
5183
5230
list_free_deep (tables );
5231
+
5232
+ /* Release prolonged tuplestores at the end of the outmost query */
5233
+ if (afterTriggers .query_depth == 0 )
5234
+ {
5235
+ foreach (lc , afterTriggers .prolonged_tuplestores )
5236
+ {
5237
+ ts = (Tuplestorestate * ) lfirst (lc );
5238
+ if (ts )
5239
+ tuplestore_end (ts );
5240
+ }
5241
+ afterTriggers .prolonged_tuplestores = NIL ;
5242
+ }
5243
+ }
5244
+
5245
+ /*
5246
+ * Release the tuplestore, or append it to the prolonged tuplestores list.
5247
+ */
5248
+ static void
5249
+ release_or_prolong_tuplestore (Tuplestorestate * ts , bool prolonged )
5250
+ {
5251
+ if (prolonged && afterTriggers .query_depth > 0 )
5252
+ {
5253
+ MemoryContext oldcxt = MemoryContextSwitchTo (CurTransactionContext );
5254
+ afterTriggers .prolonged_tuplestores = lappend (afterTriggers .prolonged_tuplestores , ts );
5255
+ MemoryContextSwitchTo (oldcxt );
5256
+ }
5257
+ else
5258
+ tuplestore_end (ts );
5184
5259
}
5185
5260
5186
5261
0 commit comments