How to fire triggers just on "top" level DML

Started by Kevin Grittnerabout 15 years ago11 messagesgeneral
Jump to latest
#1Kevin Grittner
Kevin.Grittner@wicourts.gov

We've been running for about ten years on a framework which fires
triggers similar to database triggers in a Java tier close to the
database, and we're now trying to convert these to actual PostgreSQL
database triggers. Our biggest hitch at the moment is that we
defined a class of triggers we called "top" triggers, which only
fire from DML submitted by the application, not from DML issued by
other triggers.

One significant use of this is to block direct modification of
summary data (either selected columns or entire tables) which are
supposed to be trigger maintained. It's not immediately obvious how
to accomplish this within PostgreSQL, although I'm probably missing
something. We're not tied to any particular methodology -- a
TG_DEPTH variable, if it existed, would do fine, for example.

Any suggestions?

-Kevin

#2Adrian Klaver
adrian.klaver@aklaver.com
In reply to: Kevin Grittner (#1)
Re: How to fire triggers just on "top" level DML

On Wednesday 19 January 2011 1:59:48 pm Kevin Grittner wrote:

We've been running for about ten years on a framework which fires
triggers similar to database triggers in a Java tier close to the
database, and we're now trying to convert these to actual PostgreSQL
database triggers. Our biggest hitch at the moment is that we
defined a class of triggers we called "top" triggers, which only
fire from DML submitted by the application, not from DML issued by
other triggers.

One significant use of this is to block direct modification of
summary data (either selected columns or entire tables) which are
supposed to be trigger maintained. It's not immediately obvious how
to accomplish this within PostgreSQL, although I'm probably missing
something. We're not tied to any particular methodology -- a
TG_DEPTH variable, if it existed, would do fine, for example.

Any suggestions?

-Kevin

The only thing I can think of off the top of my head is to you trigger arguments
and then use the below to test:

TG_ARGV[]

Data type array of text; the arguments from the CREATE TRIGGER statement.
The index counts from 0. Invalid indexes (less than 0 or greater than or equal
to tg_nargs) result in a null value.

--
Adrian Klaver
adrian.klaver@gmail.com

#3Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Adrian Klaver (#2)
Re: How to fire triggers just on "top" level DML

Adrian Klaver <adrian.klaver@gmail.com> wrote:

TG_ARGV[]

Data type array of text; the arguments from the CREATE
TRIGGER statement.

Thanks for the suggestion, but I don't think this does what I need.
I need to know whether the trigger was *fired* from inside another
trigger, not something about the creation of trigger.

-Kevin

#4A.M.
agentm@themactionfaction.com
In reply to: Kevin Grittner (#1)
Re: How to fire triggers just on "top" level DML

On Jan 19, 2011, at 4:59 PM, Kevin Grittner wrote:

We've been running for about ten years on a framework which fires
triggers similar to database triggers in a Java tier close to the
database, and we're now trying to convert these to actual PostgreSQL
database triggers. Our biggest hitch at the moment is that we
defined a class of triggers we called "top" triggers, which only
fire from DML submitted by the application, not from DML issued by
other triggers.

One significant use of this is to block direct modification of
summary data (either selected columns or entire tables) which are
supposed to be trigger maintained. It's not immediately obvious how
to accomplish this within PostgreSQL, although I'm probably missing
something. We're not tied to any particular methodology -- a
TG_DEPTH variable, if it existed, would do fine, for example.

Any suggestions?

Most PLs include some session-specific storage. In PL/Perl, it is %_SHARED. Setting a flag there should do the trick. If you are using a PL which does not have such a notion (like plpgsql), you can add a call in your triggers to a function written in a PL which does support this. Alternatively, a C function which sets/checks a global flag would work as well.

Cheers,
M

#5Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: A.M. (#4)
Re: How to fire triggers just on "top" level DML

"A.M." <agentm@themactionfaction.com> wrote:

Most PLs include some session-specific storage. In PL/Perl, it is
%_SHARED. Setting a flag there should do the trick. If you are
using a PL which does not have such a notion (like plpgsql), you
can add a call in your triggers to a function written in a PL
which does support this. Alternatively, a C function which
sets/checks a global flag would work as well.

I thought it might come to that. I'm comfortable writing C
functions, and we're not using any languages so far besides C, SQL,
and plpgsql, so I'd probably use C. If I'm going that far, though,
I'd be rather inclined to implement a TG_DEPTH variable (as being
easier for us to use) and offer it to the community in case there's
anyone else who would find this useful. If that turns out to be
harder than I think, I'll fall back to what you outlined here.

Thanks!

-Kevin

#6A.M.
agentm@themactionfaction.com
In reply to: Kevin Grittner (#5)
Re: How to fire triggers just on "top" level DML

On Jan 19, 2011, at 5:36 PM, Kevin Grittner wrote:

"A.M." <agentm@themactionfaction.com> wrote:

Most PLs include some session-specific storage. In PL/Perl, it is
%_SHARED. Setting a flag there should do the trick. If you are
using a PL which does not have such a notion (like plpgsql), you
can add a call in your triggers to a function written in a PL
which does support this. Alternatively, a C function which
sets/checks a global flag would work as well.

I thought it might come to that. I'm comfortable writing C
functions, and we're not using any languages so far besides C, SQL,
and plpgsql, so I'd probably use C. If I'm going that far, though,
I'd be rather inclined to implement a TG_DEPTH variable (as being
easier for us to use) and offer it to the community in case there's
anyone else who would find this useful. If that turns out to be
harder than I think, I'll fall back to what you outlined here.

If you do implement TG_DEPTH, I am curious as to what the difference between TG_DEPTH==34 and TG_DEPTH==35 could mean. I think it might cause poor coding practice in making decisions based off assumed trigger order execution. Since you only care to distinguish between depth 1 and depth 2 (and not beyond), could you elaborate on a use case where further trigger "depth" information may be useful?

Cheers,
M

#7Adrian Klaver
adrian.klaver@aklaver.com
In reply to: Kevin Grittner (#3)
Re: How to fire triggers just on "top" level DML

On Wednesday 19 January 2011 2:20:35 pm Kevin Grittner wrote:

Adrian Klaver <adrian.klaver@gmail.com> wrote:

TG_ARGV[]

Data type array of text; the arguments from the CREATE
TRIGGER statement.

Thanks for the suggestion, but I don't think this does what I need.
I need to know whether the trigger was *fired* from inside another
trigger, not something about the creation of trigger.

-Kevin

I misunderstood I thought there where 'top' triggers and other triggers and
using arguments would be way to tag them.

--
Adrian Klaver
adrian.klaver@gmail.com

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Kevin Grittner (#5)
Re: How to fire triggers just on "top" level DML

"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:

"A.M." <agentm@themactionfaction.com> wrote:

Most PLs include some session-specific storage. In PL/Perl, it is
%_SHARED. Setting a flag there should do the trick. If you are
using a PL which does not have such a notion (like plpgsql), you
can add a call in your triggers to a function written in a PL
which does support this. Alternatively, a C function which
sets/checks a global flag would work as well.

I thought it might come to that.

Correctly resetting such a value after a transaction/subtransaction
abort might be a bit problematic.

regards, tom lane

#9Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: A.M. (#6)
Re: How to fire triggers just on "top" level DML

"A.M." <agentm@themactionfaction.com> wrote:

If you do implement TG_DEPTH, I am curious as to what the
difference between TG_DEPTH==34 and TG_DEPTH==35 could mean. I
think it might cause poor coding practice in making decisions
based off assumed trigger order execution. Since you only care to
distinguish between depth 1 and depth 2 (and not beyond), could
you elaborate on a use case where further trigger "depth"
information may be useful?

Well, our current trigger engine in the Java tier keeps a count like
that and has a depth limit to protect against runaway recursion.
The only time we hit it was in purge logic, which is extremely
convoluted due to requirements of statutes, supreme court rules, and
auditor requirements. :-/ I might just put some sort of depth
check into triggers fired during purges, to provide a more
informative message than I might otherwise get.

-Kevin

#10Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Tom Lane (#8)
Re: How to fire triggers just on "top" level DML

Tom Lane <tgl@sss.pgh.pa.us> wrote:

Correctly resetting such a value after a
transaction/subtransaction abort might be a bit problematic.

Good point. Definitely an area to pay particularly close attention.

Thanks,

-Kevin

In reply to: Kevin Grittner (#1)
Re: How to fire triggers just on "top" level DML

Any suggestions?

you might find this approach useful:

http://www.depesz.com/index.php/2008/01/18/how-to-check-if-given-update-is-from-trigger-or-why-i-hate-orms/

Best regards,

depesz

--
Linkedin: http://www.linkedin.com/in/depesz / blog: http://www.depesz.com/
jid/gtalk: depesz@depesz.com / aim:depeszhdl / skype:depesz_hdl / gg:6749007