## Code Analysis ### Trigger execution runs before the insert source returns the row `server/analyzer/assign_triggers.go:50-61` ```go if len(beforeTrigs) > 0 { handling := getTriggerRowHandling(node) newNode, err = nodeWithTriggers(ctx, newNode, &pgnodes.TriggerExecution{ Timing: triggers.TriggerTiming_Before, Triggers: beforeTrigs, Source: getTriggerSource(node), Runner: pgexprs.StatementRunner{Runner: a.Runner}, TgOp: getTriggerOperation(node), }) } ``` The INSERT is wrapped in a `TriggerExecution` node with `Timing: Before`, and the trigger body uses the statement runner supplied by the analyzer. `server/node/trigger_execution.go:77-116` ```go sourceIter, err := b.Build(ctx, te.Source, r) // ... return &triggerExecutionIter{ functions: trigFuncs, runner: te.Runner.Runner, source: sourceIter, timing: te.Timing, }, nil ``` `server/node/trigger_execution.go:222-265` ```go nextRow, err := t.source.Next(ctx) // ... returnedValue, err := plpgsql.TriggerCall(ctx, function, t.runner, t.sch, oldRow, newRow, t.trigVars[funcIdx]) if err != nil { return nil, err } ``` The iterator calls the BEFORE trigger function while processing the source row and returns an error from the trigger path before the row is returned to the insert operation. This is the relevant production path for the observed separation between the rejected target row and the trigger body's audit write. ### Observed execution ```text $ psql -X -h 127.0.0.1 -p 5432 -U postgres -d postgres --- FAILING DML --- --- AUDIT AFTER FAILURE --- ERROR: Check constraint "target_chk_k0nitsv0" violated event_id | op | row_id | row_amount ----------+--------+--------+------------ 1 | INSERT | 1 | -1 (1 row) --- TARGET AFTER FAILURE --- id | amount ----+-------- (0 rows) --- VALID DML --- INSERT 0 1 --- AUDIT AFTER VALID DML --- event_id | op | row_id | row_amount ----------+--------+--------+------------ 1 | INSERT | 1 | -1 2 | INSERT | 1 | 10 (2 rows) --- TARGET AFTER VALID DML --- id | amount ----+-------- 1 | 10 (1 row) ``` ### Result The failed INSERT returned the CHECK constraint error and left the target table empty, but its BEFORE INSERT trigger left a completed audit row for `amount = -1`. The later valid INSERT added a second audit row, providing runtime evidence of inconsistent trigger side effects after failed DML.