Making AFTER triggers act properly in PL functions - Mailing list pgsql-hackers

From Tom Lane
Subject Making AFTER triggers act properly in PL functions
Date
Msg-id 2850.1094583837@sss.pgh.pa.us
Whole thread Raw
Responses Re: Making AFTER triggers act properly in PL functions  (Stephan Szabo <sszabo@megazone.bigpanda.com>)
List pgsql-hackers
Here's another thing that I think it would be good to fix in 8.0.
We've had numerous complaints before about RI constraints not firing
inside PL functions; a recent one is
http://archives.postgresql.org/pgsql-bugs/2004-09/msg00020.php

I think also that the current behavior violates the SQL spec.
SQL99 4.17 says:
        The checking of a constraint depends on its constraint mode within        the current SQL-transaction. If the
constraintmode is immediate,        then the constraint is effectively checked at the end of each SQL-
statement.
        NOTE 13 - This includes SQL-statements that are executed as a        direct result or an indirect result of
executinga different SQL-        statement.
 

The NOTE is a bit opaque but I think it has to be taken as meaning that
if a PL function contains DML commands then the results of those DML
commands are checked when they complete, not when the outermost statement
completes.

Therefore, I think the behavior we want is that immediate-mode AFTER
triggers fire at completion of the statement that queued them, no matter
whether this was an interactive statement or one inside a function.
Deferred triggers, as now, fire only at end of top-level transaction
(or when changed to immediate mode by SET CONSTRAINTS).  Changing this
obviously risks breaking existing applications, but we'll have to do it
eventually.  8.0 seems like the appropriate time.

Implementation proposal:

I was originally thinking we could just invoke DeferredTriggerEndQuery
after each query is finished, but that doesn't work.  Imagine the
situation where a PL function is being called repeatedly by an UPDATE
query (which is generating trigger events), and the PL function is doing
queries that themselves fire triggers.  We want completion of a query
within the PL function to fire only those events queued by that query,
not events already queued by the outer UPDATE.  The outer UPDATE's events
should be processed only when it finishes.

To handle this, I think we need to introduce a DeferredTriggerBeginQuery
function to match DeferredTriggerEndQuery, and make SPI and other callers
of the executor essentially do
DeferredTriggerBeginQuery()ExecutorStart()ExecutorRun()ExecutorEnd()DeferredTriggerEndQuery()

trigger.c will have to be prepared to cope with nested begin/end query
calls.  I envision it working like this:

* BeginQuery establishes a new, empty list of AFTER triggers for the
new query.  This is pushed onto a stack of active queries for the current
(sub)transaction.

* In the executor, any request to queue an AFTER trigger event queues it
onto the topmost query's list.  (Error if no active query, so you MUST
have done DeferredTriggerBeginQuery.)

* EndQuery processes and discards immediate-mode AFTER trigger events for the
current query.  Any remaining events (ie, DEFERRED triggers) are appended
to the current (sub)transaction's list of pending deferred triggers.
Note that even inside a subtransaction, we can discard immediate-mode
events.

* EndSubXact, in the commit case, expects the stack of open queries for
the current subxact to be empty (error if not); in the abort case, pop
and discard any open queries, along with any deferred triggers of the
subxact.

* EndXact and DeferredTriggerSetState continue to act the same as before.
In particular, DeferredTriggerSetState need pay no attention to trigger
events that are still in lists belonging to open queries; those events
aren't ready to fire yet.

Comments?
        regards, tom lane


pgsql-hackers by date:

Previous
From: Stephan Szabo
Date:
Subject: Re: Breakage in trigger.c
Next
From: Stephan Szabo
Date:
Subject: Re: Making AFTER triggers act properly in PL functions