summaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c385
1 files changed, 271 insertions, 114 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8f6de3fc69..576fa19705 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -25,6 +25,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -100,7 +101,8 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
HeapTuple oldtup, HeapTuple newtup,
- List *recheckIndexes, Bitmapset *modifiedCols);
+ List *recheckIndexes, Bitmapset *modifiedCols,
+ TransitionCaptureState *transition_capture);
static void AfterTriggerEnlargeQueryState(void);
#ifdef XCP
@@ -361,13 +363,6 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* adjustments will be needed below.
*/
- if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a partitioned table",
- RelationGetRelationName(rel)),
- errdetail("Triggers on partitioned tables cannot have transition tables.")));
-
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -382,6 +377,27 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
RelationGetRelationName(rel)),
errdetail("Triggers on views cannot have transition tables.")));
+ /*
+ * We currently don't allow row-level triggers with transition
+ * tables on partition or inheritance children. Such triggers
+ * would somehow need to see tuples converted to the format of the
+ * table they're attached to, and it's not clear which subset of
+ * tuples each child should see. See also the prohibitions in
+ * ATExecAttachPartition() and ATExecAddInherit().
+ */
+ if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
+ {
+ /* Use appropriate error message. */
+ if (rel->rd_rel->relispartition)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ROW triggers with transition tables are not supported on partitions")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ROW triggers with transition tables are not supported on inheritance children")));
+ }
+
if (stmt->timing != TRIGGER_TYPE_AFTER)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -392,6 +408,23 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("TRUNCATE triggers with transition tables are not supported")));
+ /*
+ * We currently don't allow multi-event triggers ("INSERT OR
+ * UPDATE") with transition tables, because it's not clear how to
+ * handle INSERT ... ON CONFLICT statements which can fire both
+ * INSERT and UPDATE triggers. We show the inserted tuples to
+ * INSERT triggers and the updated tuples to UPDATE triggers, but
+ * it's not yet clear what INSERT OR UPDATE trigger should see.
+ * This restriction could be lifted if we can decide on the right
+ * semantics in a later release.
+ */
+ if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
+ (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
+ (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Transition tables cannot be specified for triggers with more than one event")));
+
if (tt->isNew)
{
if (!(TRIGGER_FOR_INSERT(tgtype) ||
@@ -2038,6 +2071,120 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
#endif /* NOT_USED */
/*
+ * Check if there is a row-level trigger with transition tables that prevents
+ * a table from becoming an inheritance child or partition. Return the name
+ * of the first such incompatible trigger, or NULL if there is none.
+ */
+const char *
+FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
+{
+ if (trigdesc != NULL)
+ {
+ int i;
+
+ for (i = 0; i < trigdesc->numtriggers; ++i)
+ {
+ Trigger *trigger = &trigdesc->triggers[i];
+
+ if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
+ return trigger->tgname;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Make a TransitionCaptureState object from a given TriggerDesc. The
+ * resulting object holds the flags which control whether transition tuples
+ * are collected when tables are modified, and the tuplestores themselves.
+ * Note that we copy the flags from a parent table into this struct (rather
+ * than using each relation's TriggerDesc directly) so that we can use it to
+ * control the collection of transition tuples from child tables.
+ *
+ * If there are no triggers with transition tables configured for 'trigdesc',
+ * then return NULL.
+ *
+ * The resulting object can be passed to the ExecAR* functions. The caller
+ * should set tcs_map or tcs_original_insert_tuple as appropriate when dealing
+ * with child tables.
+ */
+TransitionCaptureState *
+MakeTransitionCaptureState(TriggerDesc *trigdesc)
+{
+ TransitionCaptureState *state = NULL;
+
+ if (trigdesc != NULL &&
+ (trigdesc->trig_delete_old_table || trigdesc->trig_update_old_table ||
+ trigdesc->trig_update_new_table || trigdesc->trig_insert_new_table))
+ {
+ MemoryContext oldcxt;
+ ResourceOwner saveResourceOwner;
+
+ /*
+ * Normally DestroyTransitionCaptureState should be called after
+ * executing all AFTER triggers for the current statement.
+ *
+ * To handle error cleanup, TransitionCaptureState and the tuplestores
+ * it contains will live in the current [sub]transaction's memory
+ * context. Likewise for the current resource owner, because we also
+ * want to clean up temporary files spilled to disk by the tuplestore
+ * in that scenario. This scope is sufficient, because AFTER triggers
+ * with transition tables cannot be deferred (only constraint triggers
+ * can be deferred, and constraint triggers cannot have transition
+ * tables). The AFTER trigger queue may contain pointers to this
+ * TransitionCaptureState, but any such entries will be processed or
+ * discarded before the end of the current [sub]transaction.
+ *
+ * If a future release allows deferred triggers with transition
+ * tables, we'll need to reconsider the scope of the
+ * TransitionCaptureState object.
+ */
+ oldcxt = MemoryContextSwitchTo(CurTransactionContext);
+ saveResourceOwner = CurrentResourceOwner;
+
+ state = (TransitionCaptureState *)
+ palloc0(sizeof(TransitionCaptureState));
+ state->tcs_delete_old_table = trigdesc->trig_delete_old_table;
+ state->tcs_update_old_table = trigdesc->trig_update_old_table;
+ state->tcs_update_new_table = trigdesc->trig_update_new_table;
+ state->tcs_insert_new_table = trigdesc->trig_insert_new_table;
+ PG_TRY();
+ {
+ CurrentResourceOwner = CurTransactionResourceOwner;
+ if (trigdesc->trig_delete_old_table || trigdesc->trig_update_old_table)
+ state->tcs_old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
+ if (trigdesc->trig_insert_new_table)
+ state->tcs_insert_tuplestore = tuplestore_begin_heap(false, false, work_mem);
+ if (trigdesc->trig_update_new_table)
+ state->tcs_update_tuplestore = tuplestore_begin_heap(false, false, work_mem);
+ }
+ PG_CATCH();
+ {
+ CurrentResourceOwner = saveResourceOwner;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ CurrentResourceOwner = saveResourceOwner;
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return state;
+}
+
+void
+DestroyTransitionCaptureState(TransitionCaptureState *tcs)
+{
+ if (tcs->tcs_insert_tuplestore != NULL)
+ tuplestore_end(tcs->tcs_insert_tuplestore);
+ if (tcs->tcs_update_tuplestore != NULL)
+ tuplestore_end(tcs->tcs_update_tuplestore);
+ if (tcs->tcs_old_tuplestore != NULL)
+ tuplestore_end(tcs->tcs_old_tuplestore);
+ pfree(tcs);
+}
+
+/*
* Call a trigger function.
*
* trigdata: trigger descriptor.
@@ -2195,13 +2342,14 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
}
void
-ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo)
+ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if (trigdesc && trigdesc->trig_insert_after_statement)
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
- false, NULL, NULL, NIL, NULL);
+ false, NULL, NULL, NIL, NULL, transition_capture);
}
TupleTableSlot *
@@ -2272,14 +2420,17 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes)
+ HeapTuple trigtuple, List *recheckIndexes,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- if (trigdesc &&
- (trigdesc->trig_insert_after_row || trigdesc->trig_insert_new_table))
+ if ((trigdesc && trigdesc->trig_insert_after_row) ||
+ (transition_capture && transition_capture->tcs_insert_new_table))
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
- true, NULL, trigtuple, recheckIndexes, NULL);
+ true, NULL, trigtuple,
+ recheckIndexes, NULL,
+ transition_capture);
}
TupleTableSlot *
@@ -2401,13 +2552,14 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
}
void
-ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
+ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if (trigdesc && trigdesc->trig_delete_after_statement)
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
- false, NULL, NULL, NIL, NULL);
+ false, NULL, NULL, NIL, NULL, transition_capture);
}
bool
@@ -2482,12 +2634,13 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
void
ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple)
+ HeapTuple fdw_trigtuple,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- if (trigdesc &&
- (trigdesc->trig_delete_after_row || trigdesc->trig_delete_old_table))
+ if ((trigdesc && trigdesc->trig_delete_after_row) ||
+ (transition_capture && transition_capture->tcs_delete_old_table))
{
HeapTuple trigtuple;
@@ -2503,7 +2656,8 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
trigtuple = fdw_trigtuple;
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
- true, trigtuple, NULL, NIL, NULL);
+ true, trigtuple, NULL, NIL, NULL,
+ transition_capture);
if (trigtuple != fdw_trigtuple)
heap_freetuple(trigtuple);
}
@@ -2612,14 +2766,16 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
}
void
-ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
+ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if (trigdesc && trigdesc->trig_update_after_statement)
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
false, NULL, NULL, NIL,
- GetUpdatedColumns(relinfo, estate));
+ GetUpdatedColumns(relinfo, estate),
+ transition_capture);
}
TupleTableSlot *
@@ -2744,12 +2900,15 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
HeapTuple newtuple,
- List *recheckIndexes)
+ List *recheckIndexes,
+ TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- if (trigdesc && (trigdesc->trig_update_after_row ||
- trigdesc->trig_update_old_table || trigdesc->trig_update_new_table))
+ if ((trigdesc && trigdesc->trig_update_after_row) ||
+ (transition_capture &&
+ (transition_capture->tcs_update_old_table ||
+ transition_capture->tcs_update_new_table)))
{
HeapTuple trigtuple;
@@ -2766,7 +2925,8 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
true, trigtuple, newtuple, recheckIndexes,
- GetUpdatedColumns(relinfo, estate));
+ GetUpdatedColumns(relinfo, estate),
+ transition_capture);
if (trigtuple != fdw_trigtuple)
heap_freetuple(trigtuple);
}
@@ -2897,7 +3057,7 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
if (trigdesc && trigdesc->trig_truncate_after_statement)
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_TRUNCATE,
- false, NULL, NULL, NIL, NULL);
+ false, NULL, NULL, NIL, NULL, NULL);
}
@@ -3285,6 +3445,7 @@ typedef struct AfterTriggerSharedData
Oid ats_tgoid; /* the trigger's ID */
Oid ats_relid; /* the relation it's on */
CommandId ats_firing_id; /* ID for firing cycle */
+ TransitionCaptureState *ats_transition_capture;
} AfterTriggerSharedData;
typedef struct AfterTriggerEventData *AfterTriggerEvent;
@@ -3390,9 +3551,6 @@ typedef struct AfterTriggerEventList
* fdw_tuplestores[query_depth] is a tuplestore containing the foreign tuples
* needed for the current query.
*
- * old_tuplestores[query_depth] and new_tuplestores[query_depth] hold the
- * transition relations for the current query.
- *
* maxquerydepth is just the allocated length of query_stack and the
* tuplestores.
*
@@ -3425,8 +3583,6 @@ typedef struct AfterTriggersData
AfterTriggerEventList *query_stack; /* events pending from each query */
Tuplestorestate **fdw_tuplestores; /* foreign tuples for one row from
* each query */
- Tuplestorestate **old_tuplestores; /* all old tuples from each query */
- Tuplestorestate **new_tuplestores; /* all new tuples from each query */
int maxquerydepth; /* allocated len of above array */
MemoryContext event_cxt; /* memory context for events, if any */
@@ -3447,7 +3603,8 @@ static void AfterTriggerExecute(AfterTriggerEvent event,
Instrumentation *instr,
MemoryContext per_tuple_context,
TupleTableSlot *trig_tuple_slot1,
- TupleTableSlot *trig_tuple_slot2);
+ TupleTableSlot *trig_tuple_slot2,
+ TransitionCaptureState *transition_capture);
static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,
@@ -3456,8 +3613,6 @@ static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,
/*
* Gets a current query transition tuplestore and initializes it if necessary.
- * This can be holding a single transition row tuple (in the case of an FDW)
- * or a transition table (for an AFTER trigger).
*/
static Tuplestorestate *
GetTriggerTransitionTuplestore(Tuplestorestate **tss)
@@ -3637,6 +3792,7 @@ afterTriggerAddEvent(AfterTriggerEventList *events,
if (newshared->ats_tgoid == evtshared->ats_tgoid &&
newshared->ats_relid == evtshared->ats_relid &&
newshared->ats_event == evtshared->ats_event &&
+ newshared->ats_transition_capture == evtshared->ats_transition_capture &&
newshared->ats_firing_id == 0)
break;
}
@@ -3748,7 +3904,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
FmgrInfo *finfo, Instrumentation *instr,
MemoryContext per_tuple_context,
TupleTableSlot *trig_tuple_slot1,
- TupleTableSlot *trig_tuple_slot2)
+ TupleTableSlot *trig_tuple_slot2,
+ TransitionCaptureState *transition_capture)
{
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
@@ -3863,16 +4020,36 @@ AfterTriggerExecute(AfterTriggerEvent event,
/*
* Set up the tuplestore information.
*/
- if (LocTriggerData.tg_trigger->tgoldtable)
- LocTriggerData.tg_oldtable =
- GetTriggerTransitionTuplestore(afterTriggers.old_tuplestores);
- else
- LocTriggerData.tg_oldtable = NULL;
- if (LocTriggerData.tg_trigger->tgnewtable)
- LocTriggerData.tg_newtable =
- GetTriggerTransitionTuplestore(afterTriggers.new_tuplestores);
- else
- LocTriggerData.tg_newtable = NULL;
+ LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
+ if (transition_capture != NULL)
+ {
+ if (LocTriggerData.tg_trigger->tgoldtable)
+ LocTriggerData.tg_oldtable = transition_capture->tcs_old_tuplestore;
+ if (LocTriggerData.tg_trigger->tgnewtable)
+ {
+ /*
+ * Currently a trigger with transition tables may only be defined
+ * for a single event type (here AFTER INSERT or AFTER UPDATE, but
+ * not AFTER INSERT OR ...).
+ */
+ Assert((TRIGGER_FOR_INSERT(LocTriggerData.tg_trigger->tgtype) != 0) ^
+ (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype) != 0));
+
+ /*
+ * Show either the insert or update new tuple images, depending on
+ * which event type the trigger was registered for. A single
+ * statement may have produced both in the case of INSERT ... ON
+ * CONFLICT ... DO UPDATE, and in that case the event determines
+ * which tuplestore the trigger sees as the NEW TABLE.
+ */
+ if (TRIGGER_FOR_INSERT(LocTriggerData.tg_trigger->tgtype))
+ LocTriggerData.tg_newtable =
+ transition_capture->tcs_insert_tuplestore;
+ else
+ LocTriggerData.tg_newtable =
+ transition_capture->tcs_update_tuplestore;
+ }
+ }
/*
* Setup the remaining trigger information
@@ -4080,7 +4257,8 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
* won't try to re-fire it.
*/
AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
- per_tuple_context, slot1, slot2);
+ per_tuple_context, slot1, slot2,
+ evtshared->ats_transition_capture);
/*
* Mark the event as done.
@@ -4154,8 +4332,6 @@ AfterTriggerBeginXact(void)
Assert(afterTriggers.state == NULL);
Assert(afterTriggers.query_stack == NULL);
Assert(afterTriggers.fdw_tuplestores == NULL);
- Assert(afterTriggers.old_tuplestores == NULL);
- Assert(afterTriggers.new_tuplestores == NULL);
Assert(afterTriggers.maxquerydepth == 0);
Assert(afterTriggers.event_cxt == NULL);
Assert(afterTriggers.events.head == NULL);
@@ -4224,8 +4400,6 @@ AfterTriggerEndQuery(EState *estate)
{
AfterTriggerEventList *events;
Tuplestorestate *fdw_tuplestore;
- Tuplestorestate *old_tuplestore;
- Tuplestorestate *new_tuplestore;
/* Must be inside a query, too */
Assert(afterTriggers.query_depth >= 0);
@@ -4284,18 +4458,6 @@ AfterTriggerEndQuery(EState *estate)
tuplestore_end(fdw_tuplestore);
afterTriggers.fdw_tuplestores[afterTriggers.query_depth] = NULL;
}
- old_tuplestore = afterTriggers.old_tuplestores[afterTriggers.query_depth];
- if (old_tuplestore)
- {
- tuplestore_end(old_tuplestore);
- afterTriggers.old_tuplestores[afterTriggers.query_depth] = NULL;
- }
- new_tuplestore = afterTriggers.new_tuplestores[afterTriggers.query_depth];
- if (new_tuplestore)
- {
- tuplestore_end(new_tuplestore);
- afterTriggers.new_tuplestores[afterTriggers.query_depth] = NULL;
- }
afterTriggerFreeEventList(&afterTriggers.query_stack[afterTriggers.query_depth]);
afterTriggers.query_depth--;
@@ -4409,8 +4571,6 @@ AfterTriggerEndXact(bool isCommit)
*/
afterTriggers.query_stack = NULL;
afterTriggers.fdw_tuplestores = NULL;
- afterTriggers.old_tuplestores = NULL;
- afterTriggers.new_tuplestores = NULL;
afterTriggers.maxquerydepth = 0;
afterTriggers.state = NULL;
@@ -4543,18 +4703,6 @@ AfterTriggerEndSubXact(bool isCommit)
tuplestore_end(ts);
afterTriggers.fdw_tuplestores[afterTriggers.query_depth] = NULL;
}
- ts = afterTriggers.old_tuplestores[afterTriggers.query_depth];
- if (ts)
- {
- tuplestore_end(ts);
- afterTriggers.old_tuplestores[afterTriggers.query_depth] = NULL;
- }
- ts = afterTriggers.new_tuplestores[afterTriggers.query_depth];
- if (ts)
- {
- tuplestore_end(ts);
- afterTriggers.new_tuplestores[afterTriggers.query_depth] = NULL;
- }
afterTriggerFreeEventList(&afterTriggers.query_stack[afterTriggers.query_depth]);
}
@@ -4634,12 +4782,6 @@ AfterTriggerEnlargeQueryState(void)
afterTriggers.fdw_tuplestores = (Tuplestorestate **)
MemoryContextAllocZero(TopTransactionContext,
new_alloc * sizeof(Tuplestorestate *));
- afterTriggers.old_tuplestores = (Tuplestorestate **)
- MemoryContextAllocZero(TopTransactionContext,
- new_alloc * sizeof(Tuplestorestate *));
- afterTriggers.new_tuplestores = (Tuplestorestate **)
- MemoryContextAllocZero(TopTransactionContext,
- new_alloc * sizeof(Tuplestorestate *));
afterTriggers.maxquerydepth = new_alloc;
}
else
@@ -4655,19 +4797,9 @@ AfterTriggerEnlargeQueryState(void)
afterTriggers.fdw_tuplestores = (Tuplestorestate **)
repalloc(afterTriggers.fdw_tuplestores,
new_alloc * sizeof(Tuplestorestate *));
- afterTriggers.old_tuplestores = (Tuplestorestate **)
- repalloc(afterTriggers.old_tuplestores,
- new_alloc * sizeof(Tuplestorestate *));
- afterTriggers.new_tuplestores = (Tuplestorestate **)
- repalloc(afterTriggers.new_tuplestores,
- new_alloc * sizeof(Tuplestorestate *));
/* Clear newly-allocated slots for subsequent lazy initialization. */
memset(afterTriggers.fdw_tuplestores + old_alloc,
0, (new_alloc - old_alloc) * sizeof(Tuplestorestate *));
- memset(afterTriggers.old_tuplestores + old_alloc,
- 0, (new_alloc - old_alloc) * sizeof(Tuplestorestate *));
- memset(afterTriggers.new_tuplestores + old_alloc,
- 0, (new_alloc - old_alloc) * sizeof(Tuplestorestate *));
afterTriggers.maxquerydepth = new_alloc;
}
@@ -5134,7 +5266,8 @@ static void
AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
HeapTuple oldtup, HeapTuple newtup,
- List *recheckIndexes, Bitmapset *modifiedCols)
+ List *recheckIndexes, Bitmapset *modifiedCols,
+ TransitionCaptureState *transition_capture)
{
Relation rel = relinfo->ri_RelationDesc;
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -5159,40 +5292,63 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
AfterTriggerEnlargeQueryState();
/*
- * If the relation has AFTER ... FOR EACH ROW triggers, capture rows into
- * transition tuplestores for this depth.
+ * If the directly named relation has any triggers with transition tables,
+ * then we need to capture transition tuples.
*/
- if (row_trigger)
+ if (row_trigger && transition_capture != NULL)
{
- if ((event == TRIGGER_EVENT_DELETE &&
- trigdesc->trig_delete_old_table) ||
- (event == TRIGGER_EVENT_UPDATE &&
- trigdesc->trig_update_old_table))
+ HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
+ TupleConversionMap *map = transition_capture->tcs_map;
+ bool delete_old_table = transition_capture->tcs_delete_old_table;
+ bool update_old_table = transition_capture->tcs_update_old_table;
+ bool update_new_table = transition_capture->tcs_update_new_table;
+ bool insert_new_table = transition_capture->tcs_insert_new_table;;
+
+ if ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
+ (event == TRIGGER_EVENT_UPDATE && update_old_table))
{
Tuplestorestate *old_tuplestore;
Assert(oldtup != NULL);
- old_tuplestore =
- GetTriggerTransitionTuplestore
- (afterTriggers.old_tuplestores);
- tuplestore_puttuple(old_tuplestore, oldtup);
+ old_tuplestore = transition_capture->tcs_old_tuplestore;
+
+ if (map != NULL)
+ {
+ HeapTuple converted = do_convert_tuple(oldtup, map);
+
+ tuplestore_puttuple(old_tuplestore, converted);
+ pfree(converted);
+ }
+ else
+ tuplestore_puttuple(old_tuplestore, oldtup);
}
- if ((event == TRIGGER_EVENT_INSERT &&
- trigdesc->trig_insert_new_table) ||
- (event == TRIGGER_EVENT_UPDATE &&
- trigdesc->trig_update_new_table))
+ if ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
+ (event == TRIGGER_EVENT_UPDATE && update_new_table))
{
Tuplestorestate *new_tuplestore;
Assert(newtup != NULL);
- new_tuplestore =
- GetTriggerTransitionTuplestore
- (afterTriggers.new_tuplestores);
- tuplestore_puttuple(new_tuplestore, newtup);
+ if (event == TRIGGER_EVENT_INSERT)
+ new_tuplestore = transition_capture->tcs_insert_tuplestore;
+ else
+ new_tuplestore = transition_capture->tcs_update_tuplestore;
+
+ if (original_insert_tuple != NULL)
+ tuplestore_puttuple(new_tuplestore, original_insert_tuple);
+ else if (map != NULL)
+ {
+ HeapTuple converted = do_convert_tuple(newtup, map);
+
+ tuplestore_puttuple(new_tuplestore, converted);
+ pfree(converted);
+ }
+ else
+ tuplestore_puttuple(new_tuplestore, newtup);
}
/* If transition tables are the only reason we're here, return. */
- if ((event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
+ if (trigdesc == NULL ||
+ (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
(event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
(event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row))
return;
@@ -5362,6 +5518,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
new_shared.ats_tgoid = trigger->tgoid;
new_shared.ats_relid = RelationGetRelid(rel);
new_shared.ats_firing_id = 0;
+ new_shared.ats_transition_capture = transition_capture;
afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth],
&new_event, &new_shared);