aboutsummaryrefslogtreecommitdiff
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.c58
1 files changed, 52 insertions, 6 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 54db16c9090..b502941b08b 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -401,6 +401,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) ||
@@ -2128,8 +2145,10 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc)
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 || trigdesc->trig_update_new_table)
- state->tcs_new_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();
{
@@ -2147,8 +2166,10 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc)
void
DestroyTransitionCaptureState(TransitionCaptureState *tcs)
{
- if (tcs->tcs_new_tuplestore != NULL)
- tuplestore_end(tcs->tcs_new_tuplestore);
+ 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);
@@ -3993,7 +4014,29 @@ AfterTriggerExecute(AfterTriggerEvent event,
if (LocTriggerData.tg_trigger->tgoldtable)
LocTriggerData.tg_oldtable = transition_capture->tcs_old_tuplestore;
if (LocTriggerData.tg_trigger->tgnewtable)
- LocTriggerData.tg_newtable = transition_capture->tcs_new_tuplestore;
+ {
+ /*
+ * 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;
+ }
}
/*
@@ -5241,7 +5284,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
Tuplestorestate *new_tuplestore;
Assert(newtup != NULL);
- new_tuplestore = transition_capture->tcs_new_tuplestore;
+ 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);