Command Triggers

Started by Dimitri Fontaineover 14 years ago135 messageshackers
Jump to latest
#1Dimitri Fontaine
dimitri@2ndQuadrant.fr

Hi,

As proposed by Jan Wieck on hackers and discussed in Ottawa at the
Clusters Hackers Meeting, the most (only?) workable way to ever have DDL
triggers is to have "command triggers" instead.

Rather than talking about catalogs and the like, it's all about firing a
registered user-defined function before or after processing the utility
command, or instead of processing it. This naturally involves a new
catalog (pg_cmdtrigger) and some new subcommands of CREATE and DROP
TRIGGER, and some support code for calling the function at the right
time.

The right place to hook this code in seems to be ProcessUtility(), which
is a central place for about all the commands that we handle. An
exception to that rule would be SELECT INTO and CREATE TABLE AS SELECT,
and my proposal would be to add specific call sites to the functions
I've provided in the attached patch rather than try to contort them into
being a good citizen as a utility command.

The ProcessUtility() integration currently looks like that:

const char *commandTag = CreateCommandTag(parsetree);
if (ExecBeforeOrInsteadOfCommandTriggers(parsetree, commandTag) > 0)
return;

... current code ...

ExecAfterCommandTriggers(parsetree, commandTag);

So I guess adding some call sites is manageable.

Now, the patch contains a Proof Of Concept implementation with a small
level of documentation and regression tests in order to get an agreement
on the principles that we already discussed.

The other part of the command trigger facility is what information we
should give to the user-defined functions, and in which form. I've
settled on passing always 4 text arguments: the command string, the
parse tree as a nodeToString() representation (Jan believes that this is
the easiest form we can provide for code consumption, and I tend to
agree), the schema name or NULL if the targeted object of the command is
not qualified, and the object name (or NULL if that does not apply).

CREATE FUNCTION cmdtrigger_notice
(
IN cmd_string text,
IN cmd_nodestring text,
IN schemaname text,
IN relname text
)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
RAISE NOTICE 'cmd_string: %', cmd_string;
END;
$$;

CREATE TRIGGER cmdtrigger_notice
AFTER COMMAND CREATE TABLE
EXECUTE PROCEDURE cmdtrigger_notice();

The v1 patch attached contains implementation for some commands only.
We need to add rewriting facilities for those commands we want to
support in the proposed model, because of multi-queries support in the
protocol and dynamic queries in EXECUTE e.g. (though I admit not having
had a look at EXECUTE implementation).

So each supported command has a cost, and the ones I claim to support in
the grammar in the patch are not seeing a complete support: first, I'm
missing some outfuncs and readfuncs (but I believe we can complete that
using a script on the source code) so that the cmd_nodestring argument
is currently always NULL. Second, I didn't complete the rewriting of
some more complex commands such as CREATE TABLE and ALTER TABLE.

Note that we can't reuse that much of ruleutils.c because it's written
to work from what we store in the catalogs rather than from a parsetree
object.

So, any comment on the approach before I complete the rewriting of the
commands, the out/read funcs, and add more commands to the trigger
support code?

https://github.com/dimitri/postgres/compare/master...command_triggers

$ git diff --stat master..
doc/src/sgml/ref/create_trigger.sgml | 97 +++++-
doc/src/sgml/ref/drop_trigger.sgml | 19 +-
src/backend/catalog/Makefile | 2 +-
src/backend/catalog/dependency.c | 47 ++-
src/backend/catalog/objectaddress.c | 8 +
src/backend/commands/Makefile | 4 +-
src/backend/commands/cmdtrigger.c | 580 ++++++++++++++++++++++++++
src/backend/nodes/copyfuncs.c | 32 ++
src/backend/nodes/equalfuncs.c | 28 ++
src/backend/nodes/outfuncs.c | 405 ++++++++++++++++++
src/backend/parser/gram.y | 70 +++-
src/backend/tcop/utility.c | 44 ++
src/backend/utils/adt/ruleutils.c | 613 +++++++++++++++++++++++++++-
src/include/catalog/dependency.h | 1 +
src/include/catalog/indexing.h | 5 +
src/include/catalog/pg_cmdtrigger.h | 59 +++
src/include/commands/cmdtrigger.h | 43 ++
src/include/commands/defrem.h | 14 +
src/include/nodes/nodes.h | 2 +
src/include/nodes/parsenodes.h | 28 ++
src/include/parser/kwlist.h | 1 +
src/test/regress/expected/sanity_check.out | 3 +-
src/test/regress/expected/triggers.out | 35 ++
src/test/regress/sql/triggers.sql | 35 ++
24 files changed, 2157 insertions(+), 18 deletions(-)

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

Attachments:

command-trigger.v1.patchtext/x-patchDownload+2157-18
#2Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

As proposed by Jan Wieck on hackers and discussed in Ottawa at the
Clusters Hackers Meeting, the most (only?) workable way to ever have DDL
triggers is to have "command triggers" instead.

[...]

The v1 patch attached contains implementation for some commands only.

Here's a v2 which is only about cleaning up the merge from master. The
recent DROP rework conflicted badly with the command triggers patch,
quite obviously. I didn't reimplement DROP COMMAND TRIGGER in terms of
the new facilities yet, though.

So, any comment on the approach before I complete the rewriting of the
commands, the out/read funcs, and add more commands to the trigger
support code?

https://github.com/dimitri/postgres/compare/master...command_triggers

FWIW (scheduling mainly), I don't think I'm going to spend more time on
this work until I get some comments, because all that remains to be done
is about building on top of what I've already been doing here.

Well, I'd like to implement command triggers on replica too, but baring
psql support and a bunch of commands not there yet, that's about it as
far as features go.

Oh, and have expressions rewriting from the parsetree (default, check)
actually work (rather than segfault) is still a TODO too.

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

Attachments:

command-trigger.v2.patchtext/x-patchDownload+2678-18
#3Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers

Hi Dimitri, Hi all,

On Tuesday, November 08, 2011 06:47:13 PM Dimitri Fontaine wrote:

As proposed by Jan Wieck on hackers and discussed in Ottawa at the
Clusters Hackers Meeting, the most (only?) workable way to ever have DDL
triggers is to have "command triggers" instead.
Rather than talking about catalogs and the like, it's all about firing a
registered user-defined function before or after processing the utility
command, or instead of processing it. This naturally involves a new
catalog (pg_cmdtrigger) and some new subcommands of CREATE and DROP
TRIGGER, and some support code for calling the function at the right
time.

Great ;)

fwiw I think thats the way forward as well.

The patch obviously isn't thought to be commitable yet, so I am not going to
do a very detailed code level review. Attached is a patch with low level
targeted comments and such I noticed while reading it.

At this state the important things are highlevel so I will try to concentrate
on those:

== the trigger function ==
I don't like the set of options parsed to the trigger functions very much. To
cite an example of yours those currently are:
* cmd_string text
* cmd_nodestring text
* schemaname text
* relname text

I think at least a
* command_tag text
is missing. Sure, you can disambiguate it by creating a function for every
command type but that seems pointlessly complex for many applications. And
adding it should be trivial as its already tracked ;)

Currently the examples refer to relname as relname which I dislike as that
seems to be too restricted to one use case. The code is already doing it
correctly (as objectname) though ;)

Why is there explicit documentation of not checking the arguments of said
trigger function? That seems to be a bit strange to me.

schemaname currently is mightily unusable because whether its sent or not
depends wether the table was created with a schemaname specified or not. That
doesn't seem to be a good approach.

That brings me to the next point:

== normalization of statements ==
Currently the normalization is a bit confusing. E.g. for:

CREATE SCHEMA barf;
SET search_path = barf;
CREATE TYPE bart AS ENUM ('a', 'b');
CREATE TABLE bar(a int, b bigint, c serial, d text, e varchar, "F" text, g
bart, h int4);

one gets

CREATE TABLE bar (a pg_catalog.int4,b pg_catalog.int8,c serial,d text,e
pg_catalog.varchar,F text,g bart,h int4);

Which points out two problems:
* inconsistent schema prefixing
* no quoting

Imo the output should fully schema qualify anything including operators. Yes,
thats rather ugly but anything else seems to be too likely to lead to
problems.

As an alternative it would be possible to pass the current search path but
that doesn't seem to the best approach to me;

Currently CHECK() constraints are not decodable, but inside those the same
quoting/qualifying rules should apply.

Then there is nice stuff like CREATE TABLE .... (LIKE ...);
What shall that return in future? I vote for showing it as the plain CREATE
TABLE where all columns are specified.

That touches another related topic. Dim's work in adding support for utility
cmd support in nodeToString and friends possibly will make the code somewhat
command trigger specific. Is there any other usage we envision?

== grammar ==

* multiple actions:
I think it would sensible to allow multiple actions on which to trigger to be
specified just as you can for normal triggers. I also would like an ALL option

* options:
Currently the grammar allows options to be passed to command triggers. Do we
want to keep that? If yes, with the same arcane set of datatypes as in data
triggers?
If yes it needs to be wired up.

* schema qualification:
An option to add triggers only to a specific schema would be rather neat for
many scenarios.
I am not totally sure if its worth the hassle of defining what happens in the
edge cases of e.g. moving from one into another schema though.

== locking ==

Currently there is no locking strategy at all. Which e.g. means that there is
no protection against two sessions changing stuff concurrently or such.

Was that just left out - which is ok - or did you miss that?

I think it would be ok to just always grab row level locks there.

== permissions ==

Command triggers should only be allowed for the database owner.

== recursion ==
I contrast to data triggers command triggers cannot change what is done. They
can either prevent it from running or not. Which imo is fine.
The question I have is whether it would be a good idea to make it easy to
prevent recursion.... Especially if the triggers wont be per schema.

== calling the trigger ==

Imo the current callsite in ProcessUtility isn't that good. I think it would
make more sense moving it into standard_ProcessUtility. It should be *after*
the check_xact_readonly check in there in my opinion.

I am also don't think its a good idea to run the
ExecBeforeOrInsteadOfCommandTriggers stuff there because thats allso the path
that COMMIT/BEGIN and other stuff take. Those are pretty "fast path".

I suggest making two switches in standard_ProcessUtility, one for the non-
command trigger stuff which returns on success and one after that. Only after
the first triggers are checked.

I wonder whether its the right choice to run triggers on untransformed input.
I.e. CREATE TABLE ... (LIKE ...) seems to only make sense to me after
transforming.

Also youre very repeatedly transforming the parstree into a string. It would
be better doing that only once instead of every trigger...

Ok, thats the first round of high level stuff...

Cool Work!

Some lower level annotations:

* many functions should be static but are not. Especially in cmdtrigger.c
* Either tgenable's datatype or its readfunc is wrong (watch the compiler ;))
* ruleutils.c:
* generic routine for IF EXISTS, CASCADE, ...

Greetings,

Andres

Attachments:

0001-Fix-dependency-recording-for-command-triggers.patchtext/x-patch; charset=UTF-8; name=0001-Fix-dependency-recording-for-command-triggers.patchDownload+4-8
0002-Fix-file-headers-of-new-cmdtrigger.c-file.patchtext/x-patch; charset=UTF-8; name=0002-Fix-file-headers-of-new-cmdtrigger.c-file.patchDownload+4-6
0003-Add-some-review-comments.patchtext/x-patch; charset=UTF-8; name=0003-Add-some-review-comments.patchDownload+30-11
#4Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers

Hi,

On Tuesday, November 08, 2011 06:47:13 PM Dimitri Fontaine wrote:

exception to that rule would be SELECT INTO and CREATE TABLE AS SELECT,
and my proposal would be to add specific call sites to the functions
I've provided in the attached patch rather than try to contort them into
being a good citizen as a utility command.

I would very much prefer to make them utility commands and rip out the
executor support for that.
It doesn't look that complicated and would allow us to get rid of the
partially duplicated defineRelation and related stuff from the executor where
its violating my aestetic feelings ;)

Is there something making that especially hard that I overlooked? The
infrastructure for doing so seems to be there.

Andres

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#4)
Re: Command Triggers

Andres Freund <andres@anarazel.de> writes:

On Tuesday, November 08, 2011 06:47:13 PM Dimitri Fontaine wrote:

exception to that rule would be SELECT INTO and CREATE TABLE AS SELECT,
and my proposal would be to add specific call sites to the functions
I've provided in the attached patch rather than try to contort them into
being a good citizen as a utility command.

I would very much prefer to make them utility commands and rip out the
executor support for that.
Is there something making that especially hard that I overlooked? The
infrastructure for doing so seems to be there.

Well, I think the main problem is going to be shunting the query down
the right parsing track (SELECT versus utility-statement) early enough.
Making this work cleanly would be a bigger deal than I think you're
thinking.

But yeah, it would be nicer if the executor didn't have to worry about
this. +1 if you're willing to take on the work.

regards, tom lane

#6Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#5)
Re: Command Triggers

On Thursday, December 01, 2011 07:21:25 PM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On Tuesday, November 08, 2011 06:47:13 PM Dimitri Fontaine wrote:

exception to that rule would be SELECT INTO and CREATE TABLE AS SELECT,
and my proposal would be to add specific call sites to the functions
I've provided in the attached patch rather than try to contort them into
being a good citizen as a utility command.

I would very much prefer to make them utility commands and rip out the
executor support for that.
Is there something making that especially hard that I overlooked? The
infrastructure for doing so seems to be there.

Well, I think the main problem is going to be shunting the query down
the right parsing track (SELECT versus utility-statement) early enough.
Making this work cleanly would be a bigger deal than I think you're
thinking.

Yes. I forgot how ugly SELECT INTO is...

But yeah, it would be nicer if the executor didn't have to worry about
this. +1 if you're willing to take on the work.

I won't promise anything but I will play around with the grammar and see if I
find a nice enough way.

Andres

#7Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#5)
Re: Command Triggers

On Thursday, December 01, 2011 07:21:25 PM Tom Lane wrote:

Well, I think the main problem is going to be shunting the query down
the right parsing track (SELECT versus utility-statement) early enough.
Making this work cleanly would be a bigger deal than I think you're
thinking.

Obviously that depends on the definition of clean...

Changing the grammar to make that explicit seems to involve a bit too many
changes on a first glance. The cheap way out seems to be to make the decision
in analyze.c:transformQuery.
Would that be an acceptable way forward?

Andres

#8Simon Riggs
simon@2ndQuadrant.com
In reply to: Dimitri Fontaine (#2)
Re: Command Triggers

On Sat, Nov 26, 2011 at 7:20 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:

FWIW (scheduling mainly), I don't think I'm going to spend more time on
this work until I get some comments, because all that remains to be done
is about building on top of what I've already been doing here.

+1 overall

One of the main use cases for me is the ability to
* prevent all commands
* prevent extension commands - to maintain stable execution environment

Those are especially important because in 9.2 DDL commands will cause
additional locking overheads, so preventing DDL will be essential to
keeping performance stable in high txn rate databases.

So I'd like to see a few more triggers that work out of the box for
those cases (perhaps wrapped by a function?). It would also allow a
more useful man page example of how to use this.

On the patch, header comment for cmdtrigger.c needs updating.
Comments for DropCmdTrigger etc look like you forgot to update them
after cut and paste.

Comments say "For any given command tag, you can have either Before
and After triggers, or
Instead Of triggers, not both.", which seems like a great thing to
put in the docs.

Looks good.

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

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#7)
Re: Command Triggers

Andres Freund <andres@anarazel.de> writes:

On Thursday, December 01, 2011 07:21:25 PM Tom Lane wrote:

Making this work cleanly would be a bigger deal than I think you're
thinking.

Obviously that depends on the definition of clean...

Changing the grammar to make that explicit seems to involve a bit too many
changes on a first glance. The cheap way out seems to be to make the decision
in analyze.c:transformQuery.
Would that be an acceptable way forward?

ITYM transformStmt, but yeah, somewhere around there is probably reasonable.
The way I'm imagining this would work is that IntoClause disappears from
Query altogether: analyze.c would build a utility statement
CreateTableAs, pull the IntoClause out of the SelectStmt structure and
put it into the utility statement, and then proceed much as we do for
ExplainStmt (and for the same reasons).

regards, tom lane

#10Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers

Hi,

Hm. I just noticed a relatively big hole in the patch: The handling of
deletion of dependent objects currently is nonexistant because they don't go
through ProcessUtility...

Currently thinking what the best way to nicely implement that is. For other
commands the original command string is passed - that obviously won't work for
that case...

Andres

#11Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#1)
Re: Command Triggers

Hi all,

There is also the point about how permission checks on the actual commands (in
comparison of modifying command triggers) and such are handled:

BEFORE and INSTEAD will currently be called independently of the fact whether
the user is actually allowed to do said action (which is inconsistent with
data triggers) and indepentent of whether the object they concern exists.

I wonder if anybody considers that a problem?

Andres

#12Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#3)
Re: Command Triggers

Hi,

First thing first: thank you Andres for a great review, I do appreciate
it. Please find attached a newer version of the patch. The github
repository is also updated.

Andres Freund <andres@anarazel.de> writes:

I think at least a
* command_tag text

Added.

Why is there explicit documentation of not checking the arguments of said
trigger function? That seems to be a bit strange to me.

The code is searching for a function with the given name and 5 text
arguments, whatever you say in the command. That could be changed later
on if there's a need.

schemaname currently is mightily unusable because whether its sent or not
depends wether the table was created with a schemaname specified or not. That
doesn't seem to be a good approach.

I'm not sure about that, we're spiting out what the user entered.

Imo the output should fully schema qualify anything including operators. Yes,
thats rather ugly but anything else seems to be too likely to lead to
problems.

Will look into qualifying names.

As an alternative it would be possible to pass the current search path but
that doesn't seem to the best approach to me;

The trigger runs with the same search_path settings as the command, right?

Then there is nice stuff like CREATE TABLE .... (LIKE ...);
What shall that return in future? I vote for showing it as the plain CREATE
TABLE where all columns are specified.

I don't think so. Think about the main use cases for this patch,
auditing and replication. Both cases you want to deal with what the
user said, not what the system understood.

I think it would sensible to allow multiple actions on which to trigger to be
specified just as you can for normal triggers. I also would like an ALL option

Both are now implemented.

When dealing with an ANY command trigger, your procedure can get fired
on a command for which we don't have internal support for back parsing
etc, so you only get the command tag not null. I think that's the way to
go here, as I don't want to think we need to implement full support for
command triggers on ALTER OPERATOR FAMILY from the get go.

Currently the grammar allows options to be passed to command triggers. Do we
want to keep that? If yes, with the same arcane set of datatypes as in data
triggers?
If yes it needs to be wired up.

In fact it's not the case, you always get called with the same 5
arguments, all nullable now that we have ANY COMMAND support.

* schema qualification:
An option to add triggers only to a specific schema would be rather neat for
many scenarios.
I am not totally sure if its worth the hassle of defining what happens in the
edge cases of e.g. moving from one into another schema though.

I had written down support for a WHEN clause in command triggers, but
the exact design has yet to be worked out. I'd like to have this
facility but I'd like it even more if that could happen in a later
patch.

Currently there is no locking strategy at all. Which e.g. means that there is
no protection against two sessions changing stuff concurrently or such.

Was that just left out - which is ok - or did you miss that?

I left out the locking as of now. I tried to fix some of it in the new
patch though, but I will need to spend more time on that down the road.

Command triggers should only be allowed for the database owner.

Yes, that needs to happen soon, along with pg_dump and psql support.

I contrast to data triggers command triggers cannot change what is done. They
can either prevent it from running or not. Which imo is fine.
The question I have is whether it would be a good idea to make it easy to
prevent recursion.... Especially if the triggers wont be per schema.

You already have

ATER TRIGGER foo ON COMMAND create table DISABLE;

that you can use from the trigger procedure itself. Of course, locking
is an issue if you want to go parallel on running commands with
recursive triggers attached. I think we might be able to skip solving
that problem in the first run.

Imo the current callsite in ProcessUtility isn't that good. I think it would
make more sense moving it into standard_ProcessUtility. It should be *after*
the check_xact_readonly check in there in my opinion.

Makes sense, will fix in the next revision.

I am also don't think its a good idea to run the
ExecBeforeOrInsteadOfCommandTriggers stuff there because thats allso the path
that COMMIT/BEGIN and other stuff take. Those are pretty "fast path".

I suggest making two switches in standard_ProcessUtility, one for the non-
command trigger stuff which returns on success and one after that. Only after
the first triggers are checked.

Agreed.

Also youre very repeatedly transforming the parstree into a string. It would
be better doing that only once instead of every trigger...

It was only done once per before and instead of triggers (you can't have
both on the same command), and another time for any and all after
triggers, meaning at most twice. It's now down to only once, at most.

* many functions should be static but are not. Especially in cmdtrigger.c

Fixed.

* Either tgenable's datatype or its readfunc is wrong (watch the compiler ;))
* ruleutils.c:
* generic routine for IF EXISTS, CASCADE, ...

Will have to wait until next revision.

Thanks you again for a great review, regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

Attachments:

command-trigger.v3.patch.gzapplication/octet-streamDownload
#13Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andres Freund (#11)
Re: Command Triggers

Excerpts from Andres Freund's message of vie dic 02 19:09:47 -0300 2011:

Hi all,

There is also the point about how permission checks on the actual commands (in
comparison of modifying command triggers) and such are handled:

BEFORE and INSTEAD will currently be called independently of the fact whether
the user is actually allowed to do said action (which is inconsistent with
data triggers) and indepentent of whether the object they concern exists.

I wonder if anybody considers that a problem?

Hmm, we currently even have a patch (or is it already committed?) to
avoid locking objects before we know the user has permission on the
object. Getting to the point of calling the trigger would surely be
even worse.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#14Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#13)
Re: Command Triggers

On Fri, Dec 2, 2011 at 7:09 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Hmm, we currently even have a patch (or is it already committed?) to
avoid locking objects before we know the user has permission on the
object.  Getting to the point of calling the trigger would surely be
even worse.

I committed a first round of cleanup in that area, but unfortunately
there is a lot more to be done.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#15Andres Freund
andres@anarazel.de
In reply to: Alvaro Herrera (#13)
Re: Command Triggers

On Saturday, December 03, 2011 01:09:48 AM Alvaro Herrera wrote:

Excerpts from Andres Freund's message of vie dic 02 19:09:47 -0300 2011:

Hi all,

There is also the point about how permission checks on the actual
commands (in comparison of modifying command triggers) and such are
handled:

BEFORE and INSTEAD will currently be called independently of the fact
whether the user is actually allowed to do said action (which is
inconsistent with data triggers) and indepentent of whether the object
they concern exists.

I wonder if anybody considers that a problem?

Hmm, we currently even have a patch (or is it already committed?) to
avoid locking objects before we know the user has permission on the
object. Getting to the point of calling the trigger would surely be
even worse.

Well, calling the trigger won't allow them to lock the object. It doesn't even
confirm the existance of the table.

Andres

#16Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Simon Riggs (#8)
Re: Command Triggers

Simon Riggs <simon@2ndQuadrant.com> writes:

Those are especially important because in 9.2 DDL commands will cause
additional locking overheads, so preventing DDL will be essential to
keeping performance stable in high txn rate databases.

The patch now implements "any command" triggers, and you have the
command tag to branch in the function if you need to.

CREATE TRIGGER noddl
INSTEAD OF ANY COMMAND
EXECUTE PROCEDURE cancel_any_ddl();

So I'd like to see a few more triggers that work out of the box for
those cases (perhaps wrapped by a function?). It would also allow a
more useful man page example of how to use this.

We could solve that by providing an extension implementing command
triggers ready for use. One that allows to easily switch on and off the
capability of running commands seems like a good candidate.

That said, with the current coding, you can't have both a instead of
trigger on "any command" and a before or after trigger on any given
command, so installing that extension would prevent you from any other
usage of command triggers.

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

#17Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#12)
Re: Command Triggers

On Saturday, December 03, 2011 12:04:57 AM Dimitri Fontaine wrote:

Hi,

First thing first: thank you Andres for a great review, I do appreciate
it. Please find attached a newer version of the patch. The github
repository is also updated.

Andres Freund <andres@anarazel.de> writes:

I think at least a
* command_tag text

Added.

Why is there explicit documentation of not checking the arguments of said
trigger function? That seems to be a bit strange to me.

The code is searching for a function with the given name and 5 text
arguments, whatever you say in the command. That could be changed later
on if there's a need.

Forget it, i was too dumb to remember the code when I wrote the above ;)

schemaname currently is mightily unusable because whether its sent or not
depends wether the table was created with a schemaname specified or not.
That doesn't seem to be a good approach.

I'm not sure about that, we're spiting out what the user entered.

No, were not. Were writing out something that semantically the same what the
user entered. At several places NULL/NOT NULL and such get added.

As an alternative it would be possible to pass the current search path
but that doesn't seem to the best approach to me;

The trigger runs with the same search_path settings as the command, right?

For many things it makes sense to specify the search path of a function uppon
creation of the function. Even moreso if you have security definer
functions...
Otherwise you can get into troubles via operators and such...

Then there is nice stuff like CREATE TABLE .... (LIKE ...);
What shall that return in future? I vote for showing it as the plain
CREATE TABLE where all columns are specified.

I don't think so. Think about the main use cases for this patch,
auditing and replication. Both cases you want to deal with what the
user said, not what the system understood.

Is that so? In the replication case the origin table very well may look
differently on the master compared to the standby. Which is about impossible
to diagnose with the above behaviour.

I think it would sensible to allow multiple actions on which to trigger
to be specified just as you can for normal triggers. I also would like
an ALL option

Both are now implemented.

Cool, will check.

When dealing with an ANY command trigger, your procedure can get fired
on a command for which we don't have internal support for back parsing
etc, so you only get the command tag not null. I think that's the way to
go here, as I don't want to think we need to implement full support for
command triggers on ALTER OPERATOR FAMILY from the get go.

Thats ok with me.

Currently the grammar allows options to be passed to command triggers. Do
we want to keep that? If yes, with the same arcane set of datatypes as
in data triggers?
If yes it needs to be wired up.

In fact it's not the case, you always get called with the same 5
arguments, all nullable now that we have ANY COMMAND support.

I think you misunderstood me. Check the following excerpt from gram.y:
CreateCmdTrigStmt:
CREATE TRIGGER name TriggerActionTime COMMAND trigger_command_list
EXECUTE PROCEDURE func_name '(' TriggerFuncArgs ')'

You have TriggerfuncArgs there but you ignore them.

* schema qualification:
An option to add triggers only to a specific schema would be rather neat
for many scenarios.
I am not totally sure if its worth the hassle of defining what happens in
the edge cases of e.g. moving from one into another schema though.

I had written down support for a WHEN clause in command triggers, but
the exact design has yet to be worked out. I'd like to have this
facility but I'd like it even more if that could happen in a later
patch.

Hm. I am not sure a generic WHEN clause is needed. In my opinion schema
qualification would be enough...
A later patch is fine imo.

I contrast to data triggers command triggers cannot change what is done.
They can either prevent it from running or not. Which imo is fine.
The question I have is whether it would be a good idea to make it easy to
prevent recursion.... Especially if the triggers wont be per schema.

You already have

ATER TRIGGER foo ON COMMAND create table DISABLE;

that you can use from the trigger procedure itself. Of course, locking
is an issue if you want to go parallel on running commands with
recursive triggers attached. I think we might be able to skip solving
that problem in the first run.

hm. Not yet convinced. I need to think about it a bit.

Have fun ;)

Andres

#18Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#9)
Re: Command Triggers

On Friday, December 02, 2011 03:09:55 AM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On Thursday, December 01, 2011 07:21:25 PM Tom Lane wrote:

Making this work cleanly would be a bigger deal than I think you're
thinking.

Obviously that depends on the definition of clean...

Changing the grammar to make that explicit seems to involve a bit too
many changes on a first glance. The cheap way out seems to be to make
the decision in analyze.c:transformQuery.
Would that be an acceptable way forward?

ITYM transformStmt, but yeah, somewhere around there is probably
reasonable. The way I'm imagining this would work is that IntoClause
disappears from Query altogether: analyze.c would build a utility
statement
CreateTableAs, pull the IntoClause out of the SelectStmt structure and
put it into the utility statement, and then proceed much as we do for
ExplainStmt (and for the same reasons).

Ok. Because my book turned out to be boring I started looking at this. I
wonder if wouldn't be better to do check in directly in raw_parser(). The
advantage would be that that magically would fix the issue of not logging
CTAS/select when using log_statement = ddl and we don't need a snapshot or
such so that shouldn't be a problem.

Any arguments against doing so?

Andres

#19Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#9)
Re: Command Triggers

On Friday, December 02, 2011 03:09:55 AM Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On Thursday, December 01, 2011 07:21:25 PM Tom Lane wrote:

Making this work cleanly would be a bigger deal than I think you're
thinking.

Obviously that depends on the definition of clean...

Changing the grammar to make that explicit seems to involve a bit too
many changes on a first glance. The cheap way out seems to be to make
the decision in analyze.c:transformQuery.
Would that be an acceptable way forward?

ITYM transformStmt, but yeah, somewhere around there is probably
reasonable. The way I'm imagining this would work is that IntoClause
disappears from Query altogether: analyze.c would build a utility
statement
CreateTableAs, pull the IntoClause out of the SelectStmt structure and
put it into the utility statement, and then proceed much as we do for
ExplainStmt (and for the same reasons).

I have a patch which basically does all this minus some cleanup. I currently
do the the differentiation for SELECT INTO (not CREATE AS, thats done in
gram.y) in raw_parser but thats quick to move if others don't aggree on that.

I have two questions now:

First, does anybody think it would be worth getting rid of the duplication
from OpenIntoRel (formerly from execMain.c) in regard to DefineRelation()?
I noticed that there already is some diversion between both. E.g. CREATE TABLE
frak TABLESPACE pg_global AS SELECT 1; is possible while it would be forbidden
via a plain CREATE TABLE. (I will send a fix for this separately).

Secondly, I am currently wondering whether it would be a good idea to use the
ModifyTable infrastructure for doing the insertion instead an own DestReceiver
infrastructure thats only used for CTAS.
It looks a bit too complicated to do this without removing the bulk insert and
HEAP_INSERT_SKIP_WAL optimizations.

Andres

#20Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#19)
Re: Command Triggers

Andres Freund <andres@anarazel.de> writes:

I have two questions now:

First, does anybody think it would be worth getting rid of the duplication
from OpenIntoRel (formerly from execMain.c) in regard to DefineRelation()?

That's probably reasonable to do, since as you say it would remove the
opportunity for bugs-of-omission in the CTAS table creation step.
OTOH, if you find yourself having to make any significant changes to
DefineRelation, then maybe not.

Secondly, I am currently wondering whether it would be a good idea to use the
ModifyTable infrastructure for doing the insertion instead an own DestReceiver
infrastructure thats only used for CTAS.

I think this is probably a bad idea; it will complicate matters and buy
little. There's not a lot of stuff needed for the actual data insertion
step, since we know the table can't have any defaults, constraints,
triggers, etc as yet.

regards, tom lane

#21Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#20)
#22Ross J. Reedstrom
reedstrm@rice.edu
In reply to: Andres Freund (#15)
#23Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#12)
#24Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#21)
#25Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#10)
#26Peter Eisentraut
peter_e@gmx.net
In reply to: Andres Freund (#24)
#27Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#26)
#28Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#25)
#29Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Robert Haas (#28)
#30Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#28)
#31Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#29)
#32Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#30)
#33Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#32)
#34Greg Smith
gsmith@gregsmith.com
In reply to: Robert Haas (#28)
#35Robert Haas
robertmhaas@gmail.com
In reply to: Greg Smith (#34)
#36Merlin Moncure
mmoncure@gmail.com
In reply to: Robert Haas (#35)
#37Robert Haas
robertmhaas@gmail.com
In reply to: Merlin Moncure (#36)
#38Chris Browne
cbbrowne@acm.org
In reply to: Robert Haas (#35)
#39Robert Haas
robertmhaas@gmail.com
In reply to: Chris Browne (#38)
#40Greg Smith
gsmith@gregsmith.com
In reply to: Robert Haas (#35)
#41Jan Wieck
JanWieck@Yahoo.com
In reply to: Robert Haas (#35)
#42Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#35)
#43Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Chris Browne (#38)
#44Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Jan Wieck (#41)
#45Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Dimitri Fontaine (#42)
#46Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#45)
#47Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#46)
#48Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#47)
#49Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#48)
#50Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#48)
#51Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#50)
#52Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#51)
#53Noah Misch
noah@leadboat.com
In reply to: Dimitri Fontaine (#52)
#54Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#50)
#55Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#54)
#56Bruce Momjian
bruce@momjian.us
In reply to: Dimitri Fontaine (#55)
#57Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Bruce Momjian (#56)
#58Bruce Momjian
bruce@momjian.us
In reply to: Dimitri Fontaine (#57)
#59Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#57)
#60Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#59)
#61Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#60)
#62Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#61)
#63Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#62)
#64Greg Smith
gsmith@gregsmith.com
In reply to: Robert Haas (#63)
#65Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Dimitri Fontaine (#60)
#66Robert Haas
robertmhaas@gmail.com
In reply to: Greg Smith (#64)
#67Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#66)
#68Pavel Stehule
pavel.stehule@gmail.com
In reply to: Robert Haas (#66)
#69Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#67)
#70Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#67)
#71Greg Smith
gsmith@gregsmith.com
In reply to: Robert Haas (#66)
#72Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#70)
#73Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#72)
#74Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#72)
#75Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#72)
#76Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#75)
#77Andres Freund
andres@anarazel.de
In reply to: Dimitri Fontaine (#76)
#78Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#77)
#79Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#76)
#80Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#79)
#81Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#80)
#82Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#81)
#83Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#82)
#84Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#83)
#85Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#84)
#86Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Dimitri Fontaine (#85)
#87Andres Freund
andres@anarazel.de
In reply to: Alvaro Herrera (#86)
#88Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#85)
#89Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#88)
#90Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#86)
#91Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#90)
#92Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#91)
#93Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#92)
#94Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#93)
#95Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#94)
#96Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#95)
#97Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#96)
#98Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#97)
#99Simon Riggs
simon@2ndQuadrant.com
In reply to: Dimitri Fontaine (#98)
#100Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Simon Riggs (#99)
#101Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#100)
#102Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#101)
#103Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#102)
#104Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#103)
#105Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#104)
#106Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#101)
#107Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#106)
#108Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Dimitri Fontaine (#106)
#109Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#107)
#110Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#108)
#111Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#109)
#112Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#111)
#113Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#112)
#114Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#113)
#115Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#108)
#116Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Alvaro Herrera (#108)
#117Marko Kreen
markokr@gmail.com
In reply to: Dimitri Fontaine (#114)
#118Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Marko Kreen (#117)
#119Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#114)
#120Marko Kreen
markokr@gmail.com
In reply to: Dimitri Fontaine (#118)
#121Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#111)
#122Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Dimitri Fontaine (#121)
#123Robert Haas
robertmhaas@gmail.com
In reply to: Dimitri Fontaine (#121)
#124Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#123)
#125Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#123)
#126Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#19)
#127Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#126)
#128Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#127)
#129Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#127)
#130Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#129)
#131Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tom Lane (#130)
#132Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#128)
#133Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#132)
#134Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#133)
#135Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#134)