Thread: feature proposal - triggers by semantics

feature proposal - triggers by semantics

From
Darren Duncan
Date:
I have a feature request, which at one level should require little code change, 
but at another level may require more.

Since Postgres 9.3 is going to be doing some significant feature additions for 
triggers, I'd like to see some more.

As they currently exist, triggers always fire based on certain SQL syntax used, 
rather than on the semantics of what is actually going on.

I would like to see a new class of triggers that fire when particular database 
operations happen regardless of what SQL syntax was used.

As a simple example, I'd like to be able to define a trigger like "AFTER DELETE 
ON foo FOR EACH ROW" and have that trigger be invoked not only by a DELETE on 
foo but also by a TRUNCATE on foo.  So I would like to do some auditing action 
when a row of foo is deleted, no matter how it happens.

The reason this particular example in particular is important is that TRUNCATE 
is documented as a data-manipulation action semantically equivalent to an 
unqualified DELETE in its effects, primarily.  As such, I would expect the same 
triggers to fire as would for an unqualified DELETE.

The reason I propose it be a new kind of trigger is so that then we also retain 
the ability to declare triggers that fire on DELETE and not on TRUNCATE.

Less important, but also nice at least from the ability to be less verbose, is 
that said trigger could also run when an UPDATE happens, optionally, since an 
UPDATE can be considered semantically a DELETE+INSERT.  But adding the TRUNCATE 
support is most important because it simply doesn't exist now, while UPDATE you 
can get just by adding "or update".

I suggest that the simplest way to add this feature is to just extend the 
existing syntax for defining a FOR EACH ROW so that TRUNCATE is also an option, 
besides INSERT/UPDATE/DELETE.

In that case, the semantics of the TRUNCATE statement could be altered as 
follows:  Iff "TRUNCATE foo" is invoked and foo has an "TRUNCATE FOR EACH ROW" 
trigger defined on it, then an unqualified "DELETE FROM foo" will be performed 
instead with its usual semantics.  If such a trigger is not defined on foo, then 
the old TRUNCATE semantics happen.

As such, this case of the feature can be added without breaking anything legacy.

So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR EACH ROW", 
but I'm also proposing the ability to generally define triggers based not on the 
syntax used but the actual action requested.

A tangential feature request is to provide a runtime config option that can 
cause TRUNCATE to always behave as unqualified DELETE FROM regardless of any 
triggers, as if it were just a syntactic shorthand.  Or alternately/also provide 
extra syntax to TRUNCATE itself where one can specify which behavior to have, 
and both options can be given explicitly to override any config option.

-- Darren Duncan




Re: feature proposal - triggers by semantics

From
Craig Ringer
Date:
On 11/15/2012 03:44 PM, Darren Duncan wrote:
> As they currently exist, triggers always fire based on certain SQL
> syntax used, rather than on the semantics of what is actually going on.
>
That's not quite right. COPY fires INSERT triggers, despite never using
an explicit INSERT statement. There are probably other examples.
> As a simple example, I'd like to be able to define a trigger like
> "AFTER DELETE ON foo FOR EACH ROW" and have that trigger be invoked
> not only by a DELETE on foo but also by a TRUNCATE on foo.  So I would
> like to do some auditing action when a row of foo is deleted, no
> matter how it happens.
TRUNCATE triggers already exist.

If you wanted you could have a BEFORE TRUNCATE trigger SELECT each row
and invoke a function on each row, same as a FOR EACH ROW .. DELETE
trigger. So you can already achieve what you want to do with the
currently available features.

> The reason this particular example in particular is important is that
> TRUNCATE is documented as a data-manipulation action semantically
> equivalent to an unqualified DELETE in its effects, primarily.
I'd say we should just change the docs to note that "TRUNCATE does not
act on each row individually, so it does not fire DELETE triggers. There
is a TRUNCATE trigger you can use to monitor and control TRUNCATE
behaviour."

>
> So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR
> EACH ROW", but I'm also proposing the ability to generally define
> triggers based not on the syntax used but the actual action requested.
That already exists. Maybe you're using some old version?

regress=> \h CREATE TRIGGER
Command:     CREATE TRIGGER
Description: define a new trigger
Syntax:
CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } {
event [ OR ... ] }   ON table_name
...
where event can be one of:
   INSERT   UPDATE [ OF column_name [, ... ] ]   DELETE   TRUNCATE


>
> A tangential feature request is to provide a runtime config option
> that can cause TRUNCATE to always behave as unqualified DELETE FROM
> regardless of any triggers, as if it were just a syntactic shorthand.
Please, no!

If you want to prevent TRUNCATE, deny the privilege or add a trigger
that aborts the command.

TRUNCATE should do what it says, not magically be changed to do
something else by a GUC.
> Or alternately/also provide extra syntax to TRUNCATE itself where one
> can specify which behavior to have, and both options can be given
> explicitly to override any config option.
What advantage would that have over using DELETE when you want DELETE,
and TRUNCATE when you want TRUNCATE?

-- Craig Ringer                   http://www.2ndQuadrant.com/PostgreSQL Development, 24x7 Support, Training & Services




Re: feature proposal - triggers by semantics

From
Michael Paquier
Date:
A row-level trigger for TRUNCATE does not really make sense, as it would mean that TRUNCATE needs to scan each tuple of
thetable it needs to interact with to fire its trigger, so it would more or less achieve the same performance as a
plain"DELETE FROM table;".<br /><br />TRUNCATE is performant because it only removes the tuples, and does not care
aboutscanning. I am also not sure it is meant for scanning (btw there are discussions about TRUNCATE these days so I
mightbe missing something).<br />So, what you are looking for can be simply solved with row-level triggers on DELETE,
sowhy not using that and avoid reinventing the wheel?<br />-- <br />Michael Paquier<br /><a
href="http://michael.otacoo.com"target="_blank">http://michael.otacoo.com</a><br /> 

Re: feature proposal - triggers by semantics

From
Hannu Krosing
Date:
On 11/15/2012 09:48 AM, Craig Ringer wrote:
> If you want to prevent TRUNCATE, deny the privilege or add a trigger
> that aborts the command.
You can abort the transaction but not skip action as currently it is only
possible to skip in ROW level triggers.

So I'd modify this request to allow BEFORE EACH STATEMENT triggers
to also be able to silently skip current action like BEFORE EACH ROW 
triggers can.

Then this request would simply be satisfied by a simple trigger which
rewrites TRUNCATE into DELETE .

Hannu

>
> TRUNCATE should do what it says, not magically be changed to do
> something else by a GUC.
>> Or alternately/also provide extra syntax to TRUNCATE itself where one
>> can specify which behavior to have, and both options can be given
>> explicitly to override any config option.
> What advantage would that have over using DELETE when you want DELETE,
> and TRUNCATE when you want TRUNCATE?
>




Re: feature proposal - triggers by semantics

From
Craig Ringer
Date:
On 11/15/2012 06:25 PM, Hannu Krosing wrote:
> On 11/15/2012 09:48 AM, Craig Ringer wrote:
>> If you want to prevent TRUNCATE, deny the privilege or add a trigger
>> that aborts the command.
> You can abort the transaction but not skip action as currently it is only
> possible to skip in ROW level triggers.
>
> So I'd modify this request to allow BEFORE EACH STATEMENT triggers
> to also be able to silently skip current action like BEFORE EACH ROW
> triggers can.
>
> Then this request would simply be satisfied by a simple trigger which
> rewrites TRUNCATE into DELETE .
That seems sensible to me, too.

-- Craig Ringer                   http://www.2ndQuadrant.com/PostgreSQL Development, 24x7 Support, Training & Services




Re: feature proposal - triggers by semantics

From
Darren Duncan
Date:
Craig Ringer wrote:
> On 11/15/2012 06:25 PM, Hannu Krosing wrote:
>> On 11/15/2012 09:48 AM, Craig Ringer wrote:
>>> If you want to prevent TRUNCATE, deny the privilege or add a trigger
>>> that aborts the command.
>> You can abort the transaction but not skip action as currently it is only
>> possible to skip in ROW level triggers.
>>
>> So I'd modify this request to allow BEFORE EACH STATEMENT triggers
>> to also be able to silently skip current action like BEFORE EACH ROW
>> triggers can.
>>
>> Then this request would simply be satisfied by a simple trigger which
>> rewrites TRUNCATE into DELETE .
> That seems sensible to me, too.

To further explain ...

What I'm desiring here with respect to TRUNCATE is that users are allowed to use 
TRUNCATE syntax as an alternative to DELETE (they are GRANTed both) but that any 
triggers defined for DELETE can also be applied to TRUNCATE without too much 
difficulty.  So users can use different syntactic constructs that look similar 
without having to think about, is this going to be audited.

I understand that ROW level triggers don't exist yet for TRUNCATE (this is 
already clearly documented in the manual) and adding them would mean TRUNCATE 
would do a table scan in their presence.

I still think the syntax of TRUNCATE FOR EACH ROW would be useful, but if no one 
agrees, then I'll just make do with alternative solutions mentioned here.  That 
is, either a statement-level TRUNCATE trigger of some kind, or simply disallow 
TRUNCATE privileges.

Thank you.

-- Darren Duncan



Re: feature proposal - triggers by semantics

From
Christopher Browne
Date:
On Thu, Nov 15, 2012 at 2:53 PM, Darren Duncan <darren@darrenduncan.net> wrote:
> I still think the syntax of TRUNCATE FOR EACH ROW would be useful, but if no one agrees...

I'm compelled to disagree.

What was useful about TRUNCATE in the first place was that it quickly operated against the entire table.

If you want to change that to row-by-row processing, then it is actually a little bit worse than
DELETE FROM some_table, in that it is introducing an irregularity in language that no longer
provides any corresponding benefit.  (e.g. - such as that TRUNCATE is fast).

If you want to be certain of doing row-by-row processing, then the better answer is to:
a) Use DELETE instead in your application, and
b) Put a guard on to prevent using TRUNCATE.  (e.g. - attach triggers that react to TRUNCATE with "go away, don't bother me!")

I observe that the Slony replication system initially implemented that 'guard' approach when TRUNCATE
triggers were first provided, until we came up with a suitable strategy to capture and replicate the
TRUNCATE request.
--
When confronted by a difficult problem, solve it by reducing it to the
question, "How would the Lone Ranger handle this?"

Re: feature proposal - triggers by semantics

From
Dimitri Fontaine
Date:
Darren Duncan <darren@darrenduncan.net> writes:
> So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR EACH
> ROW"

Kevin has been proposing that we consider an alternative approach in
some other cases that I think would work better for you, too. Namely, to
have access to OLD and NEW in FOR EACH STATEMENT triggers, where they
would be relations rather than records.

Regards,
-- 
Dimitri Fontaine
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support



Re: feature proposal - triggers by semantics

From
Darren Duncan
Date:
Dimitri Fontaine wrote:
> Darren Duncan <darren@darrenduncan.net> writes:
>> So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR EACH
>> ROW"
> 
> Kevin has been proposing that we consider an alternative approach in
> some other cases that I think would work better for you, too. Namely, to
> have access to OLD and NEW in FOR EACH STATEMENT triggers, where they
> would be relations rather than records.
> 
> Regards,

Yes, I believe that would work very well.  In fact, that would provide some 
power features.  I look forward to this, probably the best solution. -- Darren 
Duncan



Re: feature proposal - triggers by semantics

From
Craig Ringer
Date:
On 11/16/2012 05:38 AM, Dimitri Fontaine wrote:
> Darren Duncan <darren@darrenduncan.net> writes:
>> So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR EACH
>> ROW"
> Kevin has been proposing that we consider an alternative approach in
> some other cases that I think would work better for you, too. Namely, to
> have access to OLD and NEW in FOR EACH STATEMENT triggers, where they
> would be relations rather than records.

That would be an exceedingly useful feature. On the project I was
working on earlier I'd immediately rewrite quite a few of the triggers
if it became available, with results I'd expect to be both faster and
simpler.

I can see using the same facility to optimise foreign key validity
checks in the absence of an index on the foreign key column, too.

-- Craig Ringer                   http://www.2ndQuadrant.com/PostgreSQL Development, 24x7 Support, Training & Services




Re: feature proposal - triggers by semantics

From
Michael Paquier
Date:


On Fri, Nov 16, 2012 at 6:38 AM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Darren Duncan <darren@darrenduncan.net> writes:
> So, I'm partly proposing a specific narrow new feature, "TRUNCATE FOR EACH
> ROW"

Kevin has been proposing that we consider an alternative approach in
some other cases that I think would work better for you, too. Namely, to
have access to OLD and NEW in FOR EACH STATEMENT triggers, where they
would be relations rather than records.
+1
--
Michael Paquier
http://michael.otacoo.com

Re: feature proposal - triggers by semantics

From
Simon Riggs
Date:
On 15 November 2012 05:25, Hannu Krosing <hannu@2ndquadrant.com> wrote:
> On 11/15/2012 09:48 AM, Craig Ringer wrote:
>>
>> If you want to prevent TRUNCATE, deny the privilege or add a trigger
>> that aborts the command.
>
> You can abort the transaction but not skip action as currently it is only
> possible to skip in ROW level triggers.
>
> So I'd modify this request to allow BEFORE EACH STATEMENT triggers
> to also be able to silently skip current action like BEFORE EACH ROW
> triggers can.

+1

> Then this request would simply be satisfied by a simple trigger which
> rewrites TRUNCATE into DELETE .

However, we don't need to do that to make this work.

Just create a BEFORE EACH STATEMENT trigger on TRUNCATE that issues a
DELETE from table.
Then have a EACH ROW trigger on DELETE.

That way each row gets logged.

The TRUNCATE still occurs, but semantically does nothing, though
physically deletes the space used by the already deleted rows.

-- Simon Riggs                   http://www.2ndQuadrant.com/PostgreSQL Development, 24x7 Support, Training & Services