Event Triggers reduced, v1
Hi,
Allow me to open the new season of the DML trigger series, named
pg_event_trigger. This first episode is all about setting up the drama,
so that next ones make perfect sense.
The attached patch contains all the infrastructure for event triggers
and also a first implementation of them for the event "command_start",
implemented in a single place in utility.c.
The infrastructure is about:
- new catalog
- grammar for new commands
- documentation skeleton
- pg_dump support
- psql support
- ability to actually run user triggers
- written in "core languages"
(pl/c, pl/pgsql, pl/python, pl/perl, pl/tcl)
- limited subcommand handling
The goal for this first patch is to avoid semantics issues so that we
can get something technically clean in, and have more time to talk
semantics next times. The main discussion to avoid is deciding if we
want to fire event triggers for CREATE SEQUENCE and CREATE INDEX in a
command that just did implement a SERIAL PRIMARY KEY in a table.
This way of doing things is possible because we took time to set a road
map together with Robert when we were both in Ottawa, and because it's
early in the cycle. The complete feature still needs to happen before
9.3 is released, but any realistic progress has to be cut down.
Look, it's an easy little skinny patch to review, right:
git --no-pager diff --shortstat master
62 files changed, 4546 insertions(+), 108 deletions(-)
This patch includes regression tests that we worked on with Thom last
rounds, remember that they only run in the serial schedule, that means
with `make installcheck` only. Adding noisy output at random while the
parallel schedule run is a good way to break all the regression testing,
so I've been avoiding that.
I don't think this patch is ready as it is, by the way, I couldn't
devote nearly enough time to have something that polished already. I
think even with setting the goal not to embrace semantics, reviewing
this patch will certainly bring some interesting design discussions.
Regards,
--
Dimitri Fontaine
PostgreSQL DBA, Architecte
Attachments:
On 15 June 2012 21:27, Dimitri Fontaine <dfontaine@hi-media.com> wrote:
The goal for this first patch is to avoid semantics issues so that we
can get something technically clean in, and have more time to talk
semantics next times. The main discussion to avoid is deciding if we
want to fire event triggers for CREATE SEQUENCE and CREATE INDEX in a
command that just did implement a SERIAL PRIMARY KEY in a table.
So this patch triggers once per top level command, just with
information about the set of nested events?
I'm happy if we make sweeping initial points like "don't generate
events for sequences and indexes" in the first version. We can always
add more later.
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Excerpts from Dimitri Fontaine's message of vie jun 15 16:27:50 -0400 2012:
The attached patch contains all the infrastructure for event triggers
and also a first implementation of them for the event "command_start",
implemented in a single place in utility.c.The infrastructure is about:
- new catalog
- grammar for new commands
- documentation skeleton
- pg_dump support
- psql support
- ability to actually run user triggers
- written in "core languages"
(pl/c, pl/pgsql, pl/python, pl/perl, pl/tcl)
- limited subcommand handling
Did you try REASSIGN OWNED and DROP OWNED with a role that has defined
some event triggers?
Look, it's an easy little skinny patch to review, right:
git --no-pager diff --shortstat master
62 files changed, 4546 insertions(+), 108 deletions(-)
Skinny ... right. I started to give it a look -- I may have something
useful to comment later.
This patch includes regression tests that we worked on with Thom last
rounds, remember that they only run in the serial schedule, that means
with `make installcheck` only. Adding noisy output at random while the
parallel schedule run is a good way to break all the regression testing,
so I've been avoiding that.
Hmm, I don't like the idea of a test that only runs in serial mode.
Maybe we can find some way to make it work in parallel mode as well.
I don't have anything useful to comment right now.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Fri, Jun 15, 2012 at 4:27 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:
Allow me to open the new season of the DML trigger series, named
pg_event_trigger. This first episode is all about setting up the drama,
so that next ones make perfect sense.
Comments:
1. I still think we ought to get rid of the notion of BEFORE or AFTER
(i.e. pg_event_trigger.evttype) and just make that detail part of the
event name (e.g. pg_event_trigger.evtevent). Many easily forseeable
event types will be more like "during" rather than "before" or
"after", and for those that do have a notion of before and after, we
can have two different event names and include the word "before" or
"after" there. I am otherwise satisfied with the schema you've
chosen.
2. I think it's important to be able to add new types of event
triggers without creating excessive parser bloat. I think it's
important to use some kind of generic syntax here which will be able
to apply to all types of triggers we may want to add, now or in the
future. The easiest way to do that is to use literal syntax for the
list of command tags, rather than writing them out as key words: e.g.
'ALTER TABLE' rather than ALTER TABLE. It's not quite as pretty, but
the savings in parser bloat and future code change seem well worth it.
Or, alternatively, we could use identifier style, e.g. alter_table,
as I previously suggested.
3. The event trigger cache seems to be a few bricks shy of a load.
First, event_trigger_cache_is_stalled is mis-named; I think you mean
"stale", not "stalled". Second, instead of setting that flag and then
rebuilding the cache when you see the flag set, how about just blowing
away the cache contents whenever you would have set the flag? That
seems a whole lot simpler and cleaner, and removes the need for a
force_rebuild flag on BuildEventTriggerCache(). Third, ISTM that this
isn't going to work correctly if backend A performs an event after
backend B has built its cache. To fix this, I think you need to rip
out all the places where you force a rebuild locally and instead use
something like CacheRegisterSyscacheCallback() to blow away the cache
whenever something changes; you might find it helpful to look at
attoptcache.c.
4. The documentation doesn't build.
openjade:reference.sgml:44:4:W: cannot generate system identifier for
general entity "alterEventTrigger"
openjade:reference.sgml:44:4:E: general entity "alterEventTrigger" not
defined and no default entity
openjade:reference.sgml:44:21:E: reference to entity
"alterEventTrigger" for which no system identifier could be generated
openjade:reference.sgml:44:3: entity was defined here
openjade:reference.sgml:86:4:W: cannot generate system identifier for
general entity "createEventTrigger"
openjade:reference.sgml:86:4:E: general entity "createEventTrigger"
not defined and no default entity
openjade:reference.sgml:86:22:E: reference to entity
"createEventTrigger" for which no system identifier could be generated
openjade:reference.sgml:86:3: entity was defined here
openjade:reference.sgml:125:4:W: cannot generate system identifier for
general entity "dropEventTrigger"
openjade:reference.sgml:125:4:E: general entity "dropEventTrigger" not
defined and no default entity
openjade:reference.sgml:125:20:E: reference to entity
"dropEventTrigger" for which no system identifier could be generated
openjade:reference.sgml:125:3: entity was defined here
openjade:catalogs.sgml:1868:35:E: character "_" is not allowed in the
value of attribute "ZONE"
openjade:catalogs.sgml:1868:19:X: reference to non-existent ID
"CATALOG-PG-EVENT_TRIGGER"
openjade:trigger.sgml:43:47:X: reference to non-existent ID
"SQL-CREATECOMMANDTRIGGER"
5. In terms of a psql command, I think that \dev is both not very
mnemonic and, as you mentioned in the comment, possibly confusable
with SQL/MED. If we're going to have a \d command for this, I suggest
something like \dy, which is not very mnemonic either but at least
seems unlikely to be confused with anything else. Other things that
are unused include \dh, \dj, \dk, \dq, \dw, and \dz, if any of those
grab you, or a somewhat broader range of things (but still nothing
very memorable) if we include capital letters. Or we could branch out
into punctuation, like \d& -- or things that don't begin with the
letter d, but that doesn't seem like a particularly good idea.
6. In objectaddress.c, I think that get_object_address_event_trigger
should be eliminated in favor of an additional case in
get_object_address_unqualified.
7. There are many references to command triggers that still need to be
cleaned up. trigger.sgml still makes reference to the name command
triggers. plpgsql.sgml also contains vestiges of the command trigger
notation, and references to some TG_* variables that I don't think
exist in this version of the patch. event_trigger.c is identified
(twice) as cmdtrigger.c in the file header comment. The header
comment for InsertEventTriggerTuple makes reference to pg_cmdtrigger,
as does a line comment in RemoveEventTriggerById. The regression
output mentions cmdtrigger in a few places as well. In the
documentation, event triggers are mentioned as having return type
command_trigger, but it's now called event_trigger.
8. The re-addition of node tags like T_RemoveFuncStmt seems to be a
merging error. Changing \dc so that it rejects \dcrap appears to be a
leftover from when the command was \dcT. In one place in the docs you
have 'avent' for 'event'. In event_trigger.c, you have #ifdef
UNDEFINED; project standard is #ifdef NOT_USED (or else you can remove
the code).
9. The regression tests seem to now be testing some features that
don't exist any more, and might need some rethinking to make what they
do match the scope of this patch.
10. I suggest separating out the support for other PLs (Python, Tcl)
and submitting that as a later patch, since I'm unqualified to commit
it (and I'm hoping to get the rest of this committed). The PL/TCL
stuff also contains residual references to the command-trigger
notation which should be cleaned up before resubmitting.
There's probably more, but I'm all reviewed out for right now.
Hopefully that's enough to get you started. I think this is heading
in a good direction, even though there's still a good bit of work left
to do.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Alvaro Herrera <alvherre@commandprompt.com> writes:
Did you try REASSIGN OWNED and DROP OWNED with a role that has defined
some event triggers?
Not yet. Will add to regression tests, good catch.
Hmm, I don't like the idea of a test that only runs in serial mode.
Maybe we can find some way to make it work in parallel mode as well.
I don't see how we can manage to do that, as adding an event trigger to
some command will impact tests running in parallel.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Wednesday, June 20, 2012 09:33:26 PM Dimitri Fontaine wrote:
Hmm, I don't like the idea of a test that only runs in serial mode.
Maybe we can find some way to make it work in parallel mode as well.I don't see how we can manage to do that, as adding an event trigger to
some command will impact tests running in parallel.
Cant you just put it alone in a test group in the parallel_schedule? Several
other tests do that?
Andres
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Wed, Jun 20, 2012 at 3:39 PM, Andres Freund <andres@2ndquadrant.com> wrote:
On Wednesday, June 20, 2012 09:33:26 PM Dimitri Fontaine wrote:
Hmm, I don't like the idea of a test that only runs in serial mode.
Maybe we can find some way to make it work in parallel mode as well.I don't see how we can manage to do that, as adding an event trigger to
some command will impact tests running in parallel.Cant you just put it alone in a test group in the parallel_schedule? Several
other tests do that?
Yeah, that seems like it should work. If not, I'd say the tests
themselves are broken.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
Robert Haas <robertmhaas@gmail.com> writes:
1. I still think we ought to get rid of the notion of BEFORE or AFTER
(i.e. pg_event_trigger.evttype) and just make that detail part of the
event name (e.g. pg_event_trigger.evtevent). Many easily forseeable
event types will be more like "during" rather than "before" or
"after", and for those that do have a notion of before and after, we
can have two different event names and include the word "before" or
"after" there. I am otherwise satisfied with the schema you've
chosen.
It's not before/after anymore, but rather addon/replace if you will. I
kept the INSTEAD OF keyword for the replace semantics, that you've been
asking me to keep IIRC, with security policy plugins as a use case.
Now we can of course keep those semantics and embed them in the event
name we provide users, I though that maybe a documentation matrix of
which event support which "mode" would be cleaner to document. We might
as well find a clean way to implement both modes for most of the
commands, I don't know yet.
So, are you sure you want to embed that part of the event trigger
semantics in the event name itself?
2. I think it's important to be able to add new types of event
triggers without creating excessive parser bloat. I think it's
I've been trying to do that yes, as you can see with event_name and
event_trigger_variable rules. I've been re-using as much existing
keywords as I could because I believe that's not causing any measurable
bloat, I'll kindly reconsider if necessary, even if sadly.
important to use some kind of generic syntax here which will be able
to apply to all types of triggers we may want to add, now or in the
future. The easiest way to do that is to use literal syntax for the
list of command tags, rather than writing them out as key words: e.g.
'ALTER TABLE' rather than ALTER TABLE. It's not quite as pretty, but
the savings in parser bloat and future code change seem well worth it.
Or, alternatively, we could use identifier style, e.g. alter_table,
as I previously suggested.
Whatever the solution here, we still need to be able to ERROR out very
early if the user entered a non supported command here, such as GRANT
for the time being. I'm not sure a manual list of strcmp() would be more
effective than having bison basically produce the same thing for us. I
think using the grammar here makes for easier maintenance.
3. The event trigger cache seems to be a few bricks shy of a load.
I wouldn't be that surprised, mind you. I didn't have nearly as much
time I wanted to working on that project.
First, event_trigger_cache_is_stalled is mis-named; I think you mean
"stale", not "stalled". Second, instead of setting that flag and then
Stale. Right. Edited.
rebuilding the cache when you see the flag set, how about just blowing
away the cache contents whenever you would have set the flag? That
I've been doing that at first, but that meant several full rebuilds in a
row in the regression tests, which are adding new event triggers then
using them. I though lazily maintaining the cache would be better.
seems a whole lot simpler and cleaner, and removes the need for a
force_rebuild flag on BuildEventTriggerCache(). Third, ISTM that this
isn't going to work correctly if backend A performs an event after
backend B has built its cache. To fix this, I think you need to rip
out all the places where you force a rebuild locally and instead use
something like CacheRegisterSyscacheCallback() to blow away the cache
whenever something changes; you might find it helpful to look at
attoptcache.c.
Ok, looking at that for next revision of the patch, which I should be
able to post early next week.
4. The documentation doesn't build.
Sorry about that, will get fixed too.
5. In terms of a psql command, I think that \dev is both not very
mnemonic and, as you mentioned in the comment, possibly confusable
with SQL/MED. If we're going to have a \d command for this, I suggest
something like \dy, which is not very mnemonic either but at least
seems unlikely to be confused with anything else. Other things that
are unused include \dh, \dj, \dk, \dq, \dw, and \dz, if any of those
grab you, or a somewhat broader range of things (but still nothing
very memorable) if we include capital letters. Or we could branch out
into punctuation, like \d& -- or things that don't begin with the
letter d, but that doesn't seem like a particularly good idea.
I'm not that fond of psql commands, but I don't think it's going to fly
not to have one for event triggers. I could buy \dy.
6. In objectaddress.c, I think that get_object_address_event_trigger
should be eliminated in favor of an additional case in
get_object_address_unqualified.
Sure. It used to be more complex than that when the identifier was a
composite with the command name, it makes no sense to separate it away
now. Done.
7. There are many references to command triggers that still need to be
cleaned up. trigger.sgml still makes reference to the name command
triggers. plpgsql.sgml also contains vestiges of the command trigger
notation, and references to some TG_* variables that I don't think
exist in this version of the patch. event_trigger.c is identified
(twice) as cmdtrigger.c in the file header comment. The header
comment for InsertEventTriggerTuple makes reference to pg_cmdtrigger,
as does a line comment in RemoveEventTriggerById. The regression
output mentions cmdtrigger in a few places as well. In the
documentation, event triggers are mentioned as having return type
command_trigger, but it's now called event_trigger.
All fixed, will grep for cmd and command in the patch and fix any other
one that's still there before sending v2 of the patch. Sorry about that.
8. The re-addition of node tags like T_RemoveFuncStmt seems to be a
merging error. Changing \dc so that it rejects \dcrap appears to be a
leftover from when the command was \dcT. In one place in the docs you
have 'avent' for 'event'. In event_trigger.c, you have #ifdef
UNDEFINED; project standard is #ifdef NOT_USED (or else you can remove
the code).
Will see about node tags and psql clean merge. Docs fixed. I meant to
remove the code. Done now. Thanks.
9. The regression tests seem to now be testing some features that
don't exist any more, and might need some rethinking to make what they
do match the scope of this patch.
The current implementation must be kicking for all supported commands
and we have a authoritative list of them in the grammar, so I wanted to
maintain a regression test suite where they are all exercised, even if
we're exercising the same code path each time.
That's meant to change with later patch, I'm not sure how much gain we
have to remove test covering now knowing that we certainly won't release
with only that first patch.
10. I suggest separating out the support for other PLs (Python, Tcl)
and submitting that as a later patch, since I'm unqualified to commit
it (and I'm hoping to get the rest of this committed). The PL/TCL
stuff also contains residual references to the command-trigger
notation which should be cleaned up before resubmitting.
Fixed pltcl internal references. Will produce separate patches for next
revision.
There's probably more, but I'm all reviewed out for right now.
Hopefully that's enough to get you started. I think this is heading
in a good direction, even though there's still a good bit of work left
to do.
Thanks for your review, it's clearly enough to get started chewing on
the patch!
Early followers can see the progress, when it happens, in the github
repository, if waiting for the next patch is unbearably long :)
https://github.com/dimitri/postgres
https://github.com/dimitri/postgres/tree/evt_trig_v1
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Robert Haas <robertmhaas@gmail.com> writes:
Cant you just put it alone in a test group in the parallel_schedule? Several
other tests do that?Yeah, that seems like it should work. If not, I'd say the tests
themselves are broken.
I completely missed that we could do that. I don't feel bright. Of
course it just works.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Wed, Jun 20, 2012 at 4:36 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
1. I still think we ought to get rid of the notion of BEFORE or AFTER
(i.e. pg_event_trigger.evttype) and just make that detail part of the
event name (e.g. pg_event_trigger.evtevent). Many easily forseeable
event types will be more like "during" rather than "before" or
"after", and for those that do have a notion of before and after, we
can have two different event names and include the word "before" or
"after" there. I am otherwise satisfied with the schema you've
chosen.It's not before/after anymore, but rather addon/replace if you will. I
kept the INSTEAD OF keyword for the replace semantics, that you've been
asking me to keep IIRC, with security policy plugins as a use case.Now we can of course keep those semantics and embed them in the event
name we provide users, I though that maybe a documentation matrix of
which event support which "mode" would be cleaner to document. We might
as well find a clean way to implement both modes for most of the
commands, I don't know yet.So, are you sure you want to embed that part of the event trigger
semantics in the event name itself?
Yeah, pretty sure. I think that for regular triggers, BEFORE, AFTER,
and INSTEAD-OF are the firing-point specification. But even triggers
will have more than three firing points, probably eventually quite a
lot more. So we need something more flexible. But we don't need that
more flexible thing AND ALSO the before/after/instead-of
specification, which I think in most cases won't be meaningful anyway.
It happens to be somewhat sensible for this initial firing point, but
I think for most of them there will be just one place, and in many
cases it will be neither before, nor after, nor instead-of.
2. I think it's important to be able to add new types of event
triggers without creating excessive parser bloat. I think it'sI've been trying to do that yes, as you can see with event_name and
event_trigger_variable rules. I've been re-using as much existing
keywords as I could because I believe that's not causing any measurable
bloat, I'll kindly reconsider if necessary, even if sadly.
The issue is that the size of the parser tables grow with the square
of the number of states. This will introduce lots of new states that
we don't really need; and every new kind of event trigger that we want
to add will introduce more.
3. The event trigger cache seems to be a few bricks shy of a load.
I wouldn't be that surprised, mind you. I didn't have nearly as much
time I wanted to working on that project.First, event_trigger_cache_is_stalled is mis-named; I think you mean
"stale", not "stalled". Second, instead of setting that flag and thenStale. Right. Edited.
rebuilding the cache when you see the flag set, how about just blowing
away the cache contents whenever you would have set the flag? ThatI've been doing that at first, but that meant several full rebuilds in a
row in the regression tests, which are adding new event triggers then
using them. I though lazily maintaining the cache would be better.
Well, AFAICS, you're still doing full rebuilds whenever something
changes; you're just keeping the (useless, dead) cache around until
you decide to rebuild it. Might as well free the memory once you know
that the next access will rebuild it anyway, and for a bonus it saves
you a flag.
I'm not that fond of psql commands, but I don't think it's going to fly
not to have one for event triggers. I could buy \dy.
Yeah, I think people are going to want to have one. I really despise
the \d<whatever> syntax, but it's not 100% clear what a better one
would look like.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
It's not before/after anymore, but rather addon/replace if you will. I
kept the INSTEAD OF keyword for the replace semantics, that you've been
asking me to keep IIRC, with security policy plugins as a use case.Now we can of course keep those semantics and embed them in the event
name we provide users, I though that maybe a documentation matrix of
which event support which "mode" would be cleaner to document. We might
as well find a clean way to implement both modes for most of the
commands, I don't know yet.So, are you sure you want to embed that part of the event trigger
semantics in the event name itself?Yeah, pretty sure. I think that for regular triggers, BEFORE, AFTER,
and INSTEAD-OF are the firing-point specification. But even triggers
will have more than three firing points, probably eventually quite a
lot more. So we need something more flexible. But we don't need that
more flexible thing AND ALSO the before/after/instead-of
specification, which I think in most cases won't be meaningful anyway.
It happens to be somewhat sensible for this initial firing point, but
I think for most of them there will be just one place, and in many
cases it will be neither before, nor after, nor instead-of.
I agree with using the event name as a the specification for the firing
point, and that we should prefer documenting the ordering of those
rather than offering a fuzzy idea of BEFORE and AFTER steps in there.
The AFTER step is better expressed as BEFORE the next one.
Now, I still think there's an important discrepancy between adding a new
behaviour that adds-up to whatever the backend currently implements and
providing a replacement behaviour with a user defined function that gets
called instead of the backend code.
And I still don't think that the event name should be carrying alone
that semantic discrepancy. Now, I also want the patch to get in, so I
won't insist very much if I'm alone in that position. Anyone else
interested enough to chime in?
The user visible difference would be between those variants:
create event trigger foo at 'before_security_check' ...
create event trigger foo at 'replace_security_check' ...
create event trigger foo before 'security_check' ...
create event trigger foo instead of 'security_check' ...
Note that in this version the INSTEAD OF variant is not supported, we
only intend to offer it in some very narrow cases, or at least that is
my understanding.
The issue is that the size of the parser tables grow with the square
of the number of states. This will introduce lots of new states that
we don't really need; and every new kind of event trigger that we want
to add will introduce more.
It's a little sad not being able to reuse command tag keywords, but it's
even more sad to impact the rest of the query parsing. IIRC you had some
performance test patch with a split of the main parser into queries and
dml on the one hand, and utility commands on the other hand. Would that
help here? (I mean more as a general solution against that bloat problem
than for this very patch here).
I prefer the solution of using 'ALTER TABLE' rather than ALTER TABLE,
even if code wise we're not gaining anything in complexity: the parser
bloat gets replaced by a big series of if branches. Of course you only
exercise it when you need to. I will change that for next patch.
3. The event trigger cache seems to be a few bricks shy of a load.
Well, AFAICS, you're still doing full rebuilds whenever something
changes; you're just keeping the (useless, dead) cache around until
you decide to rebuild it. Might as well free the memory once you know
that the next access will rebuild it anyway, and for a bonus it saves
you a flag.
I'm just done rewriting the cache management with a catalog cache for
event triggers and a Syscache Callback that calls into a new module
called src/backend/utils/cache/evtcache.c that mimics attoptcache.c. No
more cache stale variable. And a proper composite hash key.
I still have some more work here before being able to send a new release
of the patch, as I said I won't have enough time to make that happen
until within next week. The git repository is updated, though.
https://github.com/dimitri/postgres/tree/evt_trig_v1
https://github.com/dimitri/postgres/compare/913091de51...861eb038d0
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Hi,
Here's an early revision 2 of the patch, not yet ready for commit, so
including the PL stuff still. What's missing is mainly a cache reference
leak to fix at DROP EVENT TRIGGER, but I failed to spot where it comes
from.
As I fixed about all the other comments I though I might as well send in
the new version of the patch to get to another round of review.
Robert Haas <robertmhaas@gmail.com> writes:
1. I still think we ought to get rid of the notion of BEFORE or AFTER
(i.e. pg_event_trigger.evttype) and just make that detail part of the
event name (e.g. pg_event_trigger.evtevent). Many easily forseeable
So, agreed on before/after, not on INSTEAD OF. No change in the patch,
still discussing that point.
2. I think it's important to be able to add new types of event
triggers without creating excessive parser bloat. I think it's
Fixed in the attached, I believe.
3. The event trigger cache seems to be a few bricks shy of a load.
Fixed in the attached, including cache invalidation registered as a
system cache callback.
4. The documentation doesn't build.
Fixed, I mainly managed to forget adding the new files.
5. In terms of a psql command, I think that \dev is both not very
Switched to \dy and cleaned up.
6. In objectaddress.c, I think that get_object_address_event_trigger
should be eliminated in favor of an additional case in
get_object_address_unqualified.
Fixed in the attached.
7. There are many references to command triggers that still need to be
cleaned up.
All fixed.
8. The re-addition of node tags like T_RemoveFuncStmt seems to be a
merging error. Changing \dc so that it rejects \dcrap appears to be a
leftover from when the command was \dcT. In one place in the docs you
have 'avent' for 'event'. In event_trigger.c, you have #ifdef
UNDEFINED; project standard is #ifdef NOT_USED (or else you can remove
the code).
All fixed.
9. The regression tests seem to now be testing some features that
don't exist any more, and might need some rethinking to make what they
do match the scope of this patch.
Actually those tests helped me spot some strange things when cleaning up
the cache key, and only some commands would fail. So I'm in favor of
keeping it all for now.
10. I suggest separating out the support for other PLs (Python, Tcl)
and submitting that as a later patch, since I'm unqualified to commit
it (and I'm hoping to get the rest of this committed). The PL/TCL
stuff also contains residual references to the command-trigger
notation which should be cleaned up before resubmitting.
That's for next turn.
There's probably more, but I'm all reviewed out for right now.
Hopefully that's enough to get you started. I think this is heading
in a good direction, even though there's still a good bit of work left
to do.
So, let's see about that remaining bit of work :)
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
On Sun, Jun 24, 2012 at 5:46 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:
Here's an early revision 2 of the patch, not yet ready for commit, so
including the PL stuff still. What's missing is mainly a cache reference
leak to fix at DROP EVENT TRIGGER, but I failed to spot where it comes
from.As I fixed about all the other comments I though I might as well send in
the new version of the patch to get to another round of review.
Some of the pg_dump hunks fail to apply for me; I guess this needs to
be remerged.
Spurious hunk:
- query_hosts
+ query_hosts
Spurious hunk:
- * need deep copies, so they should be copied via list_copy()
+ * need deep copies, so they should be copied via list_copy(const )
There are a few remaining references to EVTG_FIRED_BEFORE /
EVTG_FIRED_INSTEAD_OF which should be cleaned up. I suggest writing
the grammar as CREATE EVENT TRIGGER name ON event_name EXECUTE... On
a related note, evttype is still mentioned in the documentation, also.
And CreateEventTrigStmt has a char timing field that can go.
Incidentally, why do we not support an argument list here, as with
ordinary triggers?
I think the below hunk should get removed. Or else it should be
surrounded by #ifdef rather than commented out.
+ /*
+ * Useful to raise WARNINGs for any DDL command not yet supported.
+ *
+ elog(WARNING, "Command Tag: %s", tag);
+ elog(WARNING, "Note to String: %s", nodeToString(parsetree));
+ */
Typo:
+ * where we probide object name and namespace separately and still want nice
Typo:
+ * the easier code makes up fot it big time.
psql is now very confused about what the slash command is. It's
actually implemented as \dy, but the comments say \dev in several
places, and slashUsage() documents it as \dct.
InitializeEvtTriggerCommandCache still needs work. First, it ensures
that CacheMemoryContext is non-NULL... after it's already stored the
value of CacheMemoryContext into the HASHCTL. Obviously, the order
there needs to be fixed. Also, I think you need to split this into
two functions. There should be one function that gets called just
once at startup time to CacheRegisterSyscacheCallback(), because we
don't want to redo that every time the cache gets blown away. Then
there should be another function that gets called when, and only when,
someone needs to use the cache. That should create and populate the
hash table.
I think that all event triggers should fire in exactly alphabetical
order by name. Now that we have the concept of multiple firing
points, there's really no reason to distinguish between any triggers
and triggers on specific commands. Eliminating that distinction will
eliminate a bunch of complexity while making things *more* flexible
for the user, who will be able to get a command trigger for a specific
command to fire either before or after an ANY command trigger he has
also defined rather than having the system enforce policy on him.
plperl_validator still makes reference to CMDTRIGGER.
Let's simplify this down to an enum with just one element, since
that's all we're going to support for starters, and remove the related
parser support for everything but command_start:
+typedef enum TrigEvent
+{
+ E_CommandStart = 1,
+ E_SecurityCheck = 10,
+ E_ConsistencyCheck = 15,
+ E_NameLookup = 20,
+ E_CommandEnd = 51
+} TrigEvent;
I think we should similarly rip out the vestigial support for
supplying schema name, object name, and object ID. That's not going
to be possible at command_start anyway; we can include that stuff in
the patch that adds a later firing point (command end, or consistency
check, perhaps).
I think standard_ProcessUtility should have a shortcut that bypasses
setting up the event context if there are no event triggers at all in
the entire system, so that we do no extra work unless there's some
potential benefit.
It seems to me that we probably need a CommandCounterIncrement() after
firing each event trigger, unless that's happening under the covers
somewhere and I'm missing it. A good test case would be to have two
event triggers. Have the first one insert a row into the table and
check that the second one can see the row there. I suggest adding
something like this to the regression tests.
Instead of having giant switch statements match E_WhatEver tags to
strings and visca versa, I think it would be much better to create an
array someplace that contains all the mappings. Then you can write a
convenience function that scans the array for a string and returns the
E_WhatEver tag, and another convenience function that scans the array
for an E_WhatEver tag and returns the corresponding string. Then all
the knowledge of how those things map onto each other is in ONE place,
which should make things a whole lot easier in terms of future code
maintenance, not to mention minimizing the chances of bugs of
oversight in the patch as it stands. :-)
The comment changes in type_sanity.sql seem unnecessary. I think you
can revert that part. There's also a one-line whitespace change in
triggers.sql which can also be removed.
With respect to this hunk, it seems to me that the verbiage is
inaccurate. It will forbid the execution of any command for which
event triggers are supported, whether they happen to be DDL commands
or not. Also, a hypothetical DDL command that can't tolerate event
triggers wouldn't be forbidden. I'm not sure exactly what the wording
should look like here, but we should strive to be as exact as
possible.
+ <para>
+ Forbids the execution of any DDL command:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION abort_any_command()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ RAISE EXCEPTION 'command % is disabled', tg_tag;
+END;
+$$;
format_type_be_without_namespace is unused in this patch; please
remove it for now.
get_event_trigger_oid looks like it might be the source of your
syscache reference leak. It would be a good idea to change the coding
pattern of this function to match, say, get_foreign_server_oid. That
would fix the leak and be more consistent.
I'm all reviewed out; hope that's enough for now. I hope you can get
this cleaned up some more soon, as we are starting to run out of
CommitFest and I would really like to get this in. Of course if we
miss the CommitFest deadline I am happy to work on it in July and
August but the sooner we get it done the better.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Jun 28, 2012 at 9:46 AM, Robert Haas <robertmhaas@gmail.com> wrote:
[ review ]
Also:
../../../src/include/catalog/pg_event_trigger.h:34: error: expected
specifier-qualifier-list before ‘int2’
This needs to be changed to int16 as a result of commit
b8b2e3b2deeaab19715af063fc009b7c230b2336.
alter.c:73: warning: implicit declaration of function ‘RenameEventTrigger’
That file needs to include commands/event_trigger.h.
Please spell out DO_EVTTRIGGER as DO_EVENT_TRIGGER.
Another random warning:
plpy_main.c:195: warning: ‘retval’ may be used uninitialized in this function
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
Here's an answer to your review (thanks!), with no patch attached yet
even if I've been cleanup up most of what you reported. Incremental diff
is available for browsing here:
https://github.com/dimitri/postgres/compare/f99e8d93b7...8da156dc70
Robert Haas <robertmhaas@gmail.com> writes:
Some of the pg_dump hunks fail to apply for me; I guess this needs to
be remerged.
Done.
There are a few remaining references to EVTG_FIRED_BEFORE /
EVTG_FIRED_INSTEAD_OF which should be cleaned up. I suggest writing
the grammar as CREATE EVENT TRIGGER name ON event_name EXECUTE... On
a related note, evttype is still mentioned in the documentation, also.
And CreateEventTrigStmt has a char timing field that can go.
I didn't get the memo that we had made a decision here :) That said it
will be possible to change our mind and introduce that instead of syntax
if that's necessary later in the cycle, so I'll go clean up for the
first commit.
Incidentally, why do we not support an argument list here, as with
ordinary triggers?
Left for a follow-up patch. Do you want it already in this one?
I think the below hunk should get removed. Or else it should be
surrounded by #ifdef rather than commented out.
Removed.
Typo:
Fixed.
psql is now very confused about what the slash command is. It's
actually implemented as \dy, but the comments say \dev in several
places, and slashUsage() documents it as \dct.
Fixed.
InitializeEvtTriggerCommandCache still needs work. First, it ensures
that CacheMemoryContext is non-NULL... after it's already stored the
value of CacheMemoryContext into the HASHCTL. Obviously, the order
there needs to be fixed. Also, I think you need to split this into
two functions. There should be one function that gets called just
once at startup time to CacheRegisterSyscacheCallback(), because we
don't want to redo that every time the cache gets blown away. Then
there should be another function that gets called when, and only when,
someone needs to use the cache. That should create and populate the
hash table.
CacheMemoryContext ordering fixed, I wrongly copied from attoptcache
here even when the memory management is not really done the same. The
only place I can see where to init the Event Trigger Cache is in
InitPostgres() itself (in src/backend/utils/init/postinit.c), done now.
I think that all event triggers should fire in exactly alphabetical
order by name. Now that we have the concept of multiple firing
points, there's really no reason to distinguish between any triggers
and triggers on specific commands. Eliminating that distinction will
eliminate a bunch of complexity while making things *more* flexible
for the user, who will be able to get a command trigger for a specific
command to fire either before or after an ANY command trigger he has
also defined rather than having the system enforce policy on him.
Internally we still need to keep the distinction to be able to fire ANY
triggers on otherwise non supported commands. I agree that we should not
concern our users with that, though. Done.
plperl_validator still makes reference to CMDTRIGGER.
In a comment, fixed.
Let's simplify this down to an enum with just one element, since
that's all we're going to support for starters, and remove the related
parser support for everything but command_start:+typedef enum TrigEvent +{ + E_CommandStart = 1, + E_SecurityCheck = 10, + E_ConsistencyCheck = 15, + E_NameLookup = 20, + E_CommandEnd = 51 +} TrigEvent;
I wanted to see where the current choice would lead us, I agree that we
can remove that level of details for now, that also allows not to pick
next event integration point names already :) Done.
I think we should similarly rip out the vestigial support for
supplying schema name, object name, and object ID. That's not going
to be possible at command_start anyway; we can include that stuff in
the patch that adds a later firing point (command end, or consistency
check, perhaps).
We got this part of the API fixed last round, so I would prefer not to
dumb it down in this first patch. We know that we want to add exactly
that specification later, don't we?
I think standard_ProcessUtility should have a shortcut that bypasses
setting up the event context if there are no event triggers at all in
the entire system, so that we do no extra work unless there's some
potential benefit.
Setting up the event context is a very lightweight operation, and
there's no way to know if the command is going to fire an event trigger
without having done exactly what the InitEventContext() is doing. Maybe
what we need to do here is rename it?
Another problem with short cutting it aggressively is what happens if a
new event trigger is created while the command is in flight. We have yet
to discuss about that (as we only support a single timing point), but
doing it the way you propose forecloses any other choice than
"repeatable read" equivalent where we might want to have some "read
commited" behaviour, that is fire the new triggers if they appear while
the command is being run.
It seems to me that we probably need a CommandCounterIncrement() after
firing each event trigger, unless that's happening under the covers
somewhere and I'm missing it. A good test case would be to have two
event triggers. Have the first one insert a row into the table and
check that the second one can see the row there. I suggest adding
something like this to the regression tests.
Added CommandCounterIncrement() and a new regression test. That fails
for now, I'll have to get back to that later.
Instead of having giant switch statements match E_WhatEver tags to
strings and visca versa, I think it would be much better to create an
array someplace that contains all the mappings. Then you can write a
convenience function that scans the array for a string and returns the
E_WhatEver tag, and another convenience function that scans the array
for an E_WhatEver tag and returns the corresponding string. Then all
the knowledge of how those things map onto each other is in ONE place,
which should make things a whole lot easier in terms of future code
maintenance, not to mention minimizing the chances of bugs of
oversight in the patch as it stands. :-)
That means that the Enum definition can not jump from a number to
another non consecutive one, or that we have a very sparse array and
some way to fill it unknown to me. As those numbers are going to end up
on disk, we can not ever change them. I though it would be better to
mimic what we do with the NodeTag definition here.
The comment changes in type_sanity.sql seem unnecessary. I think you
can revert that part. There's also a one-line whitespace change in
triggers.sql which can also be removed.
Done.
With respect to this hunk, it seems to me that the verbiage is
inaccurate. It will forbid the execution of any command for which
event triggers are supported, whether they happen to be DDL commands
or not. Also, a hypothetical DDL command that can't tolerate event
triggers wouldn't be forbidden. I'm not sure exactly what the wording
should look like here, but we should strive to be as exact as
possible.+ <para> + Forbids the execution of any DDL command: + +<programlisting> +CREATE OR REPLACE FUNCTION abort_any_command() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + RAISE EXCEPTION 'command % is disabled', tg_tag; +END; +$$;
Yeah, will rework that text. Not this late though, needs more brainpower.
format_type_be_without_namespace is unused in this patch; please
remove it for now.
Done.
get_event_trigger_oid looks like it might be the source of your
syscache reference leak. It would be a good idea to change the coding
pattern of this function to match, say, get_foreign_server_oid. That
would fix the leak and be more consistent.
Fixed meanwhile, that was it, thanks.
I'm all reviewed out; hope that's enough for now. I hope you can get
this cleaned up some more soon, as we are starting to run out of
CommitFest and I would really like to get this in. Of course if we
miss the CommitFest deadline I am happy to work on it in July and
August but the sooner we get it done the better.
So, I've begun working on this already, and I intend to spend more time
on PostgreSQL development from next week on.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Robert Haas <robertmhaas@gmail.com> writes:
../../../src/include/catalog/pg_event_trigger.h:34: error: expected
specifier-qualifier-list before ‘int2’This needs to be changed to int16 as a result of commit
b8b2e3b2deeaab19715af063fc009b7c230b2336.
Done as part of the previous work.
alter.c:73: warning: implicit declaration of function ‘RenameEventTrigger’
Done now.
Please spell out DO_EVTTRIGGER as DO_EVENT_TRIGGER.
Also done as part of the previous email.
Another random warning:
plpy_main.c:195: warning: ‘retval’ may be used uninitialized in this function
Will do a whole warning check pass later. Can you give me your local
Makefile trick to turn them into hard errors again please? :)
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:
https://github.com/dimitri/postgres/compare/f99e8d93b7...8da156dc70
The revised incremental diff is here:
https://github.com/dimitri/postgres/compare/f99e8d93b7...74bbeda8
And a new revision of the patch is attached to this email. We have some
pending questions, depending on the answers it could be ready for
commit.
Robert Haas <robertmhaas@gmail.com> writes:
There are a few remaining references to EVTG_FIRED_BEFORE /
EVTG_FIRED_INSTEAD_OF which should be cleaned up. I suggest writing
the grammar as CREATE EVENT TRIGGER name ON event_name EXECUTE... On
a related note, evttype is still mentioned in the documentation, also.
And CreateEventTrigStmt has a char timing field that can go.I didn't get the memo that we had made a decision here :) That said it
will be possible to change our mind and introduce that instead of syntax
if that's necessary later in the cycle, so I'll go clean up for the
first commit.
Done now.
Incidentally, why do we not support an argument list here, as with
ordinary triggers?Left for a follow-up patch. Do you want it already in this one?
Didn't do that, I though cleaning up all the points here would go first,
please tell me if you want that in the first commit.
I think we should similarly rip out the vestigial support for
supplying schema name, object name, and object ID. That's not going
to be possible at command_start anyway; we can include that stuff in
the patch that adds a later firing point (command end, or consistency
check, perhaps).We got this part of the API fixed last round, so I would prefer not to
dumb it down in this first patch. We know that we want to add exactly
that specification later, don't we?
Didn't change anything here.
I think standard_ProcessUtility should have a shortcut that bypasses
setting up the event context if there are no event triggers at all in
the entire system, so that we do no extra work unless there's some
potential benefit.Setting up the event context is a very lightweight operation, and
there's no way to know if the command is going to fire an event trigger
without having done exactly what the InitEventContext() is doing. Maybe
what we need to do here is rename it?Another problem with short cutting it aggressively is what happens if a
new event trigger is created while the command is in flight. We have yet
to discuss about that (as we only support a single timing point), but
doing it the way you propose forecloses any other choice than
"repeatable read" equivalent where we might want to have some "read
commited" behaviour, that is fire the new triggers if they appear while
the command is being run.
Same, don't see a way to shortcut.
Added CommandCounterIncrement() and a new regression test. That fails
for now, I'll have to get back to that later.
Of course I just needed to pay attention to the new ordering rules :)
Instead of having giant switch statements match E_WhatEver tags to
strings and visca versa, I think it would be much better to create an
array someplace that contains all the mappings. Then you can write a
convenience function that scans the array for a string and returns the
E_WhatEver tag, and another convenience function that scans the array
for an E_WhatEver tag and returns the corresponding string. Then all
the knowledge of how those things map onto each other is in ONE place,
which should make things a whole lot easier in terms of future code
maintenance, not to mention minimizing the chances of bugs of
oversight in the patch as it stands. :-)That means that the Enum definition can not jump from a number to
another non consecutive one, or that we have a very sparse array and
some way to fill it unknown to me. As those numbers are going to end up
on disk, we can not ever change them. I though it would be better to
mimic what we do with the NodeTag definition here.
I'd like some more input here.
+ Forbids the execution of any DDL command:
Worked out something. Might need some more input.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
event_triggers_v1.3.patch.gzapplication/octet-streamDownload
� 8��O�]�W���������B[���Y��Cs$�$l�gOO�IL��cgm�����3#��d9 �v���X��h��hF����3ok����7��[#/��x�������&�XT�������S��� ��>�_7�0�����7��'����GYr{���3�����k����d����YL&~�v�H�d>�"o��&C��������Bj��7`6H������/X� :�"��8V� �JY�5�]������:l��}=m�S/�e��[=�2N�`�fs������y������.F������b�[3/��t�euz�b��X�ptI��,�B�.#�+��j���-��)�w�K�c
!6K32���)�����o���=K3������%u!���sU)�V��Qj��O�x>0��k�R�~�{�����@cU�jp;�����~�G#?�*q���$�eAUNI�����}�o�-du�� M�O����A�xy���`�X��0$�6��}���Y���tCT�_? ��WA*��.�0(�����2�w�_��0�y����,�G%u��6E]�&s�������C_��|v9�F�r�a����-��j��AJ���N�]y�=�R��C�D��*]��o�Y�d>�L�4�l&�,`T��8���4�iA.+0�������*���E��y�l =����eb��`5�X��Ta��AJ����-�A�W6h���Q���6}l�d�$�>�����=���S�;�)������z�_���5�`�dqm�����E+�\��547P^�mj7PTK�I��[i��el��e|��$����l�I�������� ����� +��Wq��CXb���g��q�I)<wc���]�����k/�v^���?���/�y��$���O��4���6�N�H�%m+I����������|Z-���{����� |�;iv����.�#��[��X�)H��A��;;�:�\6���M/$�m��x�����W�6�,i�x�J�C�:;��������d�%�Q��� -�d����q4���>�@�ZLe��f��M�������S��&eY�H���5���J��|)���+@�0MY��Kh�~7���_�5e0�Qu��3���.�9�B� �/���s��zns��A�}|��Xmx���x�E9\���gm���^�Q���f�=�u?0�g�u��z��{x�s�������'�7
�4�������]�m�|.A��)-���u�� H[�F�����8�94fEW�S`4)���[`8K-�e�������t@��sZ��+/�M�\�bl�����*�������Q�%�u����� �bQ��d2���s"��z�@6O"��3���E!�Y�Q�����r�"C3V� �I<�Y���E�X�g^*&'(������%s8���
PN\���*dd*"Z��`�ir6h��������n�����|j@D���|5�����r�n���WI����$���L�-�Z���q������n��1��o����N���kN�.�H��<��:�� ��J�O� ��?���/08�?�d����Z��$� �6\"*-!����5y������`����(��� ����k��)dE��0���F��d�������A��4I]�2�6�lg��kgg����z�v�[��� ��v�ow������5�[?��] 28�o�:����wO���lBF�b��l���&.kx�|�� ��������F�Na��X�1\��C�� 4dB����������Y��Ip�2�]B�#�����f��P�>`�i�H%�t�n����&~n�>>��Q�V��o�:����g�H>���^z�/�� ���i����s���Y ���}��c8���Q�{*���)����,���^ n[��Mg�E0��h�p��-7��SUC���(:eF<2�(���xNJu *|��,���(�&7���'Et����0g.���iNGk�a)|�*���?
�g�@_�\h�<���@j��^D�6C���0}���{�u_�a]����n ����q�E`�Lk�vZH�\��6!�wy_�a 72��.��jx�ry��4�V����n����c��`O�L,�?��+�D2�@R_Q����}���(N�����"7D?)���#���J��c�x*P�]��-�%�nO���q�$\�������i��<uS��q/������zU�v�^�_Z*�B���>���|
�`�*����A�Ue��e*��.� ��Z�^�'�b���r]����/=6��5��`�cT���)����^�7h
0���+q������M��
����8�bk�d\�
��^ty������v���r��-K� E�CANw�n�����i�M�u��p+��|6���r�����<+eMJ
�*~!� �=�s�N���d)�����g�38�u�L��a�>��;���{�������;��9���{�O�{�����u�A������~�"�Yl )�� �`n�b8��x�E���� ?��1����N3/�,�R
��e(�t+�����E�wn�+P#��6_kw����������u�m;l�a�{���������2?���C/I���� ��W��������:����6Y���V��(���Wo�&������{��S�����/�<8��Kw��$�3�tb*�rG����xh*6��eT=jrhQT�?��P�04W��"�x��d��*���������foo�����w���L��v�G��#w:�7�n�����4���-��W�$�����n�<s�
&QLn���R�~TJt��a�� z��R� �$�f\����tp����k��:'��g0u����!)�Ul���?��h!������P
������<U������6K��K����l��$4��c��{���WZ���8��h}=[GO�����9���������`���OK����6��
��H+��Sf��5J�h�@5�/����|���(�p�����@��t
j��d�Ay�Q�:�/���U�@|8��95Oa����=��\����� �\��h���$�B�;����v��;RX�)����x������}���R�����.�?!H�*����v$K�������t"���h��R>� �/�����N�����c���EqB����+�oBO�g��_b?(��+!�^��>��Dj2VvL,v�<��7��-I��-P���*U�I����SG(�Os2�a?p��t��a]���dR����L��8���{5N����O���=�����P�Y\����{��(k(]0b�{������, ��� �o����Y^��R<��wf���d�FM�{��f��L���]��"�}ki��yU����(��7�:���rx��,!6����Cf�$`�mw�Wo 6�v���%��X�V]������g��P�g�*/����� v����rZx���2�
*^L
���, ���"b4r2��Bf����=d�w���d06JL!9�Z,�k)���PUM�U�����f�+.�
���QP���P�A��Z�N�����[�<\�~��`/�gs�60���~�N�+�S�F~"�o���)�J�B+Z�jC������Q����; Xp�g��SVQ8XG�hl�Ex50���0�O^7�����4HG�i �����&s�,Y8���%�y1~=_aM��&�8�.(��W�'e�Eg�d���E4��������P�>�R<�Ei���g�0����F�x�Hn|XYUwE,�|z��4H
���Xgz#� ?
�{�a���k��zn3+e�5�L�!�n����������l�]Z��)W��TW ����+�{uGn�p���p��{l�x�j ���}=
#����H<l�E5�M��WbU8��X�!�������F��@�'_u|��n���`�C�@_J�~F���=�d<�������� 3�}�]-P�Y��;!^$n��C�l��,����m�tt�F�p4wA ,$'��?R)�39����lw�N��x�:�t������@��B�����)6K��g��*��]��������l���-}��YU��G^0R���zN���bw��oI�7����KC|sI�������c��Xu�5�]���#���b{����f�f�@
�-i��������5�4.��B�I4����Z���c�Sz�'+�s0�x�����]TV��3KOSpE����\n1��w�Y����k2���;�����o�h�d�n5���i���6������Z����;/��h������Z���+s�jK�i�}�����_&~�j�O�{�>�r��������b�o<��i]��2�Z���G�t8v7K�/����{��R[���&^�0R;?����|�=m��$����)�@��q�6M���<;s{���9���(����.m�=k"k�4�}{�Q��}�Q�����$���n���������0 �@�f��%y�>>��e��:lS�����E �}�e%{������+�����L/td�)Q�s�e��v?����y����Hn5�#��X�l�L�k(�j����6^�3��:J��s=��9t����KK:��T
f�@��;7��0)�tP��
*e$P����\-C��X ��2^-�`��%#���H�`V�(�=�u�J �D��� �H4�I�8)��&%. ������AIi*$)� $�p��40�9)K"}k0�):E�AJ)um�J�J0+��@V�5 ��5���^�m�+�:�6��CW^���"��f����w�o�[�������rm�%�&C42��G������"q*�����IWB��<+�[b�,2�0:�S���>�;�j��U�C�DG'�5 Ey�!�=��(�)�����+����S��K���V�!�������'6��������p��aA���%��c������>NH�U���cy=����7X�H�8�]�I����*{��~���h[m�L�@��@���j[��L������.9r����R��gW��O/��8� ����u�L;� pa&0��//1�E
ND5�k�
�j�W��7|I����4(��
"tH��s�S/E��O��"���J�=] �'��i��R�O��1���|�-�Q@���xFGn��]c�W��?�O,�%�5l���)���^M����9[��"���iEO=��4�
Q~�SM�myd-m_�����X9�F���b�s������G�*^<Ll���u������-n#���q�|��^e�)��� �0�Ft� �]���/6�)_����v��~��,�=\t���JP����|����b,����w�����L�
�T���t>��I/��:"/��A���9d�^F��������/���)�����!XN'���������,���`����O'%~x����mXZ�I_N���91z(�[��$w�/
E��r{(�B�i!z�B��S�����E�3Gi'���Q�=������_�(o!��\�����Q��<9���tT��)��I�*������Rn������#w�z��%�3�H9��A���V���������cyU��\0��.,�$�n��e~��r�A��_���b�������-���������x�kX���~��!��
P�}�u
Cb�V�84^uj���|��S�SQp�U���0��7���st�����n �i}�}�s�����H��5�&]�;`��Ir@����|�*�A�i���m<'����������x��bm�N��ou]�b�q�m�C���51����C�} �X�����>����Y�/�J��� ��+1�o|O � �<3Z�P���b�.�x1��b������N�N���)t��B���
��]Cz����5>����at�#�G���i�T�Sw�V���/���P�������>���[��!� ����Q'&�4�CbdIF<�d����s'�8-;�h��UqV�NpBSc��t��.��1����PN��1�b��q��=�E�C�n(h�I����9�KZT���������Q;������j]>������q.�/����\\n�SGK���$�=�bL�v��<�������[<=(^_�|���W�R����5xV�3|��kH/����k/���q�#�u�b��>�7��L�����{W�M�B���&2J)�e�������C`��f���R.�E9;fd�����R�YQ�/�T?�p�4R����d:�_H^�br)^����8���QMUL�D���w�_�[��Ow&V�,P�/}��}��'����ds�����
6�!Ovk��AOe��!O������qk��?s�
��&��������|7����m{�����x{l�����zHiFc�
IhOi
�K���������S�)R���L���V�%~[BU�%��y���m�=��y.\���'.v�3'&���N����_y�'��tL�7�1��C�>��X=�f\����G�A���-!o<���R���� *L�8�v����q<�[##
����C��g��/G�s���M?�-*�0C(��v��G ~���| �S���#k#���Fe=d���p��N�o����C��/���{ ��j|��s�;� yRN��r$\��s/�ng���6��b�E�x�����Q�\�zc����"8��,�f ������������������%�g+I�+�X�V�V����+����y�9p�I�z{����j���u���=��N�����W����+�+kq���9�J��U��A���
{��LK�3��e[���6X7�)mn���I��u�r��[^u��Ud�����
`T�s�k�����m��S-lr��+B�r���Pg�x�RbN�U����f�.���$�T
G���f�A@;�u�t���=�������M0� �[d��X���M�gG ��������"6@�0���w�@�o>�8@�l�u���
���=��bH F��I���������D��T���|�f\K����"�o�AQa ������L��k�r?���r�\���L�<�����+���b�_�\�����[�C�N9�B�Y���������V�
0����y�i5[������z#@V�>O�QZ���ujI����_��+���8�Ao^��������O�{8���$V��f�����Dc�� E�K��T�����F#� ,��z��l������
M��39���{B��YB.��E�E^�/j��/��Fo�.�x0���#��F�F���"!�n���N8��lV;RYB�Dk��������������'�
�;��H����a���B$��{����%��+� G�*�w����U��3a���K��[�K���y��g!f5��tJ�w�3K��WaKwH�w.����_���g~�.�t���3��k���9p'�M��XW����e�[$���T�l��R��R%p���-�*��w���o�y��(��xW��%�����th� �_�������op��KG��-���|Q<�y+/� .���<6��^`�w0eoaza����jhj���&��������:G����,
{��$�W���8���1��J�w��dO�s
��&�7�F&f�( �(���'
U��bE�h�EGA?@�%IIDE�^o��x�D�����U�a� �6@�ni�������'������������$ �9����{%n��w���G��I�������%7�0�M0�0�9�z��}�Z����*�0nq����j�@�=����*�=��.ij������($>SL���?�� ��h�6!
b���q�b�\���^
�P��WH�:�f����)�w���C����Q?X�LGS+�
6�V�����������Rj�W���#O�!;�{L���t��E�+��(=�-��QK�M��+��{�F�y�����E?�>@�.�>&/w�������K"o
��I;��H��xx��^LQ.��Ni51^]� �)0lP�T*�9����u���n����<���7tSM��(�R9��'M�\��q������pt��a��@f����5S/t��:����>��L�.]��*^�`���|�����)n6� ���f��Q����e����B��w3*[;����m�z���0���x8���FM���U�����9{L�k�v����pLre8S* ��r��au���N�D{<��������o�*�y��f��6��A�u
/C�e��M�2�l�i�u����^�V�G��r���Tb�� )�'����
+���pW�=���j u�8{�|������2����GW��@��,�:�x��C�c);��t���Jj`��gl~<�A��j'��x�Y��6.I�&�5�@0�������Z
��.
�+-����hTZ�`�R/�����<�n��d�����p#��e����
q���)Q��s]���p��:���w�h@�p|4^`��x��+�Ny�^��l�N��6u&)�B��}A�D�����
�5� e��N���i�.�� ��a��S=�����o���y���-��}�|+Uu��5��_`#�6�������JeX�D��+)#���|�J(@[���k��Yu�&�Jy�w���A4n�Im��w�KG��[�r)(4�Hv��'���1Z�\d�z;d{ �����d�uz���`}]��q�o+r2]��:�g�Od�8� Nz��*N�".�a���"�M�d��� 1�l��B��'������)�v�/��9O��"�S��I�G_$/�sGw��t���)<X$���0��v2��H�Gz�O���P�#��6��:Kv���g�������_�qR��L����Irl��!d�����_}���)i/Dl-,K�\���"���������sF�m.9o"�:s8c���ug-}U���e ��V�)��&��,{g�S�5�"jC�M&���x�o����]�{�RJ����O/����� qJ�g &�9n��F�5�+�L_$tV�e�Q��tj�y�������^x���C��d���.a��0
RZ���}
mp���vFq @���Rw.���gB{�."���L4"��7�h:2@a���������B5��E�k �x41/m+��v�q{q�#?��ef|�3wN�G��+`�������t��\RLF=YD��S.yF�-������P�����t�m�Y*'����/�O��L�++�S����
�a:c: �p���r6y�I8������$������.c9u�@�3*�}p�����N�B���ko��,N���R���>\���Ei��+�Q��Ve:��"��(��u�b�BnD�x�C�2��F��i�/���=G�7~�'� |JH�C����q� MGf�F�D%�,�d�rJ��T�L�-�����5�R1�@�I��-�.��7i�n��xf(�N9��r��|vkc�b]�����t|Xin����f
���y5H�5��-WN�
�-Z�l�<@��L��$c�/�%���h�����x�a���Q�s|�z���cd@�����Oo��F`�A|UZu��@��4y����k�'����Pi���h�=z�2g��)�]��W��[v`��B���#t��Ln*3@����!��2�K�)���
J����%�F(�`�/������FI���}����0"9CmJ���dQ�B��++7~����w�#�7*sE��d��J�����?������������E_�k&u��x������#��������Mpw/�����||��s~�9�z�K�\������~?F�4�O]r���.TN����m��X�.Q_!�k���B�[E*��\��+0�_IN� �(
�!��$y���c�K=;��A����n�X�+�� oLG�Me����x�T�i��/>��~��Y����c��sZ8������@���t���jr�����B
�%�$�������}�$%i��� i���~����/���pc�r<�Tv4��$C_��PY�=R���uX(2Eq8���}����^k���$�!�k�Z��y�j�����$�&��>}R���Z����������{�d� n��x���=���N����92��&�y�|���u���Y�)�%�7� ���2�~�����v�9N\5�z����I�M�$��e?�p'�h a�~@�����bB~Y�������k��17�R�����j�(l4&7 w ��`L�;�x8�����iR�G���)\0���5��O�)���8PS�����6AF��?Ly�D�J��CZ����=�J�&������Q)��g<���g�������;� �:V�X���6'8���EK�&+�����p���k_����C4*{�{BZ:�>����`����y�D4��x��BN��,���~�����u9`�)�dK��0�<�%e*6-�����u��������� ��Pm�������l]o��U}�����jh4!:�tjGL#)�*����7�>��]�yB����} ���@�����c=�����7Y����{wl�������Xb��=q���g�B3!�M����`^dr��QP�vCNOvN�a������D�IM�5X~)��[D"F�Oy�
\5�b�I�N/y.��2��qo�r��rl UX��L^�r�K��u�o����\������)&&tF�K�7U]Nl�����1��&SB��G�Sh���'�.����i��sW���(BU�K�~zD�v9��2n�V��?���&}����%;�|�X�NV����� fP :/�%�J]$^�p���H-)���K���GL4����7�"-|@~&�E������\h�|wJ'W�h���v�2g��3�O���C���a���;������SN9^�
i��H�6�M��9���ka������(���&����I0������,-��n��'8��L3U=�9��k �<w�^^ �r �uQ"�����b����E��B�W�A���%%'���e�l���Qo1��e��b��4�e��PoId�9wGw��%s��%�t���,��d���Ia�����N�����+s/�aS8���e=z\��L�E�[J�P�����^��K
����!IUj`��7n��{�Cq�����9�8K��g����Xv���\4x
O��'v��RW��A��Q�3�S���$���%������;A���#�|�����Ge��pHw��K�#:�#����+@A�q�J�f(��'�L�DV�az�N�sf[�/#�/����Q)�<p��V��>�Y�\��|@�p(Q�:����+<y>?a����. ��}�& �vD��}d&�)�~��ZY��$�c���Kt����c�$�1n���\iI"�B���zp5F[���>��d�B�!t �P�G\vu��z������@��;��T�� ��8����,a�`[�d�(��L�"������$
�z+�8��\R9��^�C�+0�����)L�
�0�^��r��xz��/rP�(AZYy%�����rr5@Q�58�dR���bb��I�ZF�dL��H�&�.�������0�q��{�:�.Jo������ @�!�az�h�B��b:QL��0����66i���5��49��uTb��p����<U���W:��vt8Z��I3�L���&��Kr��{�����Rr(�A������
�S�:E>��nB�X��@
�gL�AZ��H����(�\������g?��Lcu��I���A>��K1�\�m���f&i5�����2�r�%E��P@��\v�U��RR}�y84|yj��I� 8N�����D����|�W(a��Y���$�{4����#�;��/Z��7D���)����M~��AibR<f�M.�C�+��X�3�6�����FQ�q��iZOI������(��:�&��+�U.����7�t�6���m>P���<���~>`�]���d0����;������s���-�#�Y��1��[���Xpr��F��r�{[*���7\~���QxnwN���h�
�M�� �6�RIe����j�K����n�a0u�u�������ue*�0-�O"���
���'���s�)�"U|�y ���|��{6<�^s,�Ed6���4"Y2����m������o-�0)6I�.���,�2�����<xz{60����X���b4��wvNXf�;��]��L�^hx������o>T�?�M�i,i~Y��|x� %���s`)��s�I�����6�yX���"���;n�utL�����hG!-E|W��i.��L2���-��CnK��`�|>�T��
��qc���>n��i�%:�w��:�t��wmgm8���=u����M84����:��z� ���|������;�@h ����%�dH�gVJ�w:���<N-����j��d�[��y�Z�9�j1
*���c���qm��$�<��������K�M���L:���r��o��Nf����'��H�g(�v�{r���XJ���<%�{p����\����J&��+%}?c��f6��d`�Z2���[��xX�2�!����b/�f���33�}N��%�XI?$L��T������I��Z��\��\��\��\��\��\��\��b�/���{�\�����������������������������{���\E��������,���H����:+�`~������k� o9�b�����Nj:Zx�� m�������n�����X`s+z���X�l)pcP�3�: �+���]��#���H�zq�����`���0��8d��uK��a�S(��E�������V�N\3U����wo�����y����6��81���7��NAxLhz���D��t���_exl���qn����W�L�
2���d��|�%F���\wg;t�����>��a^~f��.b8`���(����;��D~o ��96�0sd\d&����\9@��|,?Q�Z������9����p�Y���L��Pm(Yd~�1���eDF����� �DL6���.������3����8`��>C�Y p��g�JX��v��rVC�@#.F�n��9`�%���X�����&��|��O��aF��X�#�����~,�J0L�V�����`�������r^��AK�u�
v���\�7���m�O.��
����:�~���kt.���u�� �X���;�,W��X�<�!�����O��tu
���t=c�j~�;m�k:�o������#�!��NN�h+qMX�0���+B��d�ZqL��j�Kv9�����C_1��<=>q�k����?������&����J�����=�aO��cLg���B�KYzR���<���~�S�bXg�U�����.��e�5������
��Cr��;���a4����J���|��V4@�����"���������g\��5;|��A���b�T�r����hM�q;��vmc�jz\�b]N������(Q�v��LA�c�N�L��
���%�#6��1K2M
W���Eai��AJ��z�K�{�:����Y���ZB���M�������J^��������%�Q��Tg=~,t��z�j�Y�Q3�z�!��JR�����y����3����@�jwey5u�S�[�z���QOq��zJ`8���3+zM����0���D]G�����������7YWz�r��g3G�|J�����
(�[o� ,!�L� �>h":� �e�s���w&$%�KI\]��,v���W-@:��|�j�p����*��Fq|��Q�a�z(K��KYG�����V;=���@�
�r�������=�R���j�c"}����>(Lq�����H93qW�9,������?.�[fCrl��3����0d��j^����3�����������!z�]�u���,s���)���M>��j��P����2����R����Z�c��\�+�R��v��%���(v�)@������`�N9���|x�s.��o>4�IgI�s�����)�R�����qW�?���w���g��IU:j���a����J�����8���.�G�����J���j��,����R�q�v.�0��J{}���Y�^�U�G������8�Lg-�*���tw���>�2�\fUuIY/+3!��#�8��b�(J\������������J|��d-���U!���������e������d,���vr2���0��8�N��Y!sR����*���c��V���'�T�����]H�T���F��>ZA�q'Q�t�q�8lc��3�?�J���{�v������_GP=[6{�F�^w��7�$��p���=_ (�������V-����)� y���&���$����S��@h6����LK�8$�H}��R��cC�\_�����
kg�<y+,0�,��$x=�r�9��q�}+=:*�_`L���]G��lW|�����W���8����m��,�N����:5+A�osh{�i�@' ������1� �h���������S�,!�;�?�`�tOO������ln�@P����TT�?����2��J������t �7h���];'����^&��K
��<�UW%���f�-�R+_�g�3va��9rtr���'�a��Q��,`��e�U��x[�j�� �e�~ M�5�U��@���^y�<B�7>��q�?�z�
�E[���{r�d�� !o�!g'� �AX�h��@A2i�������-Z�\^���z�kW��T����+G���&
��W�S�A{��c�a6Xx/L5���H�����;6f/IY$w���{����v����p���,T-��iD�2��S��k�8�5�s���T9����9mx��I������v���G��������{@���rm����������w�kG�^~��#5�v<��;����H�}��u�����������U���������1�%;������3�B`�������@�5���+!�8^���q0�4��g6�� �.|8���kW��U����Cd�����Z2���vO����E8��jc�v���D?��M�U�a��z��Q���U���A�-��o��w+d*��:��H4K�Df�Lp�aF���L�D�@,,��t�/�>�9��8���j�I)�{S�\�&^����U���s���j����mz�6*��EB�'-\[)
�91�;0�=� �16�2�"��%R��ej3��;�����������tGc��Mt�(�-��?���H�BQ����P��-�����DT��i��P��W������/Jo�s"qJp�s�8�pRO%�)�c�N�#�h|��Z.����{�a��C�}R]�~D����i0egA@0���F����lR��n�i���J���EH��������
�C�]'�&�����y�ly���g:��so�?�NH�=��5
��W���� �nE�����KY /d� QB���
yi$�(N�.��S}|�fJd.�]��|L3��;�a?� 7��JO2!�&�.b�3&�Q��h��?�� mZ;�����W��H���=P�a����F�eSn�n�I%���������$�����mW*��u;j�n4��u�G�#�6��I���������mN�O-��:$�1O��8��v����O�����{�~��=}x'+4����y6��?��I�!�\�������
~h�|��N6N�(��[�����a�����O'��(�RM��:~�i��w���k������Z��<����f�a�������>��Z�C/��.����v�������? _���b�`�2h�(Q!9/���^�x-�����yl�[����<����}�lB�����'��|�����[��vk���8��Z�w�����S�g����a�f�F�e��:U1J�T��qpk����e�
��X�����=8�
TC�p�f���L�/�6oH[��mo��/g���
�j��N"s|��! ��_++�z�h7Z�CG�I�Ti���bssS`U��lif���_�#�7�KN���3���-��j�;o�6��5dX�G�ZiM���I��MM��l�Y�>|���C�o��Vi�������53����AR���o����S�'�����?4Ns/A�"��&�������7{f�|���J��T��_>�$h��{|��F��]ak�q
���=�^�hJ��p��M�;�X|�n�q/���� @���~?�E�n��9 ��0�I$|bK/��?a����� �-9S�����Q�Nv���-���\0��NR������Fo��p�W��zu���/-����J�T��!�:������"��� q��A�p�>K'>I���R0#�0���Z?���?u���YR@�A|U� %C��~����YW$~; ���=��W���g�Tz����4�&��S��:��)k\ F?Phk9�4Y���b�{+��t@D@�?���m�o�W�Y�z�1�Ed�,#Y��[gc�b��S����f5��A��^
m����:�[(���������&RYkk�h�#� �\�H��a#�Q<���0@ ��k�O�:��h�����-h�g��&V�����X��$4�X��GD�wg �����#���2����Z�P����409�����"����6�B=<�r5�M�sv��3e��^"�
%z�ICy?f����e�����m:�Y�qi`�X�����Mk����\�N���z�v�|���V�R���-���2�X�q���r
k���6�6/��6�X�a������5��M����d�N$�����Y�y����
�����*g�K6��46�j/��2;�=��|��������3�s�������]�V>�v'�z{��h3�e;c��]�;��h�$~�lM�lS�E]�A��i��
������b�*��6��Rn�p��p[i���:�^�q�+^�]��r�&��8<7/���7���%�_��v|�d�K�bR�F�����1y�K�C�Y����a��L�A��M�����j��K6�����r���De ���E�Q9N{���E/Ry.~���/�8������ooY��K7������s�f���z��hv���������L���,��?��w� ][�9#���tj�;d~�������Y�P���������e^�&i�X�������{�`����c�&S��
�j��Z���.wJy~����YN������:�
��M/z�K|h-���9���
��"���u��,�x���-���8�����.xy�~��np�����l�f���^�������O���`��Z�+��}X���,��nn�+K�n�&������bw��!��M.�?�oc|���/y��e�Ey>��C�^�\��#�_��>�����W!�/���.t
R^�nn�+P�Va����(���4G.
����-��r�X�!#��m����8g\�I���pkIT���>���R.} u����T�t���2v4�tzP�s\�7a4N�q*���A�J���� �!�u��6G�/$��XK�G��b/��l�����h��mf�8F�������f
"k'����k44dk�?�vf`k7[���|dIR������"���i�9�pT���+��K�Sd�v��������z��T����!k�q~r|X7�5���'?�n��f1mgk�x��R�~`S:�7o�� ���~�&��E;8a��e����{[5~I_?� g��I���
S*��j���W1 ����!�gB{
��l�x���}��2wn3&ao�D�j>E��`R��d�6L.�)���������s���9���IC�5�q�'H���VlhY�g�n4z1���pr����*��~[�j{��3�����q�B},������\�sZ��b�#3���1�D?�~�xN�������[(M��������3��]wF|(�^��_����"X\�o��^�C���&;�h8G}�9��x��)Q�r�J�\�y�pdhP��`�H� �?��LF���|�8}�������c�����~w�~M���@��d���z��d�CA�I�Q�'~�^f)���������Q�@�)���f@m���-����}�{�
tR��B�'�k�z��]�Z9
�I3�!tW<��7O�)g'h'F�i�+v �f���$ �z|����L�Q�z ��l�c$�R�
�h������� ���F�
\a9�U+�f?��z��1��8`�D�����F��l�[g
������O��.��<�^<�qo��mW�$ ��OtY���|��T
�����r���h>�������4I�����a�|��$������k���(�'m���|�\=WV�=
0H8�+����%���"Nx����#w1�"����]3|j�0�����{�.`�|i ��3��C$�y�������e~m/��T�����w�
he���g}&~8��V�b>|S9�K%�[��Rh�U��l ���SWQj�u�4��67���L�a���z�S5\9�
�<ggyko��l�K�Y�7U�$���$QD����<����^e���:���A��0���U6H��k��"1o���E�9��UoZ�K��W3B��E&����J����.gww*�Y�u>
uwS�td����x�a9i�������N��Pv��nv�{^�\�-��j��
���|��3���MlX����>Q�Z�lv��pc�h��]�y�K2�o��$�:A�
9����mo�����
����M�������� �v�C���9��J���7y��?�{�S��W��F��m��s�.3�m�'�9�9i��J�
��L�](c�#J��PJ��+$��QO�YC@�v��Z0�8�G�5 ��g:E�Gph�Q �J��D E�e���9!����$
���!��,����D����K�t��Q��Z�o�_ S����r�AG��������#���'�8�r����PfE�:;>��X��� ��E��*p�A��Mk�G�9!�d<�H���fg�iL���|8btU�jL�95e6(=����-����w=�L�
��JH�'�\t�\U�R�0�Z0{~X��%Ot���xM�)����&{��$�������'�k��W��f�������C�#�y�f���R��A�c�Nu���u*f���c�;�Y]<}*J*�;|�'��!�G����L����'���6����u���������f��{��vvA!q��
c:��Ty|H������8~�W/==,�[Y�p1��������=R��D�p����R�<�j�2���)-��'zz����D����(�^`��VU22��M��Dc�y�'����]>����7�����
C2k"+�=v��\��;�w���/ �7��m9��J��Ya�
�����%�b"�C���� t�De7Du]F������G4������x������Y����m�/�����;'�S4����{��Q`��5�@?���� r�K)��9�����3D#~G��+@����2Q�����p�� ���������W���|���M��L�ag��p�����{�qa������`\O�u!J���w��Z�_buU��g�'?��u�����!����� .��sqD�v8�H�.�i_ ��uT{t�����@ A���O�!W�2E�*��y�N��/�b`����z���[�(����^�z[9�`(�F�����|��A���oJf�u�����U?���|��}�Z &����#���Gb����UL��q@�J��W�|�QW����H���@��c���H��C�u�*�2n>0���{���Kp3E�������2�<���^����i�?�f����������5��?�������� q��/W���
���hM����E��r�0&�������M��������5qj,}�rD�o6s�n����4v=�������<���n
���
��8B�p4�
5��'�����N��
C����a�!���x�F��Q����Cz_Q�,@��N���c��|ZY���5\7�A^�J�7/����`���+����t<���px�H8��r4gF�| �S(���,��/�7d>�Y��N�a���������L�����3k��t���������e�tYN�i�H���r""���:1k��;��c�I$���������{��.��+fw�o��U��Z�(�E
0��|/�m��S�C|1�&�:��.�}��XZtNt)��-VD���Z=d�*��5DcA���W����k�&�*St�9a���;�(?���c����0����!���!]gC?�]�����K������Q"p
w���zH-������j}S#�Q�6rD��N�aG�8�T��[2�?����gR���C(Oz���A ��g8�4c}���xT`0����n�2�Ru
�)��x��v'*�56J�@#�{`�����F)F����?����7��������Xu�����tg�sF�8�l�D!�n��#��h��eXXIN%��M/4�ZL �a: ���c���Iy����� D}k��\"A���L!X#���
_C�**�Z0�yrn����u���\-M�mO��U��2=s�����b
��W��G�-i�K��������
��)L�R\�"_@��
����H�2�2A���Ye����u���
��N�'����M+�E����D�,���\�KO����.K�U���M^N�D�p1U��+a�Z���O>�9�a��8���s+x������?�[?t��k4=��PlibrJ�\)���;^~:P@�������tT:%��qIN�6E��iu�s%;�cb�
�:~E^�3��{��P���
g�E
����>�;F9Z����8���J�L569��&��(v8M���=M1hB���E�d�1,�����n�f:P�.�q�c�$�2���T&��bC�����|��NU�he�G���@�b
���+H�K�M�$�v+��Io=�%)���w@\��{�u�%���9l�����K�C]i�u�OG���u�1-L�Qwy
��e�A6�>u2����0 �n,�����wI��qV3(z�
��%�jtB�H{�Q���
#�t�������Vv�4F�j?4lc�����2����3���d���3(���}uQ�U}�T��-��q����x�x+���Q=N���g��d�n���(���z%���G���{$�t�%��YW]��l�xy�[,�������q�#���$�R���RJ��=����:��F�Dte5�X67,�Ay��R9����-IR��3%$A��6�H\M}�2�O W������ *F����p(��-l{*�V�������
�����Y��AQ*��� �����s�6�r�<Kz�����D�Y�`Qu����h|�!�qW �R����t2�]��w���7�v��|w�������`e���_N�w�0>�sJHW��t�'�X�Tb���\���?Ob*bN��N�5e(7@�k���D�'i���������?�F ��HY
�b� �@���q�9Jpg��MY��]1���p�+��W���gG��1%�4������M>�{j��m&��9$�l5Z�Z�#-|�=
(U������>n6Z�p ��*"R^�p�qIX/����~"
��ju��,�,��dB?�%e%T��a�\F�fy%��-��f*�M
�6�������A��� &x�qo[�W���!2������]�O�����A�!U,���A����3�DJ�R���$r�\�����6����,����'���=5H�����3`�.������3DZ�� 5^V�8L�(�C
����~�H�S)l�D~�R��; �7q��m� ��/���F��{
��+����r*���$+�9L�xT����}�hp��������� �b��C�J��������# �|�X���VV�,l��@��������y�I
&e�i?������i�!>�o�(\$@l���7��/�P��ehB�'}q�S
F��6��n?��3N��%��-���H�"�9�x�C�+H���S�Q�1��!\�H��9"a��#�3.'Y���8���C�h&��r��"8*=�D��)�!�IR}I �7��].�~0���2��I��O�$��bY���e_�����H�����X|p���d��H*q��������-��%1i�K�O�bE6Z
o�����}�6X�A��F�q���;�A924y��8�[�+�� ��#�m��*R
9($\�Uo�n8ih�������D���D�rT�B�ZL����N�hz����#�K7���G�KqdFZ�g����(5,�gD�����Rb�����������Q��m����
��I��
���'�AyE$��*��)�K��b>#_��N�M�{��'��0&�"����+�z��x�y��9��n�J����"8E���8D�2#A�2*�s 9Uo�&|z�wTs�-�}��J��b�S����c�2�=��������7t��<oy��K������g*�(��HU��Yb���]5�P�*@OWMn�0m� 8n�?��Lny���� )�& ���(�T[�?�w;�RGJ��W)����U3����������zI�*�YC�%q��W�S�����eKR�� Vw�*LS����-�����qjPH���u�������� �2<{�|3����QX U�P��<&8|{��Rc�� S������]JO���zF�Mo{"=�u����w$���������C��?#K�������T���"vf�%K����Z����'f���V��DW��zU���4�S�� �����t����d?����&(�V�vk�+��k�(�����4�M�OR�ZfH��%�7��k���Dz5��4E �DI��@�a�t�m�q�}�������sT&��n�B��,Qi����'f�"��~e��E�K�|e�Z��g��`��0������ �����@Az#�S@���{��-[��lY1�y����m~x����~gl������OlWT��!�IZ�����������
)���2+�%��p*��_&~��G��`g���\}={3���G�{� t�b: N���`�H'��%���*��8Z��d��\��'����;o���E"��]��rN�}O�c[���]��$�W��2$O���l�����f�Nf�]Bkp����J������k��IZ���G�-���|[����B�� #>�*2��{�Z
�&fRr�a�='�u��h y�0��+nTR����4L~a
�z7��R_|�]1�)�&;���EC�l�A0�u��3W��#4 M��p�ot�0����x����'c��� $���#_vy�S�t����;���~xK ��H�T�i)����q!�
u��5�
��7:���4����j��`u��U�����V�$�yn_�A������v�mL.��|��YWUR����RZ��-nP�Be�Om4�+�)�`���_���{��1��\��\zM�����#����'|��gc�������=h+�|��gWj�n_�;�5��MHJ�(N|U��s�a� ���~����.��)��!bD�4,LcXc ([�~u��I\�����������d�?Q�72�2OCHrr���U��&�\���{�r ���2�<N�����s���G^���<��vH.�_~�]0������}<���y��bCH��2�A%��
���_�[7���]z��M�g+�m�����.����[�z$�c���W����8�~zy o�(cdRI�<���H�Y��X��r(�8�B�f �����KJa9L�P��M)��JR��<Z'�QlH���1�2�����|�J�=�0�h:�*:f����K{�jZ�w�!&�{�}�i�~�Z.�I,�0QKHODB�65��3����`�Kf��{��O�u�MI%���H��B��F�^��0����?��fQ�1�u
���S��0����&��5���X���Dv���r���7�#�0��������^7��f����O�<w�Wc�)�i`M�pl@��v� ���4��G��z�<j4��?�����c@��]�����R��?���������7�\�~w~�:F���HT����5$=�]�����4W�\���b��u�����g���Y4�%���s9�;-�&Se���w1��\�R2�,)�K%1�,���E)�,{����t�F�2g&�HF���E+��.U�h~~ ��3� ��hGg�H���N�{����������T0��!������n`�� �q��'8��{�H�����8G$B�g#�!$������4�cN�����M� ���X.��P���>p�u{{:�W��ckO�z:�sM��(�;H,� ���^+cv ������\j����������x����Vv0����D �u$y/���9*;W<#ze/x����k]��',H�����3�Rj�����,8���s��/�f�nc}e\����V�Y?>m�=D�����$eF��W�������k ����=��[��Zmw?q����������J��-wIR�s�e�0�����yl5��/@
9|�����gP��l�����Krl�Q�}v=��ny��5d�����*l�Z>Za�6��Od�<���K���U����{��Ss���������O��w0i�������fcQ�^J�Q�'
�G��J�Y�G `r�l��%E��:t�i�6��
fi�����f�5��)O����V����t�`��p�0)l�fq`R��
M���s�4g�4
p�lA����Y�7�XNn��a%��8q���q��!�m@�@I������j����V�]������M��Z�'�dB.�_��NA��#����P$d��^p���|16j�A'�)��)%�����y�@^9!*[5K�X~��=�*���#��%��d�"6(��3��G�Vuo V��F��}�zP���v�����?V�������v�~x�2 ��g�q����������.'pZ���.v!G��T�h5"�J8j�j�#
a
�T�����9/�9^��^E9|T."P� { �����{��,)���
�������Q��3y.��Ft�L)^�����M!:=����M��o*�"�{u����]�j��6�U�I����v��q��WP�8P����\i��f�%��ZkHs����0���Dm�d&��F��
����'����O)����v��R��,A��!��(:�0o�����N���%
\{!�rxeVR� �W�3� ��ID1�1a ��`�~��z�;r!a@@��Z�������~�M]�Q`w�����/�%������'�7���E�7�����o#���(sqQE�Y�/�3T�!�6�K2g+�J_|aP�'a�n�I����{{��!��_����x2��Tr�9�}����=���do8��8����"g�����_�E���R�f�����v~f3�A�*5�Xfkcx�,��8 �(�Ez��&���r�~���4C_��gz���m�dU+�x�n��C�Z���fK��e�b44Op�����Y8�T~�1��Z���=N�4o��<o�U�����"H ��<%�V��t1���>�S�U���~X�WQ:>����};]_M6A��������J�����R8��z,{�\��w�L�6����������i�%��Km����/��uv��3��.eok[�AW��Z:n�A���0X���e8T��M�TR*�'[i�^��C5��[���K��iz8���{�qJ�����[�������/d�C2�W���N:�o�:�N�yz��)����Z6nd��P ���/���@[�P����A�o8����B�����x)u�_{w�I���c�Bub�����4��v�%�%?@��J��Y�l4��hD�2q0��g*cJ��S��I ����1k;�����?%^b��yq
���~^��lC�����;��_W��]5^��� �6${$�\;[c��k2y��$K5U)���]��J��[�.v:R�za,[�v)�n/���1[ �J�k�hs�;H���3���������IX\%� 1�����0�F��Sz���n�������B|��QFE���R��-���y���*����K��{���$��aE2]�H�R$��g�� �ZG���;>wx
o�REB��`<p�l�����)���q�D����&�2kN��R��*�����H �,�7:�b��W��I��[�
�k�Y�+����S���~K���t�����sx���ksbO�J=#��;�'_a;�.�����z������x���$�N�'�8����R����3������p�VH��p ������*�;�����:l�p^�i�&����'�R���5en����w1��Q_�_cXt$��O�~�
�Z�c��dt��FHS@���������������n7�����M��7apK���(�h�P�(��) ��/ � )�$#�l�C��j.(.���=�S��?�u�(n>��!�w?2(�?�9��:���MB��\���q�� ��7� 2���/\N�
��Y��D��1��x���CT��OX2��~vxRo�:�Z�f�m�����
[b�I��:R�
|����=����7s[g�.����8EOi�.�II^fTI����`s#�r%%x����c<^��rs�����B�����[Dm��o���f�O��z��7�R����VS���4�hOTw��������t�M���-5q���3��[u�
CH��N4���U���i� X���u�i���R���U]�g����JPM��
��KA�w����������1Q�`��������ew�K�j;e�#]�����Q� ��\�{:[��v�dGW��B�4Q�[���`
]y�f��,k�3?�Le��y�8:%M(��B9�g�>��
v����`��,?F�'�QY�X���,��L~���
1S�z!f
�qR����h)�`#����ps����&M�b�%��89��B�1��M(������E/��K�I�/��#������7������A��N�7��$�p����V�P��Jw4*~ ��D�Z�����*c��s�?j���Xz����#8��89{�Y��3��vE=*�����5����Kd���r�0�L�z�=��!��T%�fx�I��*��<��y{N�p��w�){�?6��K�|���pp"$~:�������d_�(���o��^��o���n�o�"�����s��`��w�1��G�BE���;�_��2nt��8Z���g���ps�)���x����AZG���Y�����h,�\�p��3� ��1����N���
d��j����l�`�����"��H������[tN!�a��%�p���GA����k�'�)�-�<qO��l0�`��C�D��8%������U�l����L4�a
�eXe��Z�:��g�Y6(qof�.f�]�v����Z�*�����SR<��5���@�[��4w�}>�V���x��.�d�2���_q�~��[e]3�xb}���h�D��c�Ei�L��$31����KF�V����4b|��i�a��������4�S�2^���g������{
7��NC-3%3�6F !o�����wh9�"G!!�����,�e+W�f�����-�
���ap���a�.S�|j�|N"�'?6�����qs"����t`@#���,_�-
U���T)Rd�RSt��arR�295�4�lsJe�M��hR'����S�i'�j�X�Y��k%X4+�l�U��rRv��R�;�C��L1��d�9] E�2��g��4'd)��m�>G#�w4 b�A��5E~��">L�H���i�dgd�B�6�a�Ip�=En��"63�$53�"4�0��ZDF 6�q�A`��M!�Y.2�Kd���N�eZD) ���,�`*��}�ZA��8gD�AmI��q��9!bF��v��!6�Y�):(�D:S
����k�G ����������Kt N����M'���q�����F
���mp �8b��]��{����8�3�������
�q�������I]�~�]����n��:g�����������k���������0Y�������m�l����u�,����� [m�d
i����e�Q�hND��������O�I��(# �@����Z���1R>����C�Zb�I���9m�
�A�JUTD�����A/D(�����ju�"�����@�������8K;�A�3���o�p�N��b#���6��<g��k��\��7���$��"`k�nX�������J�ki�|�������_�+{��4h�
."t�o����&��c����K�Pc
[�� U� e,����5{ys����[�q��o���m�����juw�������-���8�������u�"|65&%njA���]�=4 �Pp m87�������H�j�C�r��{�s�_�.��s���\�ih����}7a��Q)y���u������ x��V�:�@�}�w�n�+a�3i�E�I������F���������r�p� !1������:�g�;�g���=(U�?�����95������p��_�v+��a*�U�(��}�wc+������-�����7_�
'������k�Yn�����2�1r�M��
$O�](;w�g�4O��
-I�~7���rE�\qJ�E�V�X��1��P���:����D�q�b��X�1�f��`\��`��b��ZX�D�f=~��s���$������bPN�!���5��v<&Q;9L�'X����%�T,8_�O!���&��E@��q ��%�+�^�������G�4^3���1�OrvU�����I4������6��������R���V���g2i�|,������Y�%T��vb��wP=��A�3{xI��q ������u���,v
��h� �o���dOA�1��>GR���\�{������]��B�C���^Ri�A������>�a�`���&i�'kF/X������/�����~r��zJ��t�%�2�W�jI}��F3�����<��LPs�h�����= 8��W��=CHRm�����������a/��/cn$QK}]t�-�F�R*�5@�c�m��&��]�L_2&n���P��;����.R���.K:���MF�4�1���H}x�(��D:P��~�2�e�$�rt[��&�`����0��sR�2���/�
Ny�O4XI��C��*�j8<R^�;�h�q��&3Ft�h�0.
$%,+$)c�H�B���w�K��j;U�b��e��,��_+��sN��9�en����L��dN��<���}��JG�#��z���{o�!N��-4:I�u���R���Es.�*��V�bx��I��I�*���[�����b]5�y�:��$^�a��s����.�f/0b�����������#u��J)FZ��$�V@��zL)h�"�+�{�4�6������\�~u��M�v|�}8�#��������v����\��
����������R]�&s��6�����8l�U��F����T��_o5������S�[�b�mE�o#�����W���i5����s�����v�^O�=\7<��Z*$�S����W?XT��}[?=��-�:�V���J��k���c;9!�-���<w$���"�tA�Q}�������(�YX�"�����M�R������`��M���o�KE^�n��G��N�V|\w�CC���_:�6]>��UE2%���nY3I��Ndv�~�y4X�g����a��1�����x�O��O`�DK�Jw�j%(�����������H@�m4]"���W������??�x�<*����n4 z�p�9=l�|�ny��i�A[��**�?-O�!1����'�����*��-w5��w���6Z�������]��G{��j�c7Q������H3�5N�5
����FUC�$g�,5p$��i��}�f��c��AYVg���DA�)�Z7�.v��N?^M�;���%Z����z�MKE����
cJ�q��O����V�|X]�/F��E@�%���%W�*�F�Ib���>�-�K�Q��F�QjU!�L(<>(s�I4&nr�H��p0t�G���r\��k��m��*)^G��(y$�$^�aF�����#5��E��q�����7���Zv� A�z:v53D�A�
$e}�������i+���zWI�2���R����)�/�)E���%�\���I�q<�d��EL������#q�gC��r�cr��1�q����\>�d\��%�V����^��.�������TS���lMrf�\�
sw�6{Wur�g�5.%B+������4'#�Q��f�� uT����(�t���m+�x�m��#P�r�������2�^�yv�s�z�����#����9|E�pV�KE��$�����9Y�J|��� �F}�7
������AO~�Kl���;�c�}�V����|���\�O;*T�e�����&���h6��/�~|H�Pe���c*�?������i��S*�����*�O^�CW����2n��I k�{�y���%�xBY
��Ct�]Z��#���� ���7��f!`�*�����I�zI�oR?���N���w���'��:�����_(y>�_6y+�D���o��*�D=<|�-b���C\f��2�#pl��fh���.�*�IF1P��b�W2���R�)EF�N�+KN D��O��lo�p�����}aW���J������Q������H�C������h���{^������j�J8p����r*��t:�({������;3#k�eR 4eN6���j��S@2�z;�7�h�"��<D�����3�!3c`Z�Ua�����_��������r��w>��I����h�j�������D�����6�L��v�^ ��e�r�WUBk �(Z�����~|�A����~�Q��g��'������927�������z#����6�������jG
���;���t6k��)�
t�s�����d:�;�^e[y�NM
�z|��}�'��h[jw�|{��Uv�i��,�h1��@MI�+�di�d 7��e��:'�H��9k��P<����B"�g6(���������������?��A��o:�Q?���o<Q���7����8��1�����c^�<K���>k���������@���BZ5�)���a���\�p��J�h{+��J�E�zW���P��n�
U�������*}����T9"�7���U<J|�<"����
��!�zg�P�*�;�>k��ec�*)�����b
W�V�+��� �x)2�fu0a����?��$���1��J��;��R ����r�\�P\z_���2�~Q|�27Y\��:z�ro+;;{�!���mWw�f-_h���[N<'�N.��*���F���z
4�>+Y�@SM�a�j�H�}4���q��1���<�dQ����}�t��f�O9���[^e�\��J�
+R1
�1�%I�~`�x��$�?���@� �d��& V{�+��SA��R� �k�2��)w��YBZ� ��}t��xB|�������9�h0�h���z)*6[���h��QQ2'�������o "LX7�y�����N{�����[>����J#C4�g�#/�h�#�W(0���W���9�kt�[�wGg�����:��Tg���k���ad��������KT�"������h��?x�8�q{X��S����2,Ub�L�e��\��O^I�0h�]��I{(�� xJ$������wo�p��E�81*�_4����s�d�� ��*���/�$���n������"c���a ���� �����O����t ;b�8���(I�� ��R���ac�&��)!��j�����A%��j���/�]�V�G��cl���}�[f�N�H�R���v�����]){������/��U�(�jy7{��c�f����Y=0��D~w�R��Nz�N�k���z���y+O<k�7J�
fn�=#�4���k��7�o�[*+��x�U����e�}�y��g���%�]�U��i��n.���L=q��7A'����7�~8��R�+�B0����~�L�]�����?K���r���}���*O�e�f��3���T��w����p�7(XY��7������C���zO�u^S?>5��6����������fC�xe��(?����0j�//�%�2���R�j�#��v�]����}�K�����g�`���~��).C�!n�;rQGa����xLr���7Sn J�����1n���~C+��m\�o�x�S�_O�-�W`A[�a�p<i$C�F��\ ���)����$xP������Wj�n4���1:ob<%
�� g�D����a{]���F60�N�R�L�J���h�y��{Ki�e_��RX� ��1F��5�����dpi-�b,|�w?'��o]������|�;�q��������_�����O�������}����(QtX�w!f���&�����jp��_ q=fd|%���(t����E���~��*�������2Lz�����#�iQB,��&F�*�wR�d��Bp�x�F���"���*{L�Rb[T2G��`�R�P�j?�Q0��wB6�%�I�.
�M�'�#�[.�:gt�)"I�0��X�><������D��wc�u�����6`��{�F�u�;Wlm�t���h�&����J�qpFQ������:p�O�.��2�}��|�%�p�d`�`5j����������=?i�m�~g_��9O�s�c�� ��g�����������h��3�Y�����=(��$�9�9v�3w�%���x}x��=�[���b@M�"� u�����&c���O^�Ar�4�G����f�'7�k�z�n�p6.��O���M7�"��Ry���j��E����~���U�[����%�p�r/�y��m�kp���R�����B{|��s������R+h+�v>w����/����pn�H�X (Z�xL���q��9,Mk������JJ�ny��IxRD���:B[;�L�q�����&��2�_;�"��S/<R��g��@q�pD�'�/V�e��K�y������!v�eM����La5qTi�aO�J��RB�=���Tv��N5 ����l���>O\���pR�vmW���"f�T�D�o�o���g�/AL����(���<����8�R���D(�[�u!mcC��vv��InG-D{�;�)�L)�C�S�4��[;c���J���rtZ{_4�w������[��No1g_���J�6�\� b�V�����g����������su����K(k_����h�8r�l;�8�`Yw$���:�T)��o�&�d�m��z��t���3k]�i�[����vI�Pl�����U�����h���)%+8h�i���G�q7��D�n����� �W��4+����h= ���V[�^m{g!�Q�DI���+-:���8����N�EL�#�'�P�}����m��R�3)K��>Ul���.�'F`��]�H�:5[�������'�|�\�c����C >�������P���wmA���(�0���Qe��6�Q>M(�a?�N�N��90_��@���8Q�{7�i���+_��-���K(0�`<��1)%�i����GVLf�9��GA:�Vz[;^m�fk =*��K�������W�������N:GoO��������Y����*��+�dV}����Wwv����b��R7���'�+Dm��8�L���C�L�2���b���k|KX)u�b���sJ���r�X�h�{�kR&������[���������;Y��� �l�����j����[������2l�i������� 4���+qBIJ�Z����2x����R�HG0;/e'����T����P��&i/G��h`��]����Th&[w��o��%�������v����Bxz��U�-�j��C�t���j+���P#���-NJp%ZO�����2 �Z���v�L������DX;�KV3�A�T.=pJa�Y&��,D��:�j��Bx�^+�8tN>�=�?��ZYX�Ni����������a�:V�{L?<;����'�G���Q����zRM(������ �w��y������
��c������C~��(!����P.����t�r�a�@��4��v����"�<��R�2��ZO��<%��-{�Y(�����0������\ l���q�!pF���+���� 2x���V}E��:�8���(�l�
"��|n,$�Z���e������k�a�� G�4��'l��>n]�^����7f�I��B����!�!�V�F������.V���vTRR�ju5�}�aX�R
����,�Jb�6���b�>������moo7�=�������Y�i���M�".J���y��{6����z�Q��D/
?�[2��/'�%���&+w|bnU�iH�[���6���a�_H��T$�O>E@f)D���kO�y�
�
6����8�:����dT|H�AP����F e�i�S���xd�����&c�P��h��^��K����3&���l�<���=5Pie�������o
�-e{���h��Z��q�Rc���M�L� �|��F���
�Q�[�|J�
K���)I��J�CnW����gp�/�o��9��(T��
�b8U��<�������y�(�#e��d��(��-�[]���5�}��/� &mR�^����#�J��a])�r|wT�����y��{��*�L'mf���,��'l�dwR_�?�2X��BA��E�I���a���%}1\�Y^'6�~+�� ��lA��N���j�K{�Xi���98��y8�81Ygz�;�J@�����7�Qz�:�A��[O��D�o����g\���>���C���]W�S��r�}g7����c`oU�(q��<A��5����� ���xL��HAL3>���=F]}:��P�Z�;n�G�@�' ��<����AP���n�����?��x���@���:��.�}1�U@0���;>}G�����V�>�E)�Lr���
'��6 Z��E��,G���jK
�r������i��S�J%7^-��R��B���%)!�J���l_��@��i$R��O��w��\P��uZ�z���q���K����8���|�e����y!��K=�����z(�aiN>�NV�������X������a? z�����r"��`)�=[4��}����N �V�������;]���Y�G�:��r�l�/9f�exa���[���I���iL����� z������;�4o�sMMJ�n��o7%���;J��/�fVre|F������n<O�n�]n����!���(o���q����`�7$��NQR���l���
��J��F9���I����++r������4�/��.���iu�����.���,�!��k���ju��������=}U��hl���|����\��[U4JYh��v��/VF�'��y����vtv�N�X�{�7���o�T�f~�d���Z ll�V����N��2��R��1���#��������r����v��W��d���ru��n�v����Q�S*��L�������~r8do�
�@[�$��L�(�
�MUx��D� �E���L%lJ]+b���1��I��r��u��F����
\��<Sx,�z�+=5����4]�;^���������;��I����� �}r���Qd����"C�p�Oz��l|��q���V���"N^v�-#v�>z����qd����[Y~�����~w�����h�m{;{eg�U�*D6��;����vmFpEq�����y[��6���U� Kj�Y!mU9f$o�F�LO��5r�wb
��nA�3�>a;�����c���}�/t�����c-w���������I�J��1�<,+<&sVT����I0�5>UII����G�z��� } V����9�
��>K�`.*J���l���v�������<�{����h����J���$z�����Zb������C*?NX�~��'�������l?��y��#WM;�Dk������7���k�r%��~���y]���_�z��}K�>������$���j��an�����~a��j��N����5��q)���V���vkz� i���A�����-��:�.f� }��M����Rf�R+R���W��NE^�A�3��y���('�v40�M��������(�������Z�x����RM��[�^k[US���[��������9�&a7(�
���6���������s�}�����5��GCT�:����
[v��9��� Yf�fN��hl��L�*�]#l��r!����Y�TW
�,��A*d�R��l�T��a���l����j�*���C��a��S������
|�2,[ro1��d�Z��^rL��;���34���z�!ebc����2\�(��C�2����R����������}�/�+uq�c$���D����"�g���0���x2������� ��eP���gR�����R��!O��W8|�o��c$&G"�[�s)JO�v~w�s�����Y@�T�$�x��m(z!M)�O�^�����E�1fe�dv�L��K��y=�26�$��";�
&x���d��pT���9j����d����&hf�Z�o^�l0� ]M�d�������B��n�i5��Id �������A�c��H\.�s?T���7 ��P
,���T�O�S�Q�+�3i(3{b��;�
]����a54!F�%9��s�b�P�E���,�*��w�j���L��D
vBC?/������N$=z��>PD��:����X�����3��Z�yZ3�Yq�?%c��cI=o������WK7����K6���QrN:# jQ����E��su���F��p�F7�����\����s�o3��@G�AZ�������Kh�O�����#M������F���eKd C����e�{���=�� V���0s8����#�����������JI��*��6�|����'X�J!gX�.�H�K9��X�n )R�#�!uY�e��l����v���c�����9DKn`Z���<�s�ET�V�s._o:om��������x&&���;�iy3���Hqd\����~��L=���p��]F�/6)o����*��������O};]��[��v+<�!I�9IY|����z��2�Q,�6��`b�J�x�3��Q��= �N1`5/2�����TO\3�q4gz`L������e���j)���m�uj������e��
�enX�V�N>>��8t$,��5Bn$p[�V�
�;���0��C���%G`Dq)0��_F�1�OR���k�3����x��X\�Y]���/�r�0k��`��>m�v����s��[�����j��w!VM�]��j�F���h�jH{��S2\)LN�V��+�F�#��(�%��y7=������m�Wc��F0�v�{�,}���T��E�ck� P���Zw�x���2aA<1�a���;��2_U�LT��k`��v���R�DW��6�A
���@�:)u�a<��H�-��C�� ���G>1�����X ��_u�ZF5O��6tRr��q�fVq��7)%Ev���1�|Xx��4���m0��ny{�N��}�w���p
d����k�
u�+�3��@
*h����p�h<�Q4��)j��/H�c�?4p;HLfc0������Po���(��xzM���Dl��������^<p����r�� �)`��*���)�d�����~�o
���"�x�%���L����b�`����,�(wy���^e\�}�ma��v\�!Oo���������*�P}h}��x`f�h
H(�9i�����bp�=��y�cP�y�}������J��{�'y��/�J�Q�#5\�o!b:T�wb2QGl��a��.�R��mbo6���oh�J�B��@���5���V�A��ba��������Q~�c�aW�S�[���%��$�,/��b�t��y,'3�a���h).�r��:��t�yTo�O�bn,;����X�U'�����h~n��xp�
\*����sF��@iVt02�&L����y
nk����$� c�����
_7?TZ���o��4]�l
i$P�L�|v����#�}�.�l��]�u�*��n�L������p���T����*�]��+/a��,����3m�ZO�T�%�^�gA�t�6N�u�x�~Dh3O}�������*d�>����oNq�{������XR��5<�7^�5����h�����6�J`(���i�,��x_?y�h�2�1����^�����EZ��/]�#Lt��t0�E�bz%�����/��Zj��(q7K��� sY�OU)�d���{���y5%x�����B�jjo��*kO;��Bm/�F�v�����D����`�&2���Z�L����>�}�_0Xc�����7M��+{V�Z��,�T��0n:-e��Eq�o��C�Z.W�
��k���������^��2Fx jA"�����qM���XM ����p<������P�������#�&Yc8�����l�:���\.����(h����w���
��A1�mv�R0����f�@���f�����Uw-S���u�Q���Q��^��y����-�Z���AY�#��t�$���S<u�wB��tT� ���0`GX��_q��k�5;��Jm�tLJ��+���8)O�(�������Y�]
:�?����%b���[��'��~4BFj]�vp�_E�����
��g�����:\��7y�T��a�wS���h7R��s����!s��F�S����[�}������mR����l��WJ�=cI�I���l/�.��zY������I���l�=�Ql������?i����2�e�5�<A����X�p{����+���4��H[��!���'-`����v��f���4�]��P�z�������v��
[���v���yd��m,����x��3\��&�4��R��z-\�v�n��5~���z��V*s��x� ?d�?��\_y.~�����(�+��ya���^G�R?���z>!�<���x��
,��e�O��u*�Z�����
R8�r
/�A'�i
j��G�Ni4j�� }�8l��A9��N�����~���\��)���/���tC9
�I��������%��V�J� K�:LAK� S�C�/k��|����1�4��s����KO%�����>�^�O�Dt1/�7�u'ap��.
��t(^i)
���
�����j&Z���P`������1�1_/^5� �����k�"���
�Z�n�y�?j���H��Qa(�]���7=������6.���Y�����Y���]���mI]*
���w��������!�3e�?@�T�p2AO\�a��pj�O������~+��}ickO2�9Nr|"@��W��{z�~��yR��KF�!������L�L�����Lw*�H���gp/K��*H|��<20>��~�x c
�b �)p�ZT����������U��f=��e���?fDM�E��E{���b~��GIcu�g*�7�w��J��c���w�'X�5)Ib�4G�\Y��4#5��,%�IZ��VF���V��rc�-������SC�Q
YA<x������G���8$n?�0��E����t}a��*]?b�����h"� C-O�w����\��������F�v���������I[�&���+���6��m��H%X�s#���mz��<~�y����d����������dX|�ro#�����m?�6��:i=[S��l���U�U�M����o����W�-��W3*����j��ksg��n�Z��z��F���j[e@��������)C�'�1�9�� ,}���� t�nG�s�������~-�I�g���U�j��(��f!|8��)t(t�\y@
]����E���>���'����Qd1v�e�fS9�Rv�����6��������c|Q�}v�t2c��,�O&�P�j��C0���H��9m�h}?;9�.��g�N�O^�2[���h��5�A���-3R���#�#M�%g�xt*yJut��7�����Ij�H���1i+L�g�Q}q�s�0[�m���f�g���(��zw�X��n���GY ���"g�`�,��(�U�����j�y8>I���T
)���{����
���
�U��wP���[\�������t�V_L/5'
��
������\�&�����\)��p0F�Pl����5ma�P'�'�x���GC1��[�0G�����F�L{K,�y����W���l�����Pb����@; OV`no�X!�����7V�t��������K��F W
Q����n���?9}\Z���7
��
��7����V�F�*�Xr�o�U�!���=���Z����^i(��EC�������8[�%����BNk��}o{Gr��|�6��)10��K��
i9 `���C���+��|+����R��E/�y1��MZFzx10�g�)Y��)��������y#�1��Z���l��XV��d�$2�0
�0l�Y��Ep���������~o�� ����%������.�@ �~��M���6���6�]� �h���aY�I��{����P�����h6�����Sq��i���d�B_Sh����u&bgs�,�;��lp@N���Z��b��(�1��(�k�����k(�K�*�]�.�(�p�h���t�
<������nP�����y"����p��0�-PXy'���t��W���d�����R�zd��G�j�~D��������(Lx�|q���?^���1�,����2����QD�j��#���N��e�{���*[��t��Jl�"K�*^GT�0�&�M��������'S�0�dj.@;\�0���p�>()W�j��o��t:X[/�h����3za���Y����2���p�S���o�PX��k�������o���Q���M�s����TE�Q���.����}:��w��/�+������p�A�>w��G����
`N]��m���A����n8��XEj |��U���1����{��Q���������g�����O��B�7����?�6~vc�6
F�&��������������F�y�w>�i=c}XI�����q��%���j0��3���}M�2*���������}��
,V]����V���!���+�'�����QS���:#��o�{3���A�ED(<�x@ ��fezyID�U�sr�@�}�:+0�3��/��|��!�Q�$���%'7B+�!j�<�����]�_y�u�QI��^���)�5��m��j��������?�O�Z{�}��+w���a���'a7�{�.Gc������e4���? �y���-
;=$�!E|^����lo� ����g�����H&�Qd��u�[2�{�]�o�W���6�H��u6Bu�>*��iE��G������{�P&�9���'��2���n�����~MiPn?���=��.{��2�T��{J h]�s?��Nz���{:��6��q�s��>jd���tj?v���>�nrL����G���n?x���(�h���/v���������g��b���������2��Q������i������������%��.�p3������K����J�_�1��Q��EV���BOfe��W����{���rjq�_�U�8c?��D�1�Y~�j�l�5d;�,vI��Cj�R�A��n�����-��F��a�c������"��n�y��W6������e�A��n�)5U���0+R33����6�t'P�{r��M�@��Ye�
�VO�P�!��q�-��.��9��z���h�����)���x�/�Q$L�����_����-_����Q��Ew�\��M�����[�Ex���JoF�T^���6�#�~2z�0��'�D��U2`O��K�z�Q+�?;o4��������n��������i�Z������iu�.��M��;�}��\�`s�H�Z��+Q��G�d��KR:F��� ������@�G�����~�M�_�C���ws�r��
��]������ kb��y�l��O�QK~}&�b^�A-�m�j��V����h�S;mn����kQ:9;��sy9��n�-0��g''�{lY��S�xf���r��~X�!l����n��PW�# 2������Y]X������3������I|;�'��^� �emA��%:9�f���ET�K��K����c��!6�����#5��c�J�>�NV��g�.�M����e��|��e_�%������,�������u����� �v��VGY?uV��
13�{�M��+�3��x�
���(�
�DT��=���~���s���5���k��pr~\���,��R?�qg7}���F=��f��������X�������5�|�J|�� k����/�:�T6],�����r�Y�H?��Q�Z)?��.�v����g�}��������t0�� R��Z�{�����Q=��/����c1���U���%�����r=�U�%�E{|L0d9����&8�
;kJ�YRp�9LzD`����
:�1�H:��,���������F��;�Z����m9
�#3�b���+�v-����Mf��n�������ZV���� �bm:��[Cd����k�����I���ED�����jU�O�'�/����/a]���vm�cnK���NW����t� � ��5�����C����|8�m������������a��P*B���zo�4,�uK�2�Z�'�V��o[n �\I�,�w�J����������-Z�z������o�5�#�/o��+iL0 ��Jy�q����������S��X�W�0�^C��ht�{����c�9��c:A����3�<��"���Q�7E�����u%���C~��a��!�m��\�I�!��! �����r�#�����Xe���y��>:c�����{^o��g>���P����1&�
�4F�5A�j��� �,NX~��������Z����l�C��v������,�e�����gn�^���-��`r���Kf��/K��9/��mtZ�I����p]#�)��j��K,{��.�8$Ac�>c��Fm��}�������/���[�e� \UM��M�y�o���-K���Y�p-I}A<w������T�*����qU��hr��Tr ���Q�]?>q�*����[�QB�$T6o\� �*W�d��m������4z��]%Lr�$����)���(Ug����b��o=�(k�LgOq�sLf\��"��%���'S�Q����{<�����������8����h^f�{�p���u`������mw���bQ��i��v�2��g�����^ 7�1����w`
�p�
E��t8)=[g��A�Ffu��@��������cce�Gx�'xv���6=�����Xc���~�~��� �:B�O���6�R���=*3'5��J��'\�3=E����'���+��{@��E��TpF��x�e�Y�7}� ���b8����1C/�P2)��������pDk��M/�����V��l���`[��l��w��V����� H6��FC�l'�v���ny>�P���:��~z\���w`�K�����\�:f?�r����:��y��������39�Dz��]|G��.���PT
vwv|����k\�7O��6����3f��
f�r"%n��jJ�� �q��J~���l�Q��8v�+1�nF�?�O�d�T�^�z�hm�2a_�/�T����2O^��Q����r�Fu6P8���;�V�B)���`��?k����]��@�;8?���5>c�B;P�9�l<O~6R)���_�z���;��=���K.�H 8�z��{����b�yBE��>�8�U.���
xo
�.��f�%����hLQZ����?���-�_�A�S�I����L������5���@�g��n%;g�!��D��j{�`o��j���eB���j���8����T�v��Q�Zf9�Gd}���hk/_�~A����.*fS�+��������o��RkP�lL���+r<lO0�A�����x���-���W�|��������3��c�bZv5�YN6zg������\X��'#�L�c����7�y�a��k���y����/1 �M��;:4t!W��/���p{Xm[�W�q��C������r�x�w�! N�w,���0�#K�w�X�$� 0��HG�\ 4����� �a�v$6���\�k]�4W�e�p�S�$ q��p����.�������kN�����8���gA�C J����b�f)�A'�C@D8I�����C�@J�qF��x�1//����v������\l����w���#��!��yG��;B�������9#�}� yyq�����1��
�X8����
F�r�S����.�X0�"A�
�Z.P�b!s&x����,��^�U��d�p#K�x���Q<,�R"
��H��b�
�������/�_~���Y/��|���^��va����O�}OW�E�"/������{8�]��n!��Y�����\'��Yu����NNg:*�w��|�r���3��R.���c��������3����b������|�r:�����}�-� mQWd��������e-��jY?RK�tzP�JK�:��zh�����W�/�$gQW5K��Y�u��.T>�+��p)��k�%\lpu�U�N|~��:`(��!<<�C��� r�����Q��������=������_�����oa{������~1��5�,f�8�lp����j�>�S�l����S}�GdD�x���2��
vQ�?=s�`�� On 2 July 2012 15:11, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:
https://github.com/dimitri/postgres/compare/f99e8d93b7...8da156dc70
The revised incremental diff is here:
https://github.com/dimitri/postgres/compare/f99e8d93b7...74bbeda8
And a new revision of the patch is attached to this email. We have some
pending questions, depending on the answers it could be ready for
commit.Robert Haas <robertmhaas@gmail.com> writes:
There are a few remaining references to EVTG_FIRED_BEFORE /
EVTG_FIRED_INSTEAD_OF which should be cleaned up. I suggest writing
the grammar as CREATE EVENT TRIGGER name ON event_name EXECUTE... On
a related note, evttype is still mentioned in the documentation, also.
And CreateEventTrigStmt has a char timing field that can go.I didn't get the memo that we had made a decision here :) That said it
will be possible to change our mind and introduce that instead of syntax
if that's necessary later in the cycle, so I'll go clean up for the
first commit.Done now.
Incidentally, why do we not support an argument list here, as with
ordinary triggers?Left for a follow-up patch. Do you want it already in this one?
Didn't do that, I though cleaning up all the points here would go first,
please tell me if you want that in the first commit.I think we should similarly rip out the vestigial support for
supplying schema name, object name, and object ID. That's not going
to be possible at command_start anyway; we can include that stuff in
the patch that adds a later firing point (command end, or consistency
check, perhaps).We got this part of the API fixed last round, so I would prefer not to
dumb it down in this first patch. We know that we want to add exactly
that specification later, don't we?Didn't change anything here.
I think standard_ProcessUtility should have a shortcut that bypasses
setting up the event context if there are no event triggers at all in
the entire system, so that we do no extra work unless there's some
potential benefit.Setting up the event context is a very lightweight operation, and
there's no way to know if the command is going to fire an event trigger
without having done exactly what the InitEventContext() is doing. Maybe
what we need to do here is rename it?Another problem with short cutting it aggressively is what happens if a
new event trigger is created while the command is in flight. We have yet
to discuss about that (as we only support a single timing point), but
doing it the way you propose forecloses any other choice than
"repeatable read" equivalent where we might want to have some "read
commited" behaviour, that is fire the new triggers if they appear while
the command is being run.Same, don't see a way to shortcut.
Added CommandCounterIncrement() and a new regression test. That fails
for now, I'll have to get back to that later.Of course I just needed to pay attention to the new ordering rules :)
Instead of having giant switch statements match E_WhatEver tags to
strings and visca versa, I think it would be much better to create an
array someplace that contains all the mappings. Then you can write a
convenience function that scans the array for a string and returns the
E_WhatEver tag, and another convenience function that scans the array
for an E_WhatEver tag and returns the corresponding string. Then all
the knowledge of how those things map onto each other is in ONE place,
which should make things a whole lot easier in terms of future code
maintenance, not to mention minimizing the chances of bugs of
oversight in the patch as it stands. :-)That means that the Enum definition can not jump from a number to
another non consecutive one, or that we have a very sparse array and
some way to fill it unknown to me. As those numbers are going to end up
on disk, we can not ever change them. I though it would be better to
mimic what we do with the NodeTag definition here.I'd like some more input here.
+ Forbids the execution of any DDL command:
Worked out something. Might need some more input.
I'm not clear on this paragraph:
Triggers on <literal>ANY</literal> command support more commands than
just this list, and will only provide the <literal>command
tag</literal> argument as <literal>NOT NULL</literal>.
Do you actually mean it will return NULL?
I also attach various typo/grammar fixes.
--
Thom
Attachments:
evt_trig_v1_changes.patchapplication/octet-stream; name=evt_trig_v1_changes.patchDownload
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index 920981e..d868889 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -1229,7 +1229,7 @@ CREATE TRIGGER test_valid_id_trig
<para>
Event trigger procedures can be written in PL/Perl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
+ <productname>PostgreSQL</productname> requires that a procedure to be called
as a trigger must be declared as a function with no arguments
and a return type of <literal>command_trigger</>.
</para>
@@ -1272,7 +1272,7 @@ CREATE TRIGGER test_valid_id_trig
<term><varname>$_TD->{objectname}</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index b71786e..102dfff 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -3940,7 +3940,7 @@ SELECT * FROM sales_summary_bytime;
</para>
<para>
- When a <application>PL/pgSQL</application> function is called as a
+ When a <application>PL/pgSQL</application> function is called as an
event trigger, several special variables are created automatically
in the top-level block. They are:
@@ -3981,7 +3981,7 @@ SELECT * FROM sales_summary_bytime;
<listitem>
<para>
Data type <type>name</type>; the name of the schema of the object
- that caused the trigger invocation. Can be <literal>NULL</literal>
+ that caused the trigger invocation. This is <literal>NULL</literal>
for objects not located in a schema.
</para>
</listitem>
@@ -4004,7 +4004,7 @@ SELECT * FROM sales_summary_bytime;
</para>
<para>
- <xref linkend="plpgsql-event-trigger-example"> shows an example of a
+ <xref linkend="plpgsql-event-trigger-example"> shows an example of an
event trigger procedure in <application>PL/pgSQL</application>.
</para>
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index 07f017a..d62da16 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -881,7 +881,7 @@ $$ LANGUAGE plpythonu;
<para>
Event trigger procedures can be written in PL/Python.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
+ <productname>PostgreSQL</productname> requires that a procedure to be called
as a trigger must be declared as a function with no arguments
and a return type of <literal>event_trigger</>.
</para>
@@ -924,7 +924,7 @@ $$ LANGUAGE plpythonu;
<term><varname>TD["objectname"]</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index c9a4e4c..1975976 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -527,7 +527,7 @@ SELECT 'doesn''t' AS ret
<para>
Trigger procedures can be written in PL/Tcl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
+ <productname>PostgreSQL</productname> requires that a procedure to be called
as a trigger must be declared as a function with no arguments
and a return type of <literal>trigger</>.
</para>
@@ -725,8 +725,8 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<para>
Event trigger procedures can be written in PL/Tcl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
+ <productname>PostgreSQL</productname> requires that a procedure to be called
+ as an event trigger must be declared as a function with no arguments
and a return type of <literal>event_trigger</>.
</para>
@@ -768,7 +768,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<term><varname>$TG_objectname</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index b21ce75..74882e4 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>ALTER EVENT TRIGGER</refname>
- <refpurpose>change the definition of a trigger</refpurpose>
+ <refpurpose>change the definition of an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-altereventtrigger">
@@ -31,8 +31,6 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
ENABLE REPLICA
DISABLE
-<phrase>and where <replaceable class="parameter">command</replaceable> can be one of the same list as in <xref linkend="sql-createeventtrigger">.</phrase>
-
</synopsis>
</refsynopsisdiv>
@@ -45,7 +43,7 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
</para>
<para>
- You must be superuser to alter a event trigger.
+ You must be superuser to alter an event trigger.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index af06c88..bd323b7 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>CREATE EVENT TRIGGER</refname>
- <refpurpose>define a new trigger</refpurpose>
+ <refpurpose>define a new event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-createeventtrigger">
@@ -135,7 +135,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
The command trigger gives a procedure to fire before the event is
executed. In some cases the procedure can be fired instead of the event
code PostgreSQL would run itself. A command trigger's function must
- return <literal>event_trigger</literal> data type. It can then only
+ return a data type of <literal>event_trigger</literal>. It can then only
abort the execution of the command by raising an exception.
</para>
@@ -146,8 +146,8 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
Note that objects dropped by the effect of <literal>DROP
- CASCADE</literal> will not result in a event trigger firing, only the
- top-level event for the main object will fire a event trigger. That
+ CASCADE</literal> will not result in an event trigger firing, only the
+ top-level event for the main object will fire an event trigger. That
also applies to other dependencies following, as in <literal>DROP OWNED
BY</literal>.
</para>
@@ -178,7 +178,8 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
The tag of the command the trigger is for. Supported commands are
mainly those acting on database objects, plus some more facilities.
- That leaves out the following list of non supported commands.
+ That leaves the following cases where commands are not supported by
+ event trigger functionality.
</para>
<para>
Commands that refer to global objects, such as databases, tablespaces
@@ -209,7 +210,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
Triggers on <literal>ANY</literal> command support more commands than
just this list, and will only provide the <literal>command
tag</literal> argument as <literal>NOT NULL</literal>. Supporting more
- commands is made so that you can actually block <xref linkend="ddl">
+ commands is made so that you can block all <xref linkend="ddl">
commands in one go.
</para>
</listitem>
@@ -220,7 +221,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<listitem>
<para>
A user-supplied function that is declared as taking no argument and
- returning type <literal>event trigger</literal>.
+ returning type <literal>event_trigger</literal>.
</para>
<para>
If your event trigger is implemented in <literal>C</literal> then it
@@ -238,11 +239,11 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<title>Notes</title>
<para>
- To create a trigger on a event, the user must be superuser.
+ To create a trigger on an event, the user must be a superuser.
</para>
<para>
- Use <xref linkend="sql-dropeventtrigger"> to remove a event trigger.
+ Use <xref linkend="sql-dropeventtrigger"> to remove an event trigger.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/drop_event_trigger.sgml b/doc/src/sgml/ref/drop_event_trigger.sgml
index fc45dff..006f136 100644
--- a/doc/src/sgml/ref/drop_event_trigger.sgml
+++ b/doc/src/sgml/ref/drop_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>DROP EVENT TRIGGER</refname>
- <refpurpose>remove a event trigger</refpurpose>
+ <refpurpose>remove an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-dropeventtrigger">
@@ -30,7 +30,7 @@ DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceabl
<para>
<command>DROP EVENT TRIGGER</command> removes an existing trigger definition.
- To execute this command, the current user must be superuser.
+ To execute this command, the current user must be a superuser.
</para>
</refsect1>
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 32994b9..dd18e7b 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -39,8 +39,8 @@
<para>
A trigger is a specification that the database should automatically
execute a particular function whenever a certain command is performed.
- The whole set of <productname>PostgreSQL</productname> commands is not
- supported for triggers, see <xref linkend="sql-createeventtrigger">
+ Not all <productname>PostgreSQL</productname> commands are supported
+ for triggers. See <xref linkend="sql-createeventtrigger">
for details.
</para>
</sect1>
On Fri, Jun 29, 2012 at 5:38 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:
Will do a whole warning check pass later. Can you give me your local
Makefile trick to turn them into hard errors again please? :)
echo COPT=-Werror > src/Makefile.custom
Your latest patch contains a warning about using a variable
uninitialized that seems to indicate that you didn't test this very
carefully: in get_event_triggers, current_any_name is set but not
used.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mon, Jul 2, 2012 at 1:56 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, Jun 29, 2012 at 5:38 PM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:Will do a whole warning check pass later. Can you give me your local
Makefile trick to turn them into hard errors again please? :)echo COPT=-Werror > src/Makefile.custom
Your latest patch contains a warning about using a variable
uninitialized that seems to indicate that you didn't test this very
carefully: in get_event_triggers, current_any_name is set but not
used.
Make that used but not set. Anyhow, it's broken. :-(
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mon, Jul 2, 2012 at 10:11 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:
[ new patch ]
I would really like to start committing parts of this, but there are
still a lot of unfinished loose ends in this code. The one that is
most immediately bothering me is related to the syntax you've
implemented, which is:
CREATE EVENT TRIGGER name ON event_name WHEN event_trigger_variable IN
(trigger_command_list) EXECUTE PROCEDURE func_name ()
I've been beating on the issue of generic syntax for a while now, and
that production looks sort of generic, but there are problems when you
dig into it. trigger_command_list is defined in a way that means that
it can never be anything other than a list of tags, which means that
event_trigger_variable can never really be anything other than TAG.
Oops. I think you need to remove the validation of
trigger_command_list from the parser and do that afterwards. In other
words, the parser should be happy if event_trigger_variable is an
ColId and trigger_command_list is a bunch of comma-separated SConst
items. Then, after parsing is complete, the code that actually
implements CREATE EVENT TRIGGER should look at event_trigger_variable
and decide whether it has a legal value and, if so, whether the
associated trigger_command_list is compatible with it. IOW, the
validation should be happening in CreateEventTrigger rather than
gram.y.
There is a related problem in terms of the system catalog
representation which I should have noted previously, but did not. The
system catalog representation has no place to store
event_trigger_variable, and the column that will store
trigger_command_list is called evttags, again implying rather strongly
that these are command tags we're dealing with rather than anything
else. Obviously this could be revised later, but it will be much
easier to add new functionality in subsequent patches if we get the
catalog infrastructure right - or close to right - on the first try,
so it seems worth spending a little bit of time on it. The two
questions that occur to me are:
1. Do we imagine a situation where a given event_name would allow more
than one choice of event_trigger_variable? If so, then
pg_event_trigger needs a place to store the event_trigger_variable.
If not, then it's a noise word, and we should consider simplifying the
syntax to something like:
CREATE EVENT TRIGGER name ON event_name (trigger_command_list) EXECUTE
PROCEDURE func_name ()
or maybe
CREATE EVENT TRIGGER name ON event_name FOR (trigger_command_list)
EXECUTE PROCEDURE func_name ()
or perhaps
CREATE EVENT TRIGGER name ON event_name USING (trigger_command_list)
EXECUTE PROCEDURE func_name ()
2. On a related point, do we anticipate that we might eventually want
to allow filtering by more than one event_trigger_variable in the same
trigger? That is, might we want to do something like this:
CREATE EVENT TRIGGER something ON somevent WHEN thingy IN ('item1',
'item2') AND otherthingy IN ('foo', 'bar') EXECUTE PROCEDURE func_name
()
If so, then how do we imagine that getting stored in the catalog?
Please let me know your thoughts.
Thanks,
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
CREATE EVENT TRIGGER name ON event_name WHEN event_trigger_variable IN
(trigger_command_list) EXECUTE PROCEDURE func_name ()
[...]
1. Do we imagine a situation where a given event_name would allow more
than one choice of event_trigger_variable? If so, then
The only use I think is really interesting here is matching command tags
for either the main event or a sub-event of some sort. We decided not to
include any sub-commands reasoning in that first patch, that's why the
syntax is way more generic than the implementation.
You could then install an event trigger for a CREATE SEQUENCE that
happens as part of a CREATE TABLE, or a DROP COLUMN that happens as part
of a DROP TYPE, for examples.
So yes the event_trigger_variable is made to only host a tag and the
associate catalog entry make that very clear.
pg_event_trigger needs a place to store the event_trigger_variable.
My thinking was to add another hard-coded list of tags for the other
variable, that is have a flexible syntax (no WHEN clause, a WHEN clause
with only main tag matching, a WHEN clause with both parent/main tag
matching, or only parent) that only uses two hard coded variables both
being tags matched against a static list.
The reason why it's not all in the current patch is that we decided
together that we want to revisit the sub-command semantics once we have
a basic framework in place. It's already leaky though.
If not, then it's a noise word, and we should consider simplifying the
syntax to something like:
CREATE EVENT TRIGGER name ON event_name FOR (trigger_command_list)
EXECUTE PROCEDURE func_name ()
That would have my preference, in case you would rather have a
simplified first patch, that is without any provision to expand on the
tag matching facility. Of course we won't be able to keep that syntax as
soon as we decide how to handle sub commands, which makes that decision
a lot less useful that it seems to be, in my mind.
2. On a related point, do we anticipate that we might eventually want
to allow filtering by more than one event_trigger_variable in the same
trigger? That is, might we want to do something like this:CREATE EVENT TRIGGER something ON somevent WHEN thingy IN ('item1',
'item2') AND otherthingy IN ('foo', 'bar') EXECUTE PROCEDURE func_name
()
That's what I want to be able to do yes, in another step. The only two
variables I think about would be named "tag" and "toplevel", or
something equivalent ("main", "host", "user", etc).
If so, then how do we imagine that getting stored in the catalog?
We will have to extend the catalog when we attack sub command handling,
at least I believe we will. Hence the current proposed catalog is not
yet finished. We could also already decide about sub command handling if
you think that's the only remaining thing to do in this patch; so that
it's easier down the road. At the expense of taking some more time right
now. Your call, I have the round tuits :)
Regards,
--
Dimitri Fontaine 06 63 07 10 78
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Mon, Jul 2, 2012 at 4:10 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
2. On a related point, do we anticipate that we might eventually want
to allow filtering by more than one event_trigger_variable in the same
trigger? That is, might we want to do something like this:CREATE EVENT TRIGGER something ON somevent WHEN thingy IN ('item1',
'item2') AND otherthingy IN ('foo', 'bar') EXECUTE PROCEDURE func_name
()That's what I want to be able to do yes, in another step. The only two
variables I think about would be named "tag" and "toplevel", or
something equivalent ("main", "host", "user", etc).If so, then how do we imagine that getting stored in the catalog?
We will have to extend the catalog when we attack sub command handling,
at least I believe we will. Hence the current proposed catalog is not
yet finished. We could also already decide about sub command handling if
you think that's the only remaining thing to do in this patch; so that
it's easier down the road. At the expense of taking some more time right
now. Your call, I have the round tuits :)
I'd really like to not have to change the catalog again in every
patch, because if we do that then we are just saying we're going to
rewrite this patch completely every time we want to add a new feature,
which kind of defeats the purpose IMHO.
So let's try to hammer something out now. The obvious thing that
occurs to me is to have a column in the catalog that is a 2-D array of
text, with the first element of each array being something like "tag"
or "subtag" (i.e. event_trigger_variable) and the remaining array
elements being a list of legal values. That is:
WHEN thingy IN thingy IN ('item1', 'item2') AND otherthingy IN ('foo', 'bar')
would be represented as this array:
{{thingy,item1,item2},{otherthingy,foo,bar}}
This would allow us to support 0 or more WHERE clauses, each of the
form "thing IN (value1, value2, ...)". Is that general enough for
every use case that you can foresee, or do we need more?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
I'd really like to not have to change the catalog again in every
patch, because if we do that then we are just saying we're going to
rewrite this patch completely every time we want to add a new feature,
which kind of defeats the purpose IMHO.
Fair enough.
So let's try to hammer something out now. The obvious thing that
occurs to me is to have a column in the catalog that is a 2-D array of
text, with the first element of each array being something like "tag"
or "subtag" (i.e. event_trigger_variable) and the remaining array
elements being a list of legal values. That is:WHEN thingy IN thingy IN ('item1', 'item2') AND otherthingy IN ('foo', 'bar')
would be represented as this array:
{{thingy,item1,item2},{otherthingy,foo,bar}}
This would allow us to support 0 or more WHERE clauses, each of the
form "thing IN (value1, value2, ...)". Is that general enough for
every use case that you can foresee, or do we need more?
Given what I foresee, simply having another columns in there named
evtstags with the exact same content as evttags would be the simplest
and most natural implementation, really.
I don't foresee more generic needs here, unless you can convince me that
we need both a. a full stack of arbitrarily nested commands and b. a way
to match and target any level of that stack.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Mon, Jul 2, 2012 at 4:53 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
So let's try to hammer something out now. The obvious thing that
occurs to me is to have a column in the catalog that is a 2-D array of
text, with the first element of each array being something like "tag"
or "subtag" (i.e. event_trigger_variable) and the remaining array
elements being a list of legal values. That is:WHEN thingy IN thingy IN ('item1', 'item2') AND otherthingy IN ('foo', 'bar')
would be represented as this array:
{{thingy,item1,item2},{otherthingy,foo,bar}}
This would allow us to support 0 or more WHERE clauses, each of the
form "thing IN (value1, value2, ...)". Is that general enough for
every use case that you can foresee, or do we need more?Given what I foresee, simply having another columns in there named
evtstags with the exact same content as evttags would be the simplest
and most natural implementation, really.
That seems a lot less general for no particular gain.
I don't foresee more generic needs here, unless you can convince me that
we need both a. a full stack of arbitrarily nested commands and b. a way
to match and target any level of that stack.
Well, let's take the example of an ALTER TABLE command. You could
want to match on:
- the type of object the user mentioned in the command (did he write
ALTER TABLE or ALTER VIEW?)
- the type of object actually being modified (could differ if he used
ALTER TABLE on a view, or visca versa)
- the particular ALTER TABLE subcommand in use (e.g. SET STATISTICS)
I suspect there are other examples as well. If we pick the 2-D list
representation I suggested, or something like it, we can easily
accommodate these kinds of filters without having to whack the catalog
representation around any further. That seems pretty appealing.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
So let's try to hammer something out now. The obvious thing that
occurs to me is to have a column in the catalog that is a 2-D array of
text, with the first element of each array being something like "tag"
or "subtag" (i.e. event_trigger_variable) and the remaining array
elements being a list of legal values. That is:
WHEN thingy IN thingy IN ('item1', 'item2') AND otherthingy IN ('foo', 'bar')
would be represented as this array:
{{thingy,item1,item2},{otherthingy,foo,bar}}
Um, doesn't that require nonrectangular arrays? Or is there some
non-obvious reason why the lists of legal values will always be all the
same length?
regards, tom lane
Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:
I don't foresee more generic needs here, unless you can convince me that
we need both a. a full stack of arbitrarily nested commands and b. a way
to match and target any level of that stack.
Um ... isn't the burden of proof the other way around here? That is,
what's the argument that we *don't* need that?
regards, tom lane
On Mon, Jul 2, 2012 at 6:59 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
So let's try to hammer something out now. The obvious thing that
occurs to me is to have a column in the catalog that is a 2-D array of
text, with the first element of each array being something like "tag"
or "subtag" (i.e. event_trigger_variable) and the remaining array
elements being a list of legal values. That is:WHEN thingy IN thingy IN ('item1', 'item2') AND otherthingy IN ('foo', 'bar')
would be represented as this array:
{{thingy,item1,item2},{otherthingy,foo,bar}}
Um, doesn't that require nonrectangular arrays? Or is there some
non-obvious reason why the lists of legal values will always be all the
same length?
Doh. You're right: I keep forgetting that arrays have to be rectangular.
Any suggestions on a sensible way to represent this?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jul 2, 2012 at 6:59 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Um, doesn't that require nonrectangular arrays?
Doh. You're right: I keep forgetting that arrays have to be rectangular.
Any suggestions on a sensible way to represent this?
Are there likely to be enough entries that storage efficiency actually
matters? If not, we could use a 2xN array of {key,allowed_value} pairs,
that is
{{thingy,item1},{thingy,item2},{otherthingy,foo},{otherthingy,bar}}
Or perhaps push these out into a separate table, along the lines
of
oid key allowed_value
and use an oidvector to list the selected values in a trigger entry?
regards, tom lane
Robert Haas <robertmhaas@gmail.com> writes:
Given what I foresee, simply having another columns in there named
evtstags with the exact same content as evttags would be the simplest
and most natural implementation, really.That seems a lot less general for no particular gain.
The gain is code, docs and usage simplification. I think going general
here is going to confuse every one involved and that we should have two
targets in mind: classic use cases that we want to address easily enough
in SQL and with some PLpgSQL help, and advanced use cases that are
possible to implement in PL/C using the parse tree and the soon to come
back rewritten command string.
IOW, let's make the simple things simple and the complex one possible.
The following is quite long an email where I try to give plenty of
examples and to detail the logic I'm working with so that you can easily
stab at whichever part you're thinking is not going to fly.
I don't foresee more generic needs here, unless you can convince me that
we need both a. a full stack of arbitrarily nested commands and b. a way
to match and target any level of that stack.Well, let's take the example of an ALTER TABLE command. You could
want to match on:- the type of object the user mentioned in the command (did he write
ALTER TABLE or ALTER VIEW?)
- the type of object actually being modified (could differ if he used
I don't think it's possible to implement that without shaking all the
system, after having a look at the following lines from gram.y:
ALTER VIEW qualified_name alter_table_cmds
{
AlterTableStmt *n = makeNode(AlterTableStmt);
So, the way to implement that need from an event trigger is to use the
parse tree, and hopefully soon enough the rewritten command string.
ALTER TABLE on a view, or visca versa)
- the particular ALTER TABLE subcommand in use (e.g. SET STATISTICS)
Now we can publish that, we would have some events with
tag = 'ALTER TABLE'
then some others with
toplevel = 'ALTER TABLE'
tag = 'SET STATISTICS'
The same idea would need to get implemented for serial, where the tag is
'CREATE SEQUENCE' and the toplevel tag is 'CREATE TABLE'. That allows to
easily install an event trigger that gets called every time a sequence
is created, you can then have a look at the toplevel command tag if you
need to.
CREATE EVENT TRIGGER snitch_seqs
ON command_start
WHEN tag IN ('CREATE SEQUENCE')
EXECUTE PROCEDURE snitch_seqs();
The idea is that the function snitch_seqs has a "magic" variable
TG_TOPLEVEL that can be tested and will be set to 'CREATE TABLE' when
we're dealing with a SERIAL, in that example.
If you want your event trigger to only ever deal with SERIAL, you could
install it this way:
CREATE EVENT TRIGGER my_serial_trigger
ON command_start
WHEN toplevel IN ('CREATE TABLE')
AND tag IN ('CREATE SEQUENCE')
EXECUTE PROCEDURE handle_serial_trigger();
Now let's see about getting more generic than that.
We also can get tag = 'CREATE INDEX' and toplevel = 'ALTER TABLE' when
adding a primary key for example. That's an example that can lead us to
more than 2 levels of nested tags, which I would want to avoid. The
stack here would look like:
1. ALTER TABLE
2. ADD PRIMARY KEY
3. CREATE INDEX
I think only having 1 and 3 is enough, for more details the command
string and the parse tree are available. In the main use case of
replication, you mostly just want to replicate the command string. You
might want to apply some transformation rules to it (table names, cope
with a different target schema, etc) but typically those rules are to be
run in the subscriber system, not on the provider (picture a federating
system where each provider uses the same schema, that gets mapped to a
schema per provider on the subscriber).
The other problem with the stack of tags is matching them. I don't see
that it helps writing event triggers much. In the previous example, if
you want an event trigger that fires on each ALTER TABLE, you don't know
which level of the stack to target. Either you have to target the
current tag or the toplevel tag or something in between. We could easily
have the following tag stack:
1. CREATE SCHEMA
2. ALTER TABLE
3. ADD PRIMARY KEY
4. CREATE INDEX
So now we need a way to target any entry in the stack and a way to
represent the stack in every PL language we have, and an easy way to
analyze the stack. For PLpgSQL I guess that means we want to expose this
tag stack as a TABLE, and the complexity just went off the table.
My view point is that for any practical case I can think about what we
care about is the current command being run, and given how PostgreSQL is
made today that means handling one level of sub commands. That addresses
ALTER TABLE and also DROP CASCADE.
I don't think adding-in an ALTER TABLE that never happened in the middle
of those two elements is going to make life easier for anybody involved,
quite the contrary:
1. DROP TYPE
2. DROP COLUMN
Users that need that level of detail for their processing are welcome to
code their Event Trigger in PL/C and analyze the parse tree. We can call
that advanced analysis.
I suspect there are other examples as well. If we pick the 2-D list
representation I suggested, or something like it, we can easily
accommodate these kinds of filters without having to whack the catalog
representation around any further. That seems pretty appealing.
The generic approach leads us to invent a stack of tags and (I suspect)
a DSL for tag matching where you can express at least those different
things:
- this tag is found in the stack (tag <@ stack)
- this other tag is found higher in the stack
- this other tag is found just one level higher in the stack
- this other tag is found at least 2 levels higher in the stack
- this third tag is found lower in the stack
- this third tag is found just one level lower in the stack
- and maybe some more
Those semantics are going to be needed, either in the event trigger
definition itself, or in the event trigger code. We could expose a stack
of tags and ditch the WHEN clause here, but we still have to be able to
implement the filtering in PLpgSQL for simple cases.
If we're not able to express such detailed semantics I don't think we're
servicing users by making things way more complex for them to use. The
drawback is that we will have to make choices as to which tag we expose
exactly, remembering that all the details are to be found in the parse
tree and the rewritten command string.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Mon, Jul 2, 2012 at 11:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jul 2, 2012 at 6:59 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Um, doesn't that require nonrectangular arrays?
Doh. You're right: I keep forgetting that arrays have to be rectangular.
Any suggestions on a sensible way to represent this?
Are there likely to be enough entries that storage efficiency actually
matters? If not, we could use a 2xN array of {key,allowed_value} pairs,
that is{{thingy,item1},{thingy,item2},{otherthingy,foo},{otherthingy,bar}}
Or perhaps push these out into a separate table, along the lines
of
oid key allowed_value
and use an oidvector to list the selected values in a trigger entry?
It seems likely that there will fairly commonly be many allowed values
per key. However, the universe of legal keys will be quite small,
probably a total of 2-4. So maybe the best representation is to have
an a separate column for each key and store the array of legal values
in it. That's more or less what Dimitri already has in his latest
patch, except that after looking it over I'm inclined to think that
we'd be better off storing the keys as text and translating to
internal ID numbers when we read and cache the table, rather than
storing the ID numbers in the table. That will make the catalog
contents easier to read, and more importantly will mean that a change
to the internal ID numbers need not be initdb-forcing.
Thoughts?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
in it. That's more or less what Dimitri already has in his latest
patch, except that after looking it over I'm inclined to think that
we'd be better off storing the keys as text and translating to
internal ID numbers when we read and cache the table, rather than
storing the ID numbers in the table. That will make the catalog
contents easier to read, and more importantly will mean that a change
to the internal ID numbers need not be initdb-forcing.
Well that's what I had in previous versions of the patch, where the code
was dealing directly with command tags. If we store text in catalogs and
given that we already have the command tag as text, I suppose we would
get back to only managing tags as text in the cache key and the rest of
the code.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Tue, Jul 3, 2012 at 11:52 AM, Dimitri Fontaine
<dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
in it. That's more or less what Dimitri already has in his latest
patch, except that after looking it over I'm inclined to think that
we'd be better off storing the keys as text and translating to
internal ID numbers when we read and cache the table, rather than
storing the ID numbers in the table. That will make the catalog
contents easier to read, and more importantly will mean that a change
to the internal ID numbers need not be initdb-forcing.Well that's what I had in previous versions of the patch, where the code
was dealing directly with command tags. If we store text in catalogs and
given that we already have the command tag as text, I suppose we would
get back to only managing tags as text in the cache key and the rest of
the code.
Yeah, I'm of two minds on that. I thought that it made sense to use
integer identifiers internally for speed, but now I'm worried that the
effort to translate back and forth between strings and integers is
going to end up being more than any speed we might save. But I'm
still not certain which way is best: maybe your original idea of using
strings everywhere will prove to be the winner, but on the other hand
I'm not convinced that the code as you have it written today is as
efficient as it could be.
At any rate, whether or not we end up using strings or integers inside
the backend-local caches, I'm inclined to think that it's better to
store strings in the catalog, so we'd end up with something like this:
CATALOG(pg_event_trigger,3466)
{
NameData evtname; /* trigger's name */
int16 evtevent; /* trigger's event */
Oid evtfoid; /* OID of function to be
char evtenabled; /* trigger's firing configuratio
* session_repli
#ifdef CATALOG_VARLEN
text evttags[1]; /* command TAGs this event trigger targe
#endif
}
If there's no filter on tags, then I think we should let evttags = NULL.
If in the future we have filtering that's not based on tags, we'd add,
e.g. "text evtshushiness[1]" if we were going to filter based on
smushiness level. We'd set evtsmushiness = NULL if there's no
filtering by smushiness, or else an array of the smushiness levels
that fire that trigger.
Does that work for you?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, I'm of two minds on that. I thought that it made sense to use
integer identifiers internally for speed, but now I'm worried that the
effort to translate back and forth between strings and integers is
going to end up being more than any speed we might save.
We do that for nodetags in stored query/expression trees, and I've never
seen any indication that it costs enough to notice. It's definitely a
huge win for anything that changes regularly, which seems like it'll be
a pretty good description of event tags for at least the next few years.
regards, tom lane
On Tue, Jul 3, 2012 at 12:18 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, I'm of two minds on that. I thought that it made sense to use
integer identifiers internally for speed, but now I'm worried that the
effort to translate back and forth between strings and integers is
going to end up being more than any speed we might save.We do that for nodetags in stored query/expression trees, and I've never
seen any indication that it costs enough to notice. It's definitely a
huge win for anything that changes regularly, which seems like it'll be
a pretty good description of event tags for at least the next few years.
Good analogy. In that case, as in what I'm proposing here, we use
integers in memory and text on disk.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Jul 3, 2012 at 12:18 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, I'm of two minds on that. I thought that it made sense to use
integer identifiers internally for speed, but now I'm worried that the
effort to translate back and forth between strings and integers is
going to end up being more than any speed we might save.We do that for nodetags in stored query/expression trees, and I've never
seen any indication that it costs enough to notice. It's definitely a
huge win for anything that changes regularly, which seems like it'll be
a pretty good description of event tags for at least the next few years.Good analogy. In that case, as in what I'm proposing here, we use
integers in memory and text on disk.
New patch for that tomorrow. I assume we agree on having a column per
extra variable, I'm not sure about already including full support for
the toplevel one. With some luck I'll have news from you about that
before sending the next revision of the patch, which then would include
the int16 evttoplevel[1] column.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Tue, Jul 3, 2012 at 1:31 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Jul 3, 2012 at 12:18 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, I'm of two minds on that. I thought that it made sense to use
integer identifiers internally for speed, but now I'm worried that the
effort to translate back and forth between strings and integers is
going to end up being more than any speed we might save.We do that for nodetags in stored query/expression trees, and I've never
seen any indication that it costs enough to notice. It's definitely a
huge win for anything that changes regularly, which seems like it'll be
a pretty good description of event tags for at least the next few years.Good analogy. In that case, as in what I'm proposing here, we use
integers in memory and text on disk.New patch for that tomorrow. I assume we agree on having a column per
extra variable,
Yes.
I'm not sure about already including full support for
the toplevel one. With some luck I'll have news from you about that
before sending the next revision of the patch, which then would include
the int16 evttoplevel[1] column.
No, I'm not asking you to add any more columns right now (in fact,
please do not). But the type of the existing column should change to
text[].
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
No, I'm not asking you to add any more columns right now (in fact,
please do not). But the type of the existing column should change to
text[].
Ok, done in the attached. We now read text from either the user input in
the grammar or from the catalogs (in a 1-D array there), and convert to
our enum structure for internal code use.
In fact I attached both the new patch version and the incremental patch
on top of the v1.3 you had before, I suspect that might make life easier
for you. It's of course browsable online too at this URL:
https://github.com/dimitri/postgres/commit/a8cf89116ae7b6e51c8a580510612b85caae2d38
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
event_triggers_v1.3--v.1.4.patchtext/x-patchDownload
commit a8cf89116ae7b6e51c8a580510612b85caae2d38
Author: Dimitri Fontaine <dim@tapoueh.org>
Date: Wed Jul 4 16:51:36 2012 +0200
Store text in the event triggers catalogs rather than the enum/int constants
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df6da1f..6e29758 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -12,7 +12,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
- pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
+ pg_depend.o pg_enum.o pg_event_trigger.o pg_inherits.o pg_largeobject.o \
+ pg_namespace.o \
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
pg_type.o storage.o toasting.o
diff --git a/src/backend/catalog/pg_event_trigger.c b/src/backend/catalog/pg_event_trigger.c
new file mode 100644
index 0000000..7d52a84
--- /dev/null
+++ b/src/backend/catalog/pg_event_trigger.c
@@ -0,0 +1,200 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_event_trigger.c
+ * routines to support manipulation of the pg_event_trigger relation
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_event_trigger.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
+#include "utils/builtins.h"
+
+TrigEventCommand
+parse_event_tag(char *command, bool noerror)
+{
+ if (pg_strcasecmp(command, "ALTER AGGREGATE") == 0)
+ return E_AlterAggregate;
+ else if (pg_strcasecmp(command, "ALTER COLLATION") == 0)
+ return E_AlterCollation;
+ else if (pg_strcasecmp(command, "ALTER CONVERSION") == 0)
+ return E_AlterConversion;
+ else if (pg_strcasecmp(command, "ALTER DOMAIN") == 0)
+ return E_AlterDomain;
+ else if (pg_strcasecmp(command, "ALTER EXTENSION") == 0)
+ return E_AlterExtension;
+ else if (pg_strcasecmp(command, "ALTER FOREIGN DATA WRAPPER") == 0)
+ return E_AlterForeignDataWrapper;
+ else if (pg_strcasecmp(command, "ALTER FOREIGN TABLE") == 0)
+ return E_AlterForeignTable;
+ else if (pg_strcasecmp(command, "ALTER FUNCTION") == 0)
+ return E_AlterFunction;
+ else if (pg_strcasecmp(command, "ALTER LANGUAGE") == 0)
+ return E_AlterLanguage;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR") == 0)
+ return E_AlterOperator;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR CLASS") == 0)
+ return E_AlterOperatorClass;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR FAMILY") == 0)
+ return E_AlterOperatorFamily;
+ else if (pg_strcasecmp(command, "ALTER SEQUENCE") == 0)
+ return E_AlterSequence;
+ else if (pg_strcasecmp(command, "ALTER SERVER") == 0)
+ return E_AlterServer;
+ else if (pg_strcasecmp(command, "ALTER SCHEMA") == 0)
+ return E_AlterSchema;
+ else if (pg_strcasecmp(command, "ALTER TABLE") == 0)
+ return E_AlterTable;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH CONFIGURATION") == 0)
+ return E_AlterTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH DICTIONARY") == 0)
+ return E_AlterTextSearchDictionary;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH PARSER") == 0)
+ return E_AlterTextSearchParser;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH TEMPLATE") == 0)
+ return E_AlterTextSearchTemplate;
+ else if (pg_strcasecmp(command, "ALTER TRIGGER") == 0)
+ return E_AlterTrigger;
+ else if (pg_strcasecmp(command, "ALTER TYPE") == 0)
+ return E_AlterType;
+ else if (pg_strcasecmp(command, "ALTER USER MAPPING") == 0)
+ return E_AlterUserMapping;
+ else if (pg_strcasecmp(command, "ALTER VIEW") == 0)
+ return E_AlterView;
+ else if (pg_strcasecmp(command, "CLUSTER") == 0)
+ return E_Cluster;
+ else if (pg_strcasecmp(command, "CREATE AGGREGATE") == 0)
+ return E_CreateAggregate;
+ else if (pg_strcasecmp(command, "CREATE CAST") == 0)
+ return E_CreateCast;
+ else if (pg_strcasecmp(command, "CREATE COLLATION") == 0)
+ return E_CreateCollation;
+ else if (pg_strcasecmp(command, "CREATE CONVERSION") == 0)
+ return E_CreateConversion;
+ else if (pg_strcasecmp(command, "CREATE DOMAIN") == 0)
+ return E_CreateDomain;
+ else if (pg_strcasecmp(command, "CREATE EXTENSION") == 0)
+ return E_CreateExtension;
+ else if (pg_strcasecmp(command, "CREATE FOREIGN DATA WRAPPER") == 0)
+ return E_CreateForeignDataWrapper;
+ else if (pg_strcasecmp(command, "CREATE FOREIGN TABLE") == 0)
+ return E_CreateForeignTable;
+ else if (pg_strcasecmp(command, "CREATE FUNCTION") == 0)
+ return E_CreateFunction;
+ else if (pg_strcasecmp(command, "CREATE INDEX") == 0)
+ return E_CreateIndex;
+ else if (pg_strcasecmp(command, "CREATE LANGUAGE") == 0)
+ return E_CreateLanguage;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR") == 0)
+ return E_CreateOperator;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR CLASS") == 0)
+ return E_CreateOperatorClass;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR FAMILY") == 0)
+ return E_CreateOperatorFamily;
+ else if (pg_strcasecmp(command, "CREATE RULE") == 0)
+ return E_CreateRule;
+ else if (pg_strcasecmp(command, "CREATE SEQUENCE") == 0)
+ return E_CreateSequence;
+ else if (pg_strcasecmp(command, "CREATE SERVER") == 0)
+ return E_CreateServer;
+ else if (pg_strcasecmp(command, "CREATE SCHEMA") == 0)
+ return E_CreateSchema;
+ else if (pg_strcasecmp(command, "CREATE TABLE") == 0)
+ return E_CreateTable;
+ else if (pg_strcasecmp(command, "CREATE TABLE AS") == 0)
+ return E_CreateTableAs;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH CONFIGURATION") == 0)
+ return E_CreateTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH DICTIONARY") == 0)
+ return E_CreateTextSearchDictionary;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH PARSER") == 0)
+ return E_CreateTextSearchParser;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH TEMPLATE") == 0)
+ return E_CreateTextSearchTemplate;
+ else if (pg_strcasecmp(command, "CREATE TRIGGER") == 0)
+ return E_CreateTrigger;
+ else if (pg_strcasecmp(command, "CREATE TYPE") == 0)
+ return E_CreateType;
+ else if (pg_strcasecmp(command, "CREATE USER MAPPING") == 0)
+ return E_CreateUserMapping;
+ else if (pg_strcasecmp(command, "CREATE VIEW") == 0)
+ return E_CreateView;
+ else if (pg_strcasecmp(command, "DROP AGGREGATE") == 0)
+ return E_DropAggregate;
+ else if (pg_strcasecmp(command, "DROP CAST") == 0)
+ return E_DropCast;
+ else if (pg_strcasecmp(command, "DROP COLLATION") == 0)
+ return E_DropCollation;
+ else if (pg_strcasecmp(command, "DROP CONVERSION") == 0)
+ return E_DropConversion;
+ else if (pg_strcasecmp(command, "DROP DOMAIN") == 0)
+ return E_DropDomain;
+ else if (pg_strcasecmp(command, "DROP EXTENSION") == 0)
+ return E_DropExtension;
+ else if (pg_strcasecmp(command, "DROP FOREIGN DATA WRAPPER") == 0)
+ return E_DropForeignDataWrapper;
+ else if (pg_strcasecmp(command, "DROP FOREIGN TABLE") == 0)
+ return E_DropForeignTable;
+ else if (pg_strcasecmp(command, "DROP FUNCTION") == 0)
+ return E_DropFunction;
+ else if (pg_strcasecmp(command, "DROP INDEX") == 0)
+ return E_DropIndex;
+ else if (pg_strcasecmp(command, "DROP LANGUAGE") == 0)
+ return E_DropLanguage;
+ else if (pg_strcasecmp(command, "DROP OPERATOR") == 0)
+ return E_DropOperator;
+ else if (pg_strcasecmp(command, "DROP OPERATOR CLASS") == 0)
+ return E_DropOperatorClass;
+ else if (pg_strcasecmp(command, "DROP OPERATOR FAMILY") == 0)
+ return E_DropOperatorFamily;
+ else if (pg_strcasecmp(command, "DROP RULE") == 0)
+ return E_DropRule;
+ else if (pg_strcasecmp(command, "DROP SCHEMA") == 0)
+ return E_DropSchema;
+ else if (pg_strcasecmp(command, "DROP SEQUENCE") == 0)
+ return E_DropSequence;
+ else if (pg_strcasecmp(command, "DROP SERVER") == 0)
+ return E_DropServer;
+ else if (pg_strcasecmp(command, "DROP TABLE") == 0)
+ return E_DropTable;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH CONFIGURATION") == 0)
+ return E_DropTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH DICTIONARY") == 0)
+ return E_DropTextSearchDictionary;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH PARSER") == 0)
+ return E_DropTextSearchParser;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH TEMPLATE") == 0)
+ return E_DropTextSearchTemplate;
+ else if (pg_strcasecmp(command, "DROP TRIGGER") == 0)
+ return E_DropTrigger;
+ else if (pg_strcasecmp(command, "DROP TYPE") == 0)
+ return E_DropType;
+ else if (pg_strcasecmp(command, "DROP USER MAPPING") == 0)
+ return E_DropUserMapping;
+ else if (pg_strcasecmp(command, "DROP VIEW") == 0)
+ return E_DropView;
+ else if (pg_strcasecmp(command, "LOAD") == 0)
+ return E_Load;
+ else if (pg_strcasecmp(command, "REINDEX") == 0)
+ return E_Reindex;
+ else if (pg_strcasecmp(command, "SELECT INTO") == 0)
+ return E_SelectInto;
+ else if (pg_strcasecmp(command, "VACUUM") == 0)
+ return E_Vacuum;
+ else
+ {
+ if (!noerror)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized command \"%s\"", command)));
+ }
+ return E_UNKNOWN;
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 13ffeb5..a93698b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -103,9 +103,13 @@ InsertEventTriggerTuple(char *trigname, TrigEvent event,
foreach(lc, cmdlist)
{
- tags[i++] = Int16GetDatum(lfirst_int(lc));
+ TrigEventCommand cmd = lfirst_int(lc);
+ char *cmdstr = command_to_string(cmd);
+ if (cmd == E_UNKNOWN || cmdstr == NULL)
+ elog(ERROR, "Unknown command %d", cmd);
+ tags[i++] = PointerGetDatum(cstring_to_text(cmdstr));
}
- tagArray = construct_array(tags, l, INT2OID, 2, true, 's');
+ tagArray = construct_array(tags, l, TEXTOID, -1, false, 'i');
values[Anum_pg_event_trigger_evttags - 1] = PointerGetDatum(tagArray);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ca95d76..21d92ef 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -54,6 +54,7 @@
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -4381,182 +4382,17 @@ trigger_command_list:
trigger_command:
SCONST
{
- if (pg_strcasecmp($1, "ALTER AGGREGATE") == 0)
- $$ = E_AlterAggregate;
- else if (pg_strcasecmp($1, "ALTER COLLATION") == 0)
- $$ = E_AlterCollation;
- else if (pg_strcasecmp($1, "ALTER CONVERSION") == 0)
- $$ = E_AlterConversion;
- else if (pg_strcasecmp($1, "ALTER DOMAIN") == 0)
- $$ = E_AlterDomain;
- else if (pg_strcasecmp($1, "ALTER EXTENSION") == 0)
- $$ = E_AlterExtension;
- else if (pg_strcasecmp($1, "ALTER FOREIGN DATA WRAPPER") == 0)
- $$ = E_AlterForeignDataWrapper;
- else if (pg_strcasecmp($1, "ALTER FOREIGN TABLE") == 0)
- $$ = E_AlterForeignTable;
- else if (pg_strcasecmp($1, "ALTER FUNCTION") == 0)
- $$ = E_AlterFunction;
- else if (pg_strcasecmp($1, "ALTER LANGUAGE") == 0)
- $$ = E_AlterLanguage;
- else if (pg_strcasecmp($1, "ALTER OPERATOR") == 0)
- $$ = E_AlterOperator;
- else if (pg_strcasecmp($1, "ALTER OPERATOR CLASS") == 0)
- $$ = E_AlterOperatorClass;
- else if (pg_strcasecmp($1, "ALTER OPERATOR FAMILY") == 0)
- $$ = E_AlterOperatorFamily;
- else if (pg_strcasecmp($1, "ALTER SEQUENCE") == 0)
- $$ = E_AlterSequence;
- else if (pg_strcasecmp($1, "ALTER SERVER") == 0)
- $$ = E_AlterServer;
- else if (pg_strcasecmp($1, "ALTER SCHEMA") == 0)
- $$ = E_AlterSchema;
- else if (pg_strcasecmp($1, "ALTER TABLE") == 0)
- $$ = E_AlterTable;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_AlterTextSearchConfiguration;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH DICTIONARY") == 0)
- $$ = E_AlterTextSearchDictionary;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH PARSER") == 0)
- $$ = E_AlterTextSearchParser;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH TEMPLATE") == 0)
- $$ = E_AlterTextSearchTemplate;
- else if (pg_strcasecmp($1, "ALTER TRIGGER") == 0)
- $$ = E_AlterTrigger;
- else if (pg_strcasecmp($1, "ALTER TYPE") == 0)
- $$ = E_AlterType;
- else if (pg_strcasecmp($1, "ALTER USER MAPPING") == 0)
- $$ = E_AlterUserMapping;
- else if (pg_strcasecmp($1, "ALTER VIEW") == 0)
- $$ = E_AlterView;
- else if (pg_strcasecmp($1, "CLUSTER") == 0)
- $$ = E_Cluster;
- else if (pg_strcasecmp($1, "CREATE AGGREGATE") == 0)
- $$ = E_CreateAggregate;
- else if (pg_strcasecmp($1, "CREATE CAST") == 0)
- $$ = E_CreateCast;
- else if (pg_strcasecmp($1, "CREATE COLLATION") == 0)
- $$ = E_CreateCollation;
- else if (pg_strcasecmp($1, "CREATE CONVERSION") == 0)
- $$ = E_CreateConversion;
- else if (pg_strcasecmp($1, "CREATE DOMAIN") == 0)
- $$ = E_CreateDomain;
- else if (pg_strcasecmp($1, "CREATE EXTENSION") == 0)
- $$ = E_CreateExtension;
- else if (pg_strcasecmp($1, "CREATE FOREIGN DATA WRAPPER") == 0)
- $$ = E_CreateForeignDataWrapper;
- else if (pg_strcasecmp($1, "CREATE FOREIGN TABLE") == 0)
- $$ = E_CreateForeignTable;
- else if (pg_strcasecmp($1, "CREATE FUNCTION") == 0)
- $$ = E_CreateFunction;
- else if (pg_strcasecmp($1, "CREATE INDEX") == 0)
- $$ = E_CreateIndex;
- else if (pg_strcasecmp($1, "CREATE LANGUAGE") == 0)
- $$ = E_CreateLanguage;
- else if (pg_strcasecmp($1, "CREATE OPERATOR") == 0)
- $$ = E_CreateOperator;
- else if (pg_strcasecmp($1, "CREATE OPERATOR CLASS") == 0)
- $$ = E_CreateOperatorClass;
- else if (pg_strcasecmp($1, "CREATE OPERATOR FAMILY") == 0)
- $$ = E_CreateOperatorFamily;
- else if (pg_strcasecmp($1, "CREATE RULE") == 0)
- $$ = E_CreateRule;
- else if (pg_strcasecmp($1, "CREATE SEQUENCE") == 0)
- $$ = E_CreateSequence;
- else if (pg_strcasecmp($1, "CREATE SERVER") == 0)
- $$ = E_CreateServer;
- else if (pg_strcasecmp($1, "CREATE SCHEMA") == 0)
- $$ = E_CreateSchema;
- else if (pg_strcasecmp($1, "CREATE TABLE") == 0)
- $$ = E_CreateTable;
- else if (pg_strcasecmp($1, "CREATE TABLE AS") == 0)
- $$ = E_CreateTableAs;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_CreateTextSearchConfiguration;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH DICTIONARY") == 0)
- $$ = E_CreateTextSearchDictionary;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH PARSER") == 0)
- $$ = E_CreateTextSearchParser;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH TEMPLATE") == 0)
- $$ = E_CreateTextSearchTemplate;
- else if (pg_strcasecmp($1, "CREATE TRIGGER") == 0)
- $$ = E_CreateTrigger;
- else if (pg_strcasecmp($1, "CREATE TYPE") == 0)
- $$ = E_CreateType;
- else if (pg_strcasecmp($1, "CREATE USER MAPPING") == 0)
- $$ = E_CreateUserMapping;
- else if (pg_strcasecmp($1, "CREATE VIEW") == 0)
- $$ = E_CreateView;
- else if (pg_strcasecmp($1, "DROP AGGREGATE") == 0)
- $$ = E_DropAggregate;
- else if (pg_strcasecmp($1, "DROP CAST") == 0)
- $$ = E_DropCast;
- else if (pg_strcasecmp($1, "DROP COLLATION") == 0)
- $$ = E_DropCollation;
- else if (pg_strcasecmp($1, "DROP CONVERSION") == 0)
- $$ = E_DropConversion;
- else if (pg_strcasecmp($1, "DROP DOMAIN") == 0)
- $$ = E_DropDomain;
- else if (pg_strcasecmp($1, "DROP EXTENSION") == 0)
- $$ = E_DropExtension;
- else if (pg_strcasecmp($1, "DROP FOREIGN DATA WRAPPER") == 0)
- $$ = E_DropForeignDataWrapper;
- else if (pg_strcasecmp($1, "DROP FOREIGN TABLE") == 0)
- $$ = E_DropForeignTable;
- else if (pg_strcasecmp($1, "DROP FUNCTION") == 0)
- $$ = E_DropFunction;
- else if (pg_strcasecmp($1, "DROP INDEX") == 0)
- $$ = E_DropIndex;
- else if (pg_strcasecmp($1, "DROP LANGUAGE") == 0)
- $$ = E_DropLanguage;
- else if (pg_strcasecmp($1, "DROP OPERATOR") == 0)
- $$ = E_DropOperator;
- else if (pg_strcasecmp($1, "DROP OPERATOR CLASS") == 0)
- $$ = E_DropOperatorClass;
- else if (pg_strcasecmp($1, "DROP OPERATOR FAMILY") == 0)
- $$ = E_DropOperatorFamily;
- else if (pg_strcasecmp($1, "DROP RULE") == 0)
- $$ = E_DropRule;
- else if (pg_strcasecmp($1, "DROP SCHEMA") == 0)
- $$ = E_DropSchema;
- else if (pg_strcasecmp($1, "DROP SEQUENCE") == 0)
- $$ = E_DropSequence;
- else if (pg_strcasecmp($1, "DROP SERVER") == 0)
- $$ = E_DropServer;
- else if (pg_strcasecmp($1, "DROP TABLE") == 0)
- $$ = E_DropTable;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_DropTextSearchConfiguration;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH DICTIONARY") == 0)
- $$ = E_DropTextSearchDictionary;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH PARSER") == 0)
- $$ = E_DropTextSearchParser;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH TEMPLATE") == 0)
- $$ = E_DropTextSearchTemplate;
- else if (pg_strcasecmp($1, "DROP TRIGGER") == 0)
- $$ = E_DropTrigger;
- else if (pg_strcasecmp($1, "DROP TYPE") == 0)
- $$ = E_DropType;
- else if (pg_strcasecmp($1, "DROP USER MAPPING") == 0)
- $$ = E_DropUserMapping;
- else if (pg_strcasecmp($1, "DROP VIEW") == 0)
- $$ = E_DropView;
- else if (pg_strcasecmp($1, "LOAD") == 0)
- $$ = E_Load;
- else if (pg_strcasecmp($1, "REINDEX") == 0)
- $$ = E_Reindex;
- else if (pg_strcasecmp($1, "SELECT INTO") == 0)
- $$ = E_SelectInto;
- else if (pg_strcasecmp($1, "VACUUM") == 0)
- $$ = E_Vacuum;
- else
+ TrigEventCommand cmdtag = parse_event_tag($1, true);
+ if (cmdtag == E_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized command \"%s\"", $1),
- parser_errposition(@1)));
+ parser_errposition(@1)));
+ $$ = cmdtag;
}
;
+
DropEventTrigStmt:
DROP EVENT TRIGGER name opt_drop_behavior
{
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index e08f045..660d1cf 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -23,6 +23,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -195,7 +196,7 @@ BuildEventTriggerCache()
else
{
ArrayType *arr;
- int16 *tags;
+ Datum *tags;
int i;
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
@@ -204,15 +205,17 @@ BuildEventTriggerCache()
if (ARR_NDIM(arr) != 1 ||
numkeys < 0 ||
ARR_HASNULL(arr) ||
- ARR_ELEMTYPE(arr) != INT2OID)
- elog(ERROR, "evttags is not a 1-D smallint array");
+ ARR_ELEMTYPE(arr) != TEXTOID)
+ elog(ERROR, "evttags is not a 1-D text array");
- tags = (int16 *) ARR_DATA_PTR(arr);
+ deconstruct_array(arr, TEXTOID, -1, false, 'i',
+ &tags, NULL, &numkeys);
for (i = 0; i < numkeys; i++)
{
- command = tags[i];
- add_funcall_to_command_event(event, command, name, proc);
+ char *cmdstr = TextDatumGetCString(tags[i]);
+ command = parse_event_tag(cmdstr, false);
+ add_funcall_to_command_event(event, command, name, proc);
}
}
}
diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h
index 9af38a1..6e270e3 100644
--- a/src/include/catalog/pg_event_trigger.h
+++ b/src/include/catalog/pg_event_trigger.h
@@ -36,7 +36,7 @@ CATALOG(pg_event_trigger,3466)
char evtenabled; /* trigger's firing configuration WRT
* session_replication_role */
#ifdef CATALOG_VARLEN
- int16 evttags[1]; /* command TAGs this event trigger targets */
+ text evttags[1]; /* command TAGs this event trigger targets */
#endif
} FormData_pg_event_trigger;
diff --git a/src/include/catalog/pg_event_trigger_fn.h b/src/include/catalog/pg_event_trigger_fn.h
new file mode 100644
index 0000000..d1e7502
--- /dev/null
+++ b/src/include/catalog/pg_event_trigger_fn.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_event_trigger_fn.h
+ * prototypes for functions in catalog/pg_event_trigger.c
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_event_trigger_fn.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_EVENT_TRIGGER_FN_H
+#define PG_EVENT_TRIGGER_FN_H
+
+#include "catalog/pg_event_trigger.h"
+
+TrigEventCommand parse_event_tag(char *command, bool noerror);
+
+#endif /* PG_EVENT_TRIGGER_FN_H */
Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:
Robert Haas <robertmhaas@gmail.com> writes:
No, I'm not asking you to add any more columns right now (in fact,
please do not). But the type of the existing column should change to
text[].Ok, done in the attached. We now read text from either the user input in
the grammar or from the catalogs (in a 1-D array there), and convert to
our enum structure for internal code use.
In the previous one I missed adapting pg_dump and psql, here's the
updated set of patches. Sorry about that. This unexpected electrical
shut down in the middle of the day was not cool. Nor was the second one.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
event_triggers_v1.3--v1.5.patchtext/x-patchDownload
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df6da1f..6e29758 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -12,7 +12,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
- pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
+ pg_depend.o pg_enum.o pg_event_trigger.o pg_inherits.o pg_largeobject.o \
+ pg_namespace.o \
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
pg_type.o storage.o toasting.o
diff --git a/src/backend/catalog/pg_event_trigger.c b/src/backend/catalog/pg_event_trigger.c
new file mode 100644
index 0000000..7d52a84
--- /dev/null
+++ b/src/backend/catalog/pg_event_trigger.c
@@ -0,0 +1,200 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_event_trigger.c
+ * routines to support manipulation of the pg_event_trigger relation
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_event_trigger.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
+#include "utils/builtins.h"
+
+TrigEventCommand
+parse_event_tag(char *command, bool noerror)
+{
+ if (pg_strcasecmp(command, "ALTER AGGREGATE") == 0)
+ return E_AlterAggregate;
+ else if (pg_strcasecmp(command, "ALTER COLLATION") == 0)
+ return E_AlterCollation;
+ else if (pg_strcasecmp(command, "ALTER CONVERSION") == 0)
+ return E_AlterConversion;
+ else if (pg_strcasecmp(command, "ALTER DOMAIN") == 0)
+ return E_AlterDomain;
+ else if (pg_strcasecmp(command, "ALTER EXTENSION") == 0)
+ return E_AlterExtension;
+ else if (pg_strcasecmp(command, "ALTER FOREIGN DATA WRAPPER") == 0)
+ return E_AlterForeignDataWrapper;
+ else if (pg_strcasecmp(command, "ALTER FOREIGN TABLE") == 0)
+ return E_AlterForeignTable;
+ else if (pg_strcasecmp(command, "ALTER FUNCTION") == 0)
+ return E_AlterFunction;
+ else if (pg_strcasecmp(command, "ALTER LANGUAGE") == 0)
+ return E_AlterLanguage;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR") == 0)
+ return E_AlterOperator;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR CLASS") == 0)
+ return E_AlterOperatorClass;
+ else if (pg_strcasecmp(command, "ALTER OPERATOR FAMILY") == 0)
+ return E_AlterOperatorFamily;
+ else if (pg_strcasecmp(command, "ALTER SEQUENCE") == 0)
+ return E_AlterSequence;
+ else if (pg_strcasecmp(command, "ALTER SERVER") == 0)
+ return E_AlterServer;
+ else if (pg_strcasecmp(command, "ALTER SCHEMA") == 0)
+ return E_AlterSchema;
+ else if (pg_strcasecmp(command, "ALTER TABLE") == 0)
+ return E_AlterTable;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH CONFIGURATION") == 0)
+ return E_AlterTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH DICTIONARY") == 0)
+ return E_AlterTextSearchDictionary;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH PARSER") == 0)
+ return E_AlterTextSearchParser;
+ else if (pg_strcasecmp(command, "ALTER TEXT SEARCH TEMPLATE") == 0)
+ return E_AlterTextSearchTemplate;
+ else if (pg_strcasecmp(command, "ALTER TRIGGER") == 0)
+ return E_AlterTrigger;
+ else if (pg_strcasecmp(command, "ALTER TYPE") == 0)
+ return E_AlterType;
+ else if (pg_strcasecmp(command, "ALTER USER MAPPING") == 0)
+ return E_AlterUserMapping;
+ else if (pg_strcasecmp(command, "ALTER VIEW") == 0)
+ return E_AlterView;
+ else if (pg_strcasecmp(command, "CLUSTER") == 0)
+ return E_Cluster;
+ else if (pg_strcasecmp(command, "CREATE AGGREGATE") == 0)
+ return E_CreateAggregate;
+ else if (pg_strcasecmp(command, "CREATE CAST") == 0)
+ return E_CreateCast;
+ else if (pg_strcasecmp(command, "CREATE COLLATION") == 0)
+ return E_CreateCollation;
+ else if (pg_strcasecmp(command, "CREATE CONVERSION") == 0)
+ return E_CreateConversion;
+ else if (pg_strcasecmp(command, "CREATE DOMAIN") == 0)
+ return E_CreateDomain;
+ else if (pg_strcasecmp(command, "CREATE EXTENSION") == 0)
+ return E_CreateExtension;
+ else if (pg_strcasecmp(command, "CREATE FOREIGN DATA WRAPPER") == 0)
+ return E_CreateForeignDataWrapper;
+ else if (pg_strcasecmp(command, "CREATE FOREIGN TABLE") == 0)
+ return E_CreateForeignTable;
+ else if (pg_strcasecmp(command, "CREATE FUNCTION") == 0)
+ return E_CreateFunction;
+ else if (pg_strcasecmp(command, "CREATE INDEX") == 0)
+ return E_CreateIndex;
+ else if (pg_strcasecmp(command, "CREATE LANGUAGE") == 0)
+ return E_CreateLanguage;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR") == 0)
+ return E_CreateOperator;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR CLASS") == 0)
+ return E_CreateOperatorClass;
+ else if (pg_strcasecmp(command, "CREATE OPERATOR FAMILY") == 0)
+ return E_CreateOperatorFamily;
+ else if (pg_strcasecmp(command, "CREATE RULE") == 0)
+ return E_CreateRule;
+ else if (pg_strcasecmp(command, "CREATE SEQUENCE") == 0)
+ return E_CreateSequence;
+ else if (pg_strcasecmp(command, "CREATE SERVER") == 0)
+ return E_CreateServer;
+ else if (pg_strcasecmp(command, "CREATE SCHEMA") == 0)
+ return E_CreateSchema;
+ else if (pg_strcasecmp(command, "CREATE TABLE") == 0)
+ return E_CreateTable;
+ else if (pg_strcasecmp(command, "CREATE TABLE AS") == 0)
+ return E_CreateTableAs;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH CONFIGURATION") == 0)
+ return E_CreateTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH DICTIONARY") == 0)
+ return E_CreateTextSearchDictionary;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH PARSER") == 0)
+ return E_CreateTextSearchParser;
+ else if (pg_strcasecmp(command, "CREATE TEXT SEARCH TEMPLATE") == 0)
+ return E_CreateTextSearchTemplate;
+ else if (pg_strcasecmp(command, "CREATE TRIGGER") == 0)
+ return E_CreateTrigger;
+ else if (pg_strcasecmp(command, "CREATE TYPE") == 0)
+ return E_CreateType;
+ else if (pg_strcasecmp(command, "CREATE USER MAPPING") == 0)
+ return E_CreateUserMapping;
+ else if (pg_strcasecmp(command, "CREATE VIEW") == 0)
+ return E_CreateView;
+ else if (pg_strcasecmp(command, "DROP AGGREGATE") == 0)
+ return E_DropAggregate;
+ else if (pg_strcasecmp(command, "DROP CAST") == 0)
+ return E_DropCast;
+ else if (pg_strcasecmp(command, "DROP COLLATION") == 0)
+ return E_DropCollation;
+ else if (pg_strcasecmp(command, "DROP CONVERSION") == 0)
+ return E_DropConversion;
+ else if (pg_strcasecmp(command, "DROP DOMAIN") == 0)
+ return E_DropDomain;
+ else if (pg_strcasecmp(command, "DROP EXTENSION") == 0)
+ return E_DropExtension;
+ else if (pg_strcasecmp(command, "DROP FOREIGN DATA WRAPPER") == 0)
+ return E_DropForeignDataWrapper;
+ else if (pg_strcasecmp(command, "DROP FOREIGN TABLE") == 0)
+ return E_DropForeignTable;
+ else if (pg_strcasecmp(command, "DROP FUNCTION") == 0)
+ return E_DropFunction;
+ else if (pg_strcasecmp(command, "DROP INDEX") == 0)
+ return E_DropIndex;
+ else if (pg_strcasecmp(command, "DROP LANGUAGE") == 0)
+ return E_DropLanguage;
+ else if (pg_strcasecmp(command, "DROP OPERATOR") == 0)
+ return E_DropOperator;
+ else if (pg_strcasecmp(command, "DROP OPERATOR CLASS") == 0)
+ return E_DropOperatorClass;
+ else if (pg_strcasecmp(command, "DROP OPERATOR FAMILY") == 0)
+ return E_DropOperatorFamily;
+ else if (pg_strcasecmp(command, "DROP RULE") == 0)
+ return E_DropRule;
+ else if (pg_strcasecmp(command, "DROP SCHEMA") == 0)
+ return E_DropSchema;
+ else if (pg_strcasecmp(command, "DROP SEQUENCE") == 0)
+ return E_DropSequence;
+ else if (pg_strcasecmp(command, "DROP SERVER") == 0)
+ return E_DropServer;
+ else if (pg_strcasecmp(command, "DROP TABLE") == 0)
+ return E_DropTable;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH CONFIGURATION") == 0)
+ return E_DropTextSearchConfiguration;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH DICTIONARY") == 0)
+ return E_DropTextSearchDictionary;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH PARSER") == 0)
+ return E_DropTextSearchParser;
+ else if (pg_strcasecmp(command, "DROP TEXT SEARCH TEMPLATE") == 0)
+ return E_DropTextSearchTemplate;
+ else if (pg_strcasecmp(command, "DROP TRIGGER") == 0)
+ return E_DropTrigger;
+ else if (pg_strcasecmp(command, "DROP TYPE") == 0)
+ return E_DropType;
+ else if (pg_strcasecmp(command, "DROP USER MAPPING") == 0)
+ return E_DropUserMapping;
+ else if (pg_strcasecmp(command, "DROP VIEW") == 0)
+ return E_DropView;
+ else if (pg_strcasecmp(command, "LOAD") == 0)
+ return E_Load;
+ else if (pg_strcasecmp(command, "REINDEX") == 0)
+ return E_Reindex;
+ else if (pg_strcasecmp(command, "SELECT INTO") == 0)
+ return E_SelectInto;
+ else if (pg_strcasecmp(command, "VACUUM") == 0)
+ return E_Vacuum;
+ else
+ {
+ if (!noerror)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized command \"%s\"", command)));
+ }
+ return E_UNKNOWN;
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 13ffeb5..a93698b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -103,9 +103,13 @@ InsertEventTriggerTuple(char *trigname, TrigEvent event,
foreach(lc, cmdlist)
{
- tags[i++] = Int16GetDatum(lfirst_int(lc));
+ TrigEventCommand cmd = lfirst_int(lc);
+ char *cmdstr = command_to_string(cmd);
+ if (cmd == E_UNKNOWN || cmdstr == NULL)
+ elog(ERROR, "Unknown command %d", cmd);
+ tags[i++] = PointerGetDatum(cstring_to_text(cmdstr));
}
- tagArray = construct_array(tags, l, INT2OID, 2, true, 's');
+ tagArray = construct_array(tags, l, TEXTOID, -1, false, 'i');
values[Anum_pg_event_trigger_evttags - 1] = PointerGetDatum(tagArray);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ca95d76..21d92ef 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -54,6 +54,7 @@
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -4381,182 +4382,17 @@ trigger_command_list:
trigger_command:
SCONST
{
- if (pg_strcasecmp($1, "ALTER AGGREGATE") == 0)
- $$ = E_AlterAggregate;
- else if (pg_strcasecmp($1, "ALTER COLLATION") == 0)
- $$ = E_AlterCollation;
- else if (pg_strcasecmp($1, "ALTER CONVERSION") == 0)
- $$ = E_AlterConversion;
- else if (pg_strcasecmp($1, "ALTER DOMAIN") == 0)
- $$ = E_AlterDomain;
- else if (pg_strcasecmp($1, "ALTER EXTENSION") == 0)
- $$ = E_AlterExtension;
- else if (pg_strcasecmp($1, "ALTER FOREIGN DATA WRAPPER") == 0)
- $$ = E_AlterForeignDataWrapper;
- else if (pg_strcasecmp($1, "ALTER FOREIGN TABLE") == 0)
- $$ = E_AlterForeignTable;
- else if (pg_strcasecmp($1, "ALTER FUNCTION") == 0)
- $$ = E_AlterFunction;
- else if (pg_strcasecmp($1, "ALTER LANGUAGE") == 0)
- $$ = E_AlterLanguage;
- else if (pg_strcasecmp($1, "ALTER OPERATOR") == 0)
- $$ = E_AlterOperator;
- else if (pg_strcasecmp($1, "ALTER OPERATOR CLASS") == 0)
- $$ = E_AlterOperatorClass;
- else if (pg_strcasecmp($1, "ALTER OPERATOR FAMILY") == 0)
- $$ = E_AlterOperatorFamily;
- else if (pg_strcasecmp($1, "ALTER SEQUENCE") == 0)
- $$ = E_AlterSequence;
- else if (pg_strcasecmp($1, "ALTER SERVER") == 0)
- $$ = E_AlterServer;
- else if (pg_strcasecmp($1, "ALTER SCHEMA") == 0)
- $$ = E_AlterSchema;
- else if (pg_strcasecmp($1, "ALTER TABLE") == 0)
- $$ = E_AlterTable;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_AlterTextSearchConfiguration;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH DICTIONARY") == 0)
- $$ = E_AlterTextSearchDictionary;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH PARSER") == 0)
- $$ = E_AlterTextSearchParser;
- else if (pg_strcasecmp($1, "ALTER TEXT SEARCH TEMPLATE") == 0)
- $$ = E_AlterTextSearchTemplate;
- else if (pg_strcasecmp($1, "ALTER TRIGGER") == 0)
- $$ = E_AlterTrigger;
- else if (pg_strcasecmp($1, "ALTER TYPE") == 0)
- $$ = E_AlterType;
- else if (pg_strcasecmp($1, "ALTER USER MAPPING") == 0)
- $$ = E_AlterUserMapping;
- else if (pg_strcasecmp($1, "ALTER VIEW") == 0)
- $$ = E_AlterView;
- else if (pg_strcasecmp($1, "CLUSTER") == 0)
- $$ = E_Cluster;
- else if (pg_strcasecmp($1, "CREATE AGGREGATE") == 0)
- $$ = E_CreateAggregate;
- else if (pg_strcasecmp($1, "CREATE CAST") == 0)
- $$ = E_CreateCast;
- else if (pg_strcasecmp($1, "CREATE COLLATION") == 0)
- $$ = E_CreateCollation;
- else if (pg_strcasecmp($1, "CREATE CONVERSION") == 0)
- $$ = E_CreateConversion;
- else if (pg_strcasecmp($1, "CREATE DOMAIN") == 0)
- $$ = E_CreateDomain;
- else if (pg_strcasecmp($1, "CREATE EXTENSION") == 0)
- $$ = E_CreateExtension;
- else if (pg_strcasecmp($1, "CREATE FOREIGN DATA WRAPPER") == 0)
- $$ = E_CreateForeignDataWrapper;
- else if (pg_strcasecmp($1, "CREATE FOREIGN TABLE") == 0)
- $$ = E_CreateForeignTable;
- else if (pg_strcasecmp($1, "CREATE FUNCTION") == 0)
- $$ = E_CreateFunction;
- else if (pg_strcasecmp($1, "CREATE INDEX") == 0)
- $$ = E_CreateIndex;
- else if (pg_strcasecmp($1, "CREATE LANGUAGE") == 0)
- $$ = E_CreateLanguage;
- else if (pg_strcasecmp($1, "CREATE OPERATOR") == 0)
- $$ = E_CreateOperator;
- else if (pg_strcasecmp($1, "CREATE OPERATOR CLASS") == 0)
- $$ = E_CreateOperatorClass;
- else if (pg_strcasecmp($1, "CREATE OPERATOR FAMILY") == 0)
- $$ = E_CreateOperatorFamily;
- else if (pg_strcasecmp($1, "CREATE RULE") == 0)
- $$ = E_CreateRule;
- else if (pg_strcasecmp($1, "CREATE SEQUENCE") == 0)
- $$ = E_CreateSequence;
- else if (pg_strcasecmp($1, "CREATE SERVER") == 0)
- $$ = E_CreateServer;
- else if (pg_strcasecmp($1, "CREATE SCHEMA") == 0)
- $$ = E_CreateSchema;
- else if (pg_strcasecmp($1, "CREATE TABLE") == 0)
- $$ = E_CreateTable;
- else if (pg_strcasecmp($1, "CREATE TABLE AS") == 0)
- $$ = E_CreateTableAs;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_CreateTextSearchConfiguration;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH DICTIONARY") == 0)
- $$ = E_CreateTextSearchDictionary;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH PARSER") == 0)
- $$ = E_CreateTextSearchParser;
- else if (pg_strcasecmp($1, "CREATE TEXT SEARCH TEMPLATE") == 0)
- $$ = E_CreateTextSearchTemplate;
- else if (pg_strcasecmp($1, "CREATE TRIGGER") == 0)
- $$ = E_CreateTrigger;
- else if (pg_strcasecmp($1, "CREATE TYPE") == 0)
- $$ = E_CreateType;
- else if (pg_strcasecmp($1, "CREATE USER MAPPING") == 0)
- $$ = E_CreateUserMapping;
- else if (pg_strcasecmp($1, "CREATE VIEW") == 0)
- $$ = E_CreateView;
- else if (pg_strcasecmp($1, "DROP AGGREGATE") == 0)
- $$ = E_DropAggregate;
- else if (pg_strcasecmp($1, "DROP CAST") == 0)
- $$ = E_DropCast;
- else if (pg_strcasecmp($1, "DROP COLLATION") == 0)
- $$ = E_DropCollation;
- else if (pg_strcasecmp($1, "DROP CONVERSION") == 0)
- $$ = E_DropConversion;
- else if (pg_strcasecmp($1, "DROP DOMAIN") == 0)
- $$ = E_DropDomain;
- else if (pg_strcasecmp($1, "DROP EXTENSION") == 0)
- $$ = E_DropExtension;
- else if (pg_strcasecmp($1, "DROP FOREIGN DATA WRAPPER") == 0)
- $$ = E_DropForeignDataWrapper;
- else if (pg_strcasecmp($1, "DROP FOREIGN TABLE") == 0)
- $$ = E_DropForeignTable;
- else if (pg_strcasecmp($1, "DROP FUNCTION") == 0)
- $$ = E_DropFunction;
- else if (pg_strcasecmp($1, "DROP INDEX") == 0)
- $$ = E_DropIndex;
- else if (pg_strcasecmp($1, "DROP LANGUAGE") == 0)
- $$ = E_DropLanguage;
- else if (pg_strcasecmp($1, "DROP OPERATOR") == 0)
- $$ = E_DropOperator;
- else if (pg_strcasecmp($1, "DROP OPERATOR CLASS") == 0)
- $$ = E_DropOperatorClass;
- else if (pg_strcasecmp($1, "DROP OPERATOR FAMILY") == 0)
- $$ = E_DropOperatorFamily;
- else if (pg_strcasecmp($1, "DROP RULE") == 0)
- $$ = E_DropRule;
- else if (pg_strcasecmp($1, "DROP SCHEMA") == 0)
- $$ = E_DropSchema;
- else if (pg_strcasecmp($1, "DROP SEQUENCE") == 0)
- $$ = E_DropSequence;
- else if (pg_strcasecmp($1, "DROP SERVER") == 0)
- $$ = E_DropServer;
- else if (pg_strcasecmp($1, "DROP TABLE") == 0)
- $$ = E_DropTable;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH CONFIGURATION") == 0)
- $$ = E_DropTextSearchConfiguration;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH DICTIONARY") == 0)
- $$ = E_DropTextSearchDictionary;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH PARSER") == 0)
- $$ = E_DropTextSearchParser;
- else if (pg_strcasecmp($1, "DROP TEXT SEARCH TEMPLATE") == 0)
- $$ = E_DropTextSearchTemplate;
- else if (pg_strcasecmp($1, "DROP TRIGGER") == 0)
- $$ = E_DropTrigger;
- else if (pg_strcasecmp($1, "DROP TYPE") == 0)
- $$ = E_DropType;
- else if (pg_strcasecmp($1, "DROP USER MAPPING") == 0)
- $$ = E_DropUserMapping;
- else if (pg_strcasecmp($1, "DROP VIEW") == 0)
- $$ = E_DropView;
- else if (pg_strcasecmp($1, "LOAD") == 0)
- $$ = E_Load;
- else if (pg_strcasecmp($1, "REINDEX") == 0)
- $$ = E_Reindex;
- else if (pg_strcasecmp($1, "SELECT INTO") == 0)
- $$ = E_SelectInto;
- else if (pg_strcasecmp($1, "VACUUM") == 0)
- $$ = E_Vacuum;
- else
+ TrigEventCommand cmdtag = parse_event_tag($1, true);
+ if (cmdtag == E_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized command \"%s\"", $1),
- parser_errposition(@1)));
+ parser_errposition(@1)));
+ $$ = cmdtag;
}
;
+
DropEventTrigStmt:
DROP EVENT TRIGGER name opt_drop_behavior
{
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index e08f045..660d1cf 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -23,6 +23,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_event_trigger.h"
+#include "catalog/pg_event_trigger_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -195,7 +196,7 @@ BuildEventTriggerCache()
else
{
ArrayType *arr;
- int16 *tags;
+ Datum *tags;
int i;
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
@@ -204,15 +205,17 @@ BuildEventTriggerCache()
if (ARR_NDIM(arr) != 1 ||
numkeys < 0 ||
ARR_HASNULL(arr) ||
- ARR_ELEMTYPE(arr) != INT2OID)
- elog(ERROR, "evttags is not a 1-D smallint array");
+ ARR_ELEMTYPE(arr) != TEXTOID)
+ elog(ERROR, "evttags is not a 1-D text array");
- tags = (int16 *) ARR_DATA_PTR(arr);
+ deconstruct_array(arr, TEXTOID, -1, false, 'i',
+ &tags, NULL, &numkeys);
for (i = 0; i < numkeys; i++)
{
- command = tags[i];
- add_funcall_to_command_event(event, command, name, proc);
+ char *cmdstr = TextDatumGetCString(tags[i]);
+ command = parse_event_tag(cmdstr, false);
+ add_funcall_to_command_event(event, command, name, proc);
}
}
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 450e93a..561281c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5327,8 +5327,8 @@ getEvtTriggers(Archive *fout, int *numEvtTriggers)
"SELECT e.tableoid, e.oid, evtname, evtenabled, "
"pg_catalog.pg_evtevent_to_string(evtevent) as evtevent, "
"array_to_string(array("
- "select '''' || pg_catalog.pg_evttag_to_string(x) || '''' "
- "from unnest(evttags) as t(x)), ', ') as evttags, "
+ "select '''' || x || '''' "
+ " from unnest(evttags) as t(x)), ', ') as evttags, "
"n.nspname || '.' || p.proname as evtfname "
"FROM pg_event_trigger e JOIN pg_proc p on e.evtfoid = p.oid "
"JOIN pg_namespace n ON p.pronamespace = n.oid "
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b30af0d..a2a9a62 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2975,7 +2975,7 @@ listEvtTriggers(const char *pattern, bool verbose)
" when 'D' then 'disabled' end as \"%s\", "
"pg_catalog.pg_evtevent_to_string(evtevent) as \"%s\", "
"n.nspname || '.' || p.proname || '()' as \"%s\", "
- " array_to_string(array(select pg_evttag_to_string(x) "
+ " array_to_string(array(select x "
" from unnest(evttags) as t(x)), ', ') as \"%s\" "
"FROM pg_event_trigger e JOIN pg_proc p on e.evtfoid = p.oid "
"JOIN pg_namespace n ON p.pronamespace = n.oid ",
diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h
index 9af38a1..6e270e3 100644
--- a/src/include/catalog/pg_event_trigger.h
+++ b/src/include/catalog/pg_event_trigger.h
@@ -36,7 +36,7 @@ CATALOG(pg_event_trigger,3466)
char evtenabled; /* trigger's firing configuration WRT
* session_replication_role */
#ifdef CATALOG_VARLEN
- int16 evttags[1]; /* command TAGs this event trigger targets */
+ text evttags[1]; /* command TAGs this event trigger targets */
#endif
} FormData_pg_event_trigger;
diff --git a/src/include/catalog/pg_event_trigger_fn.h b/src/include/catalog/pg_event_trigger_fn.h
new file mode 100644
index 0000000..d1e7502
--- /dev/null
+++ b/src/include/catalog/pg_event_trigger_fn.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_event_trigger_fn.h
+ * prototypes for functions in catalog/pg_event_trigger.c
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_event_trigger_fn.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_EVENT_TRIGGER_FN_H
+#define PG_EVENT_TRIGGER_FN_H
+
+#include "catalog/pg_event_trigger.h"
+
+TrigEventCommand parse_event_tag(char *command, bool noerror);
+
+#endif /* PG_EVENT_TRIGGER_FN_H */
On Wed, Jul 4, 2012 at 1:56 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:
Robert Haas <robertmhaas@gmail.com> writes:
No, I'm not asking you to add any more columns right now (in fact,
please do not). But the type of the existing column should change to
text[].Ok, done in the attached. We now read text from either the user input in
the grammar or from the catalogs (in a 1-D array there), and convert to
our enum structure for internal code use.In the previous one I missed adapting pg_dump and psql, here's the
updated set of patches. Sorry about that. This unexpected electrical
shut down in the middle of the day was not cool. Nor was the second one.
Thanks. There are some review comments from previous rounds that
don't seem to have been fully incorporated to this version:
- You only changed the definition of pg_event_triggers.evttags, not
the documentation. I don't think we need pg_evttag_to_string aka
pg_event_trigger_command_to_string any more either.
- When you removed format_type_be_without_namespace, you didn't remove
either the function prototype or the related changes in format_type.c.
- I'm pretty sure that a previous review mentioned the compiler
warning in evtcache.c, which seems not to be fixed. It doesn't look
harmless, either.
- The definitions of EVTG_FIRED_* are still present in
pg_event_trigger.h, even though they're longer used anywhere.
- The comments in parsenodes.h still refer to command triggers rather
than event triggers. event_trigger.h contains a reference to
CommandTriggerData that should say EventTriggerData. plperl.sgml has
vestiges of the old terminology as well.
In terms of the schema itself, I think we are almost there, but:
- I noticed while playing around with this that pg_dump emits a
completely empty owner field when dumping an event trigger. At first
I thought that was just an oversight, but then I realized there's a
deeper reason for it: pg_event_trigger doesn't have an owner field. I
think it should. The only other objects in the system that don't have
owners are text search parsers and text search templates (and casts,
sort of). It might seem redundant to have an owner even when
event-triggers are superuser-only, but we might want to try to relax
that restriction later. Note that foreign data wrappers, which are
also superuser-create-only, do have an owner. (Note that if we give
event triggers an owner, then we also need ALTER .. OWNER TO support
for them.)
- It seems pretty clear at this point that the cache you've
implemented in src/backend/utils/cache/evtcache.c is going to do all
the heavy lifting of converting the stored catalog representation to a
form that is suitable for quick access. Given that, I wonder whether
we should go whole hog and change evtevent to a text field. This
would simplify things for pg_dump and we could get rid of
pg_evtevent_to_string, at a cost of doing marginally more work when we
rebuild the event cache (but who really cares, given that you're
reading the entire table every time you rebuild the cache anyway?).
Thoughts?
Some other minor comments:
- In the \dy output, command_start is referred to as the "condition",
but the documentation calls it an "event" and the grammar calls it
"event_name". "event" or "event_name" seems better, so I think this
is just a matter of changing psql to match.
- AlterEventTrigStmt defines tgenbled as char *; I think it should
just be char. In gram.y, you can declare the enable_trigger
production as <chr> (or <ival>?) rather than <str>. At any rate
pstrdup(stmt->tgenabled)[0] doesn't look right; you don't need to copy
a string to fetch the first byte.
- Why did you create a separate file pg_event_trigger_fn.h instead of
just including that single prototype in pg_event_trigger.h?
- The EVENTTRIGGEROID syscache is used only in RemoveEventTriggerById.
I don't think that's a sufficient justification for eating up more
memory for another system cache. I think you should just remove that
cache and recode RemoveEventTriggerById to find the correct tuple via
an index scan. See RemoveEventTriggerById for an example.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
Please find attached v6 of the patch, fixing all your comments here.
Sorry about missing some obvious things in the making of this patch.
Given that I had to merge master again, I can't easily produce an
incremental patch on top of last version, so you only have the full
patch attached.
Robert Haas <robertmhaas@gmail.com> writes:
- You only changed the definition of pg_event_triggers.evttags, not
the documentation. I don't think we need pg_evttag_to_string aka
pg_event_trigger_command_to_string any more either.
Done. Removed now useless exposed functions.
- When you removed format_type_be_without_namespace, you didn't remove
either the function prototype or the related changes in format_type.c.
Fixed.
- I'm pretty sure that a previous review mentioned the compiler
warning in evtcache.c, which seems not to be fixed. It doesn't look
harmless, either.
I'm failing to reproduce it here, in -O0. […Trying with the -O2
default…] I got the error in -O2, fixed in the attached.
- The definitions of EVTG_FIRED_* are still present in
pg_event_trigger.h, even though they're longer used anywhere.
Fixed.
- The comments in parsenodes.h still refer to command triggers rather
than event triggers. event_trigger.h contains a reference to
CommandTriggerData that should say EventTriggerData. plperl.sgml has
vestiges of the old terminology as well.
Fixed. I only found a single such vestige in plperl.sgml though.
In terms of the schema itself, I think we are almost there, but:
- I noticed while playing around with this that pg_dump emits a
completely empty owner field when dumping an event trigger. At first
I thought that was just an oversight, but then I realized there's a
deeper reason for it: pg_event_trigger doesn't have an owner field. I
think it should. The only other objects in the system that don't have
owners are text search parsers and text search templates (and casts,
sort of). It might seem redundant to have an owner even when
event-triggers are superuser-only, but we might want to try to relax
that restriction later. Note that foreign data wrappers, which are
also superuser-create-only, do have an owner. (Note that if we give
event triggers an owner, then we also need ALTER .. OWNER TO support
for them.)
Damn, I had it on my TODO and Álvaro hinted me already, and I kept
forgetting about it nonetheless. Fixed now.
- It seems pretty clear at this point that the cache you've
implemented in src/backend/utils/cache/evtcache.c is going to do all
the heavy lifting of converting the stored catalog representation to a
form that is suitable for quick access. Given that, I wonder whether
we should go whole hog and change evtevent to a text field. This
would simplify things for pg_dump and we could get rid of
pg_evtevent_to_string, at a cost of doing marginally more work when we
rebuild the event cache (but who really cares, given that you're
reading the entire table every time you rebuild the cache anyway?).
Agreed, done that way (using a NameData fixed width field).
- In the \dy output, command_start is referred to as the "condition",
but the documentation calls it an "event" and the grammar calls it
"event_name". "event" or "event_name" seems better, so I think this
is just a matter of changing psql to match.
Fixed.
- AlterEventTrigStmt defines tgenbled as char *; I think it should
just be char. In gram.y, you can declare the enable_trigger
production as <chr> (or <ival>?) rather than <str>. At any rate
pstrdup(stmt->tgenabled)[0] doesn't look right; you don't need to copy
a string to fetch the first byte.
D'oh. Sure. Done.
- Why did you create a separate file pg_event_trigger_fn.h instead of
just including that single prototype in pg_event_trigger.h?
To mimic some existing files, fixed.
- The EVENTTRIGGEROID syscache is used only in RemoveEventTriggerById.
I don't think that's a sufficient justification for eating up more
memory for another system cache. I think you should just remove that
cache and recode RemoveEventTriggerById to find the correct tuple via
an index scan. See RemoveEventTriggerById for an example.
It's now used in more places (dependencies, alter owner), so thinking
that it's pulling its weight now with 3 call sites I've left it alone.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
On Thu, Jul 5, 2012 at 5:31 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
[ new patch ]
Attached is a incremental patch with a bunch of minor cleanups,
including reverts of a few spurious white space changes. Could you
merge this into your version?
I have some concerns about pg_dump:
1. Can we spell out EvtTriggerInfo, getEvtTriggers, and dumpEvtTrigger
as EventTriggerInfo, getEventTriggers, and dumpEventTrigger?
2. I don't think that this code properly handles older server
versions. First, the version test in getEvtTriggers checks against
90200 instead of 90300. Second, when running against a too-old server
version, this is going to try to execute an empty query and then parse
the results. I think you want to restructure this code so that it
just plain old returns if the server is too old. See for example
getForeignDataWrappers.
3. The way you're generating evtfname is unsafe if either the schema
or the function name contains SQL characters. I think this should
probably be casting the function OID to regproc in lieu of rolling its
own formatting code. Also, the tags should probably be escaped using
quote_literal, just to be future-proof.
4. I think we should aim to generate all the SQL in upper case. Right
now "CREATE EVENT TRIGGER" and "EXECUTE PROCEDURE" are in upper case
but "when tag in" is in lower case.
In psql, I think we should similarly have listEventTriggers() rather
than listEvtTriggers(); here as in pg_dump I think you should cast the
evtfoid to regproc to get schema-qualification and escaping, in lieu
of the explicit join.
In terms of the schema itself, I think we are almost there, but:
- I noticed while playing around with this that pg_dump emits a
completely empty owner field when dumping an event trigger. At first
I thought that was just an oversight, but then I realized there's a
deeper reason for it: pg_event_trigger doesn't have an owner field. I
think it should. The only other objects in the system that don't have
owners are text search parsers and text search templates (and casts,
sort of). It might seem redundant to have an owner even when
event-triggers are superuser-only, but we might want to try to relax
that restriction later. Note that foreign data wrappers, which are
also superuser-create-only, do have an owner. (Note that if we give
event triggers an owner, then we also need ALTER .. OWNER TO support
for them.)Damn, I had it on my TODO and Álvaro hinted me already, and I kept
forgetting about it nonetheless. Fixed now.
evtowner seems to have missed the documentation bus.
With respect to the documentation, keep in mind that the synopsis is
going to show up in the command line help for \h. I'm thinking that
the full list of command tags is too long for that, and that we should
instead rearrange the page so that the list of supported commands is
outside the synopsis. The synposis is also somewhat misleading, I
think, in that "variable in (tag, ...)" conveys the idea that no
matter what the variable is, the items in parentheses will surely be
tags. I suggest that we say something like "filter_variable in
(filter_value, ...)" and then document in the text that
filter_variable must currently always be TAG, and that the legal
values for filter_value are dependent on the choice of
filter_variable, and the legal choices for TAG are those listed in the
following table: <splat>.
The documentation contains the following claim with which I'm
extremely uncomfortable:
+ <para>
+ Triggers on <literal>ANY</literal> command support more commands than
+ just this list, and will only provide the <literal>command
+ tag</literal> argument as <literal>NOT NULL</literal>. Supporting more
+ commands is made so that you can actually block <xref linkend="ddl">
+ commands in one go.
+ </para>
A minor issue is that there's no notion of ANY any more; it's just a
consequence of leaving out the WHEN clause. The bigger issue is that
I can't see any reason to do it that way. Surely if we're firing the
trigger at all, we can arrange to have the command tag properly filled
in so that we can filter by it.
This might be a crazy idea, but... it seems like it would be awfully
sweet if we could find a way to avoid having to translate between node
tags (i.e. constants beginning with T_* that are used to identify the
type of statement that is executing) and event tags (i.e. constants
beginning with E_* that are used to identify the type of statement
that is executing). Now obviously this is not quite possible because
in some cases the choice of E_* constant depends on not only the node
tag but also the type of object being operated on. However, it seems
like this is a surmountable obstacle: since we no longer need to store
the E_* constants in a system catalog, they don't really need to be
integers. For example, we could define something like this:
typedef struct
{
NodeTag nodetag;
ObjectType objecttype;
} NodeTagWithObjectType;
...and set the objecttype to a new OBJECT_DUMMY value or somesuch when
the NodeTag is such that the ObjectType isn't relevant. If we did
that, then all the E_* constants could go away, and InitEventContext()
would become a lot simpler and, presumably, faster. It would just
need to check whether it's got one of the node tags that needs the
object-type filled in. If so, it does that; if not, it just sets the
nodetag field to the statement's node-tag and the objecttype to
OBJECT_DUMMY, and it's done. Well, OK, it's probably not quite that
simple... but it still seems like we'd need explicit handling of a
smaller number of cases than presently.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
event-trigger-cleanups.patchapplication/octet-stream; name=event-trigger-cleanups.patchDownload
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index b783e86..9ec7a6e 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -634,7 +634,7 @@ SELECT init_hosts_query();
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();
- query_hosts
+ query_hosts
-----------------
(1,192.168.1.1)
(2,192.168.1.2)
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index 07f017a..e2a8f41 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -441,7 +441,7 @@ return (1, 2, 3, 4, 5)
$$ LANGUAGE plpythonu;
SELECT return_arr();
- return_arr
+ return_arr
-------------
{1,2,3,4,5}
(1 row)
diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index e236921..d4af986 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>ALTER EVENT TRIGGER</refname>
- <refpurpose>change the definition of a trigger</refpurpose>
+ <refpurpose>change the definition of an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-altereventtrigger">
@@ -46,7 +46,7 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
</para>
<para>
- You must be superuser to alter a event trigger.
+ You must be superuser to alter an event trigger.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index af06c88..fdf7a6d 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>CREATE EVENT TRIGGER</refname>
- <refpurpose>define a new trigger</refpurpose>
+ <refpurpose>define a new event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-createeventtrigger">
@@ -128,13 +128,13 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
The trigger will be associated with the specified event and will
execute the specified
function <replaceable class="parameter">function_name</replaceable> when
- that event is run.
+ that event occurs.
</para>
<para>
- The command trigger gives a procedure to fire before the event is
+ The event trigger gives a procedure to fire before the event is
executed. In some cases the procedure can be fired instead of the event
- code PostgreSQL would run itself. A command trigger's function must
+ code PostgreSQL would run itself. A event trigger's function must
return <literal>event_trigger</literal> data type. It can then only
abort the execution of the command by raising an exception.
</para>
@@ -186,7 +186,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
</para>
<para>
The command <literal>ALTER TYPE ... ADD VALUE ...</literal> prevents
- transaction control entirely, thus no command trigger will get fired
+ transaction control entirely, thus no event trigger will get fired
when it's used.
</para>
<para>
diff --git a/doc/src/sgml/ref/drop_event_trigger.sgml b/doc/src/sgml/ref/drop_event_trigger.sgml
index fc45dff..6b30dd9 100644
--- a/doc/src/sgml/ref/drop_event_trigger.sgml
+++ b/doc/src/sgml/ref/drop_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>DROP EVENT TRIGGER</refname>
- <refpurpose>remove a event trigger</refpurpose>
+ <refpurpose>remove an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-dropeventtrigger">
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4ea2848..561ce42 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -49,7 +49,7 @@ static void AlterEventTriggerOwner_internal(Relation rel,
Oid newOwnerId);
/*
- * Check permission: command triggers are only available for superusers. Raise
+ * Check permission: event triggers are only available for superusers. Raise
* an exception when requirements are not fullfilled.
*
* It's not clear how to accept that database owners be able to create command
@@ -62,7 +62,7 @@ CheckEventTriggerPrivileges()
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to use command triggers"))));
+ (errmsg("must be superuser to use event triggers"))));
}
/*
@@ -214,7 +214,7 @@ RemoveEventTriggerById(Oid trigOid)
}
/*
- * ALTER EVENT TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
+ * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
*/
void
AlterEventTrigger(AlterEventTrigStmt *stmt)
@@ -248,7 +248,7 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
/*
- * Rename command trigger
+ * Rename event trigger
*/
void
RenameEventTrigger(const char *trigname, const char *newname)
@@ -382,9 +382,9 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
}
/*
- * Functions to execute the command triggers.
+ * Functions to execute the event triggers.
*
- * We call the functions that matches the command triggers definitions in
+ * We call the functions that matches the event triggers definitions in
* alphabetical order, and give them those arguments:
*
* toplevel command tag, text
@@ -394,7 +394,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* objectname, text
*
* Those are passed down as special "context" magic variables and need specific
- * support in each PL that wants to support command triggers. All core PL do.
+ * support in each PL that wants to support event triggers. All core PL do.
*/
static void
@@ -409,7 +409,7 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
fmgr_info(proc, &flinfo);
/*
- * Prepare the command trigger function context from the Command Context.
+ * Prepare the event trigger function context from the Command Context.
* We prepare a dedicated Node here so as not to publish internal data.
*/
trigdata.type = T_EventTriggerData;
@@ -440,11 +440,11 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
void
InitEventContext(EventContext evt, const Node *parsetree)
{
- evt->command = E_UNKNOWN;
- evt->toplevel = NULL;
- evt->tag = (char *) CreateCommandTag((Node *)parsetree);
- evt->parsetree = (Node *)parsetree;
- evt->objectId = InvalidOid;
+ evt->command = E_UNKNOWN;
+ evt->toplevel = NULL;
+ evt->tag = (char *) CreateCommandTag((Node *)parsetree);
+ evt->parsetree = (Node *)parsetree;
+ evt->objectId = InvalidOid;
evt->objectname = NULL;
evt->schemaname = NULL;
@@ -1010,8 +1010,9 @@ InitEventContext(EventContext evt, const Node *parsetree)
break;
default:
- /* reaching that part of the code only means that we are not
- * supporting command triggers for the given command, which still
+ /*
+ * reaching that part of the code only means that we are not
+ * supporting event triggers for the given command, which still
* needs to execute.
*/
break;
@@ -1037,7 +1038,7 @@ CommandFiresTriggersForEvent(EventContext ev_ctx, TrigEvent tev)
}
/*
- * Actually run command triggers of a specific command. We first run ANY
+ * Actually run event triggers for a specific command. We first run ANY
* command triggers.
*/
void
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 29eec11..c8c1a81 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
@@ -356,11 +357,8 @@ standard_ProcessUtility(Node *parsetree,
/* Event Trigger support for command_start */
InitEventContext(&evt, (Node *)parsetree);
-
if (CommandFiresTriggersForEvent(&evt, E_CommandStart))
- {
ExecEventTriggers(&evt, E_CommandStart);
- }
switch (nodeTag(parsetree))
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 17b62b6..ec93149 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -39,11 +39,9 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
-#include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
-#include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
@@ -261,6 +259,7 @@ static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
+
/* ----------
* get_ruledef - Do it all and return a text
* that could be used as a statement
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 65bc6be..cf9f3e5 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -116,7 +116,7 @@ add_funcall_to_command_event(TrigEvent event,
* foreach(cell, EventCommandTriggerCache[TrigEventCommand][TrigEvent])
*/
static void
-BuildEventTriggerCache()
+BuildEventTriggerCache(void)
{
HASHCTL info;
Relation rel, irel;
@@ -144,8 +144,9 @@ BuildEventTriggerCache()
indexScan = index_beginscan(rel, irel, SnapshotNow, 0, 0);
index_rescan(indexScan, NULL, 0, NULL, 0);
- /* we use a full indexscan to guarantee that we see event triggers ordered
- * by name, this way we only even have to append the trigger's function Oid
+ /*
+ * We use a full indexscan to guarantee that we see event triggers ordered
+ * by name. This way, we only even have to append the trigger's function Oid
* to the target cache Oid list.
*/
while (HeapTupleIsValid(tuple = index_getnext(indexScan, ForwardScanDirection)))
@@ -327,7 +328,8 @@ get_event_triggers(TrigEvent event, TrigEventCommand command)
lc_any_procs = lnext(lc_any_procs);
}
- /* now append as many elements from CMD list named before next ANY
+ /*
+ * now append as many elements from CMD list named before next ANY
* entry
*/
do
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 993053c..09e104f 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -242,7 +242,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
getTriggers(fout, tblinfo, numTables);
if (g_verbose)
- write_msg(NULL, "reading command triggers\n");
+ write_msg(NULL, "reading event triggers\n");
getEvtTriggers(fout, &numEvtTriggers);
*numTablesPtr = numTables;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75b7ff0..ef859dc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5300,7 +5300,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
/*
* getEvtTriggers
- * get information about every command trigger on a dumpable table
+ * get information about event triggers
*/
EvtTriggerInfo *
getEvtTriggers(Archive *fout, int *numEvtTriggers)
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5807265..0843b65 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -229,7 +229,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
- fprintf(output, _(" \\dy [PATTERN] list command triggers\n"));
+ fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 340de88..bee7154 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4645,6 +4645,7 @@ DESCR("SP-GiST support for suffix tree over text");
DATA(insert OID = 4031 ( spg_text_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_text_leaf_consistent _null_ _null_ _null_ ));
DESCR("SP-GiST support for suffix tree over text");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 71563ad..1063403 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1013,7 +1013,6 @@ extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS);
/* format_type.c */
extern Datum format_type(PG_FUNCTION_ARGS);
extern char *format_type_be(Oid type_oid);
-extern char *format_type_be_without_namespace(Oid type_oid);
extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
extern int32 type_maximum_size(Oid type_oid, int32 typemod);
diff --git a/src/pl/plperl/expected/plperl_trigger.out b/src/pl/plperl/expected/plperl_trigger.out
index b61d47c..bfb59db 100644
--- a/src/pl/plperl/expected/plperl_trigger.out
+++ b/src/pl/plperl/expected/plperl_trigger.out
@@ -309,7 +309,7 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
ERROR: trigger functions can only be called as triggers
CONTEXT: compilation of PL/Perl function "direct_trigger"
--- test plperl command triggers
+-- test plperl event triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: "
. $_TD->{when} . " "
diff --git a/src/pl/plperl/sql/plperl_trigger.sql b/src/pl/plperl/sql/plperl_trigger.sql
index 9cc2408..1c0d37b 100644
--- a/src/pl/plperl/sql/plperl_trigger.sql
+++ b/src/pl/plperl/sql/plperl_trigger.sql
@@ -170,7 +170,7 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
--- test plperl command triggers
+-- test plperl event triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: "
. $_TD->{when} . " "
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index f756462..197431b 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -610,7 +610,7 @@ SELECT * FROM composite_trigger_nested_test;
("(,t)","(1,f)",)
(3 rows)
--- test plpython command triggers
+-- test plpython event triggers
create or replace function pysnitch() returns event_trigger language plpythonu as $$
plpy.notice(" pysnitch: %s %s %s.%s" %
(TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 7ec6e0b..4df599a 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -388,7 +388,7 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
SELECT * FROM composite_trigger_nested_test;
--- test plpython command triggers
+-- test plpython event triggers
create or replace function pysnitch() returns event_trigger language plpythonu as $$
plpy.notice(" pysnitch: %s %s %s.%s" %
(TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out
index 9b9c157..f4dcbe7 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -519,7 +519,7 @@ select tcl_date_week(2001,10,24);
42
(1 row)
--- test pltcl command triggers
+-- test pltcl event triggers
create or replace function tclsnitch() returns event_trigger language pltcl as $$
elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
$$;
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 864e23b..c8436f0 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -560,7 +560,7 @@ $$ language pltcl immutable;
select tcl_date_week(2010,1,24);
select tcl_date_week(2001,10,24);
--- test pltcl command triggers
+-- test pltcl event triggers
create or replace function tclsnitch() returns event_trigger language pltcl as $$
elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
$$;
Robert Haas <robertmhaas@gmail.com> writes:
Attached is a incremental patch with a bunch of minor cleanups,
including reverts of a few spurious white space changes. Could you
merge this into your version?
Thank you very much for that, yes it's included now. So you have 3
attachments here, the whole new patch revision (v1.7), the incremental
patch to go from 1.6 to 1.7 and the incremental patch that should apply
cleanly on top of your cleanups.
1. Can we spell out EvtTriggerInfo, getEvtTriggers, and dumpEvtTrigger
as EventTriggerInfo, getEventTriggers, and dumpEventTrigger?
Done.
2. I don't think that this code properly handles older server
versions.
Fixed.
3. The way you're generating evtfname is unsafe if either the schema
Fixed using regproc cast, both in pg_dump and in psql.
4. I think we should aim to generate all the SQL in upper case. Right
now "CREATE EVENT TRIGGER" and "EXECUTE PROCEDURE" are in upper case
but "when tag in" is in lower case.
Oh, sure, done.
In psql, I think we should similarly have listEventTriggers() rather
than listEvtTriggers(); here as in pg_dump I think you should cast the
evtfoid to regproc to get schema-qualification and escaping, in lieu
of the explicit join.
Done.
evtowner seems to have missed the documentation bus.
I'm told it finally did catch it.
With respect to the documentation, keep in mind that the synopsis is
going to show up in the command line help for \h. I'm thinking that
the full list of command tags is too long for that, and that we should
instead rearrange the page so that the list of supported commands is
outside the synopsis. The synposis is also somewhat misleading, I
think, in that "variable in (tag, ...)" conveys the idea that no
matter what the variable is, the items in parentheses will surely be
tags. I suggest that we say something like "filter_variable in
(filter_value, ...)" and then document in the text that
filter_variable must currently always be TAG, and that the legal
values for filter_value are dependent on the choice of
filter_variable, and the legal choices for TAG are those listed in the
following table: <splat>.
I tried to arrange something here, I'm not quite sure about the current
result but it's already much better than the previous version.
Articulating ideas that way also allows to begin that command / events
support matrix, as you can see in the attached.
The documentation contains the following claim with which I'm
extremely uncomfortable:
[…ANY command set…]
It's a vestige from a long time ago, I removed that and some other
verbiage now.
I can't see any reason to do it that way. Surely if we're firing the
trigger at all, we can arrange to have the command tag properly filled
in so that we can filter by it.
Indeed, command tag is always available. The magic trigger variables
might not all be, though, that was what this text was alluding to, but
that case is already covered in the specific PL docs.
The part that we will certainly have to re-install later is when we add
new event "integration points" that we can only implement in some of the
supported commands, not all of them. Think about command_end and CLUSTER
or other commands managing the transaction themselves (concurrently).
But that's not at all relevant to what's in this reduced v1 patch.
This might be a crazy idea, but... it seems like it would be awfully
sweet if we could find a way to avoid having to translate between node
tags (i.e. constants beginning with T_* that are used to identify the
type of statement that is executing) and event tags (i.e. constants
beginning with E_* that are used to identify the type of statement
that is executing). Now obviously this is not quite possible because
in some cases the choice of E_* constant depends on not only the node
tag but also the type of object being operated on. However, it seems
like this is a surmountable obstacle: since we no longer need to store
the E_* constants in a system catalog, they don't really need to be
integers. For example, we could define something like this:
All of that happens in InitEventContext(). We can see in there that we
wouldn't gain much, the great majority of this code is dealing with
cases where we have no symmetry at all. Also I don't think that the
nodeTag macro is expensive, nor is a 2-levels nested switch statement.
So while I would enjoy seeing that part simplified, I don't think your
angle of attack would provide us much progress here…
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Fri, Jul 6, 2012 at 7:21 AM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Attached is a incremental patch with a bunch of minor cleanups,
including reverts of a few spurious white space changes. Could you
merge this into your version?Thank you very much for that, yes it's included now. So you have 3
attachments here, the whole new patch revision (v1.7), the incremental
patch to go from 1.6 to 1.7 and the incremental patch that should apply
cleanly on top of your cleanups.
Here is an incremental documentation patch which I hope you will like.
I made the event triggers stuff its own chapter rather than trying to
fold it in under triggers, and added some more detail. It's already
quite a bit of extra stuff, and it's only going to become more as we
expand this feature, so I think a separate chapter is appropriate. I
moved a bunch of the details that were under CREATE EVENT TRIGGER into
this new chapter, which I think is a better location, and reformatted
the matrix somewhat. I think as we add more firing points it will be
clearer and easier to read if we have all the commands arranged in
columns rather than listing a bunch of firing points on each line. I
also made a bunch of minor edits to improve readability and improve
the English (which wasn't bad, but I touched it up a bit); and I tried
to add some extra detail here and there (some of it recycled from
previous patch versions). Assuming this all seems reasonably
agreeable, can you merge it on your side?
This took the last several hours, so I haven't looked at your latest
code changes yet. However, in the course of editing the
documentation, it occurred to me that we seem to be fairly arbitrarily
excluding a large number of commands from the event trigger mechanism.
For example, GRANT and REVOKE. In earlier patches, we needed
specific changes for every command, so there was some reason not to
try to support everything right out of the gate. But ISTM that the
argument for this is much less now; presumably it's just a few extra
lines of code per command, so maybe we ought to go ahead and try to
make this as complete as possible. I attempt to explain in the
attached patch the reasons why we don't support certain classes of
commands, but I can't come up with any explanation for supporting
GRANT and REVOKE that doesn't fall flat. I can't even really see a
reason not to support things like LISTEN and NOTIFY, and it would
certainly be more consistent with the notion of a command_start
trigger to support as many commands as we can.
I had an interesting experience while testing this patch. I
accidentally redefined my event trigger function to something which
errored out. That of course precluded me from using CREATE OR REPLACE
FUNCTION to fix it. This makes me feel rather glad that we decided to
exclude CREATE/ALTER/DROP EVENT TRIGGER from the event trigger
mechanism, else recovery would have had to involve system catalog
hackery.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
event-trigger-docs-incremental.patchapplication/octet-stream; name=event-trigger-docs-incremental.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dae8028..b100a42 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -99,11 +99,6 @@
</row>
<row>
- <entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
- <entry>event triggers</entry>
- </row>
-
- <row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
</row>
@@ -149,6 +144,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
+ <entry>event triggers</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
<entry>installed extensions</entry>
</row>
@@ -1871,6 +1871,7 @@
<para>
The catalog <structname>pg_event_trigger</structname> stores event triggers.
+ See <xref linkend="event-triggers"> for more information.
</para>
<table>
@@ -1891,30 +1892,28 @@
<entry><structfield>evtname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
- <entry>Trigger name (unique)</entry>
+ <entry>Trigger name (must be unique)</entry>
</row>
<row>
<entry><structfield>evtevent</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
- <entry>The event this trigger fires for.</entry>
+ <entry>Identifies the event for which this trigger fires</entry>
</row>
<row>
<entry><structfield>evtowner</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
- <entry>Owner of the event</entry>
+ <entry>Owner of the event trigger</entry>
</row>
<row>
<entry><structfield>evtfoid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>
- The OID of the function called by this event trigger.
- </entry>
+ <entry>The function to be called</entry>
</row>
<row>
@@ -1935,7 +1934,10 @@
<entry><structfield>evttags</structfield></entry>
<entry><type>text[]</type></entry>
<entry></entry>
- <entry>Command tags of the commands this trigger is restricted to.</entry>
+ <entry>
+ Command tags for which this trigger will fire. If NULL, the firing
+ of this trigger is not restricted on the basis of the command tag.
+ </entry>
</row>
</tbody>
</tgroup>
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
new file mode 100644
index 0000000..4ff93d0
--- /dev/null
+++ b/doc/src/sgml/event-trigger.sgml
@@ -0,0 +1,466 @@
+<!-- doc/src/sgml/event-trigger.sgml -->
+
+ <chapter id="event-triggers">
+ <title>Event Triggers</title>
+
+ <indexterm zone="event-triggers">
+ <primary>event trigger</primary>
+ </indexterm>
+
+ <para>
+ To supplement the trigger mechanism discussed in <xref linkend="triggers">,
+ <productname>PostgreSQL</> also provides event triggers. Unlike regular
+ triggers, which are attached to a single table and capture only DML events,
+ event triggers are global to a particular database and are capable of
+ capturing DDL events.
+ </para>
+
+ <para>
+ Like regular triggers, event triggers can be written in any procedural
+ language that includes event trigger support, or in C, but not in plain
+ SQL.
+ </para>
+
+ <sect1 id="event-trigger-definition">
+ <title>Overview of Event Trigger Behavior</title>
+
+ <para>
+ An event trigger fires whenever the event with which it is
+ associated occurs in the database in which it is defined. Currently,
+ the only supported event is <literal>command_start</>. Support for
+ additional events may be added in future releases.
+ </para>
+
+ <para>
+ The <literal>command_start</> event occurs just before the execution of
+ many SQL commands, most of which can be broadly described as
+ <link linkend="ddl">DDL</link>. There are a number of commands that,
+ for various reasons, are specifically excluded from the event trigger
+ system:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands that
+ access or modify data within a particular table, such
+ <xref linkend="sql-select">, <xref linkend="sql-insert">,
+ <xref linkend="sql-update">, <xref linkend="sql-delete">, and
+ <xref linkend="sql-truncate">. This also includes commands related
+ to prepared plans, such as <xref linkend="sql-prepare">,
+ <xref linkend="sql-deallocate">, <xref linkend="sql-declare">,
+ and <xref linkend="sql-fetch">. Ordinary triggers or rules should
+ be used in these cases.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands that
+ create, alter, or drop global objects. Unlike the event trigger, these
+ objects are not part of the current database; they are accessible from
+ all databases in the cluster, and are therefore unaffected by event
+ triggers. Such objects include databases, tablespaces, and roles.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands where
+ firing a trigger might result either result in system instability or
+ interfere with the administrator's ability to regain control of the
+ database. These include transaction control commands, such as
+ <xref linkend="sql-begin"> or <xref linkend="sql-commit">; configuration
+ commands, such as <xref linkend="sql-set"> or <xref linkend="sql-show">;
+ and commands related to the event trigger mechanism itself, such as
+ <xref linkend="sql-createeventtrigger">,
+ <xref linkend="sql-altereventtrigger">, and
+ <xref linkend="sql-dropeventtrigger">.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ For a complete list of commands supported by the event trigger mechanism,
+ see <xref linkend="event-trigger-matrix">.
+ </para>
+
+ <para>
+ In order to create an event trigger, you must first create a function with
+ the special return type <literal>event_trigger</literal>. This function
+ need not (and may not) return a value; the return type serves merely as
+ a signal that the function is to be invoked as an event trigger.
+ </para>
+
+ <para>
+ If more than one event trigger is defined for a particular event, they will
+ fire in alphabetical order by trigger name.
+ </para>
+
+ <para>
+ A trigger definition can also specify a <literal>WHEN</literal>
+ condition so that, for example, a <literal>command_start</literal>
+ tag can be fired only for particular commands which the user wishes
+ to intercept. A common use of such triggers is to restrict the range of
+ DDL operations which users may perform.
+ </para>
+ </sect1>
+
+ <sect1 id="event-trigger-matrix">
+ <title>Event Trigger Firing Matrix</title>
+
+ <para>
+ <xref linkend="event-trigger-by-command-tag"> lists all commands
+ for which event triggers are supported.
+ </para>
+
+ <table id="event-trigger-by-command-tag">
+ <title>Event Trigger Support by Command Tag</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>command tag</entry>
+ <entry><literal>command_start</literal></entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry align="left"><literal>ALTER AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CLUSTER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE CAST</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE INDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE RULE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TABLE AS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP CAST</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP INDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP RULE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>LOAD</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>REINDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>SELECT INTO</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>VACUUM</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 82b9e39..db4cc3a 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -61,6 +61,7 @@
<!ENTITY rules SYSTEM "rules.sgml">
<!ENTITY spi SYSTEM "spi.sgml">
<!ENTITY trigger SYSTEM "trigger.sgml">
+<!ENTITY event-trigger SYSTEM "event-trigger.sgml">
<!ENTITY xaggr SYSTEM "xaggr.sgml">
<!ENTITY xfunc SYSTEM "xfunc.sgml">
<!ENTITY xindex SYSTEM "xindex.sgml">
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index 9ec7a6e..e490086 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -1220,7 +1220,7 @@ CREATE TRIGGER test_valid_id_trig
</sect2>
<sect2 id="plperl-event-trigger">
- <title>Trigger Procedures on Events in PL/Perl </title>
+ <title>Trigger Procedures on Events in PL/Perl</title>
<indexterm>
<primary>event trigger</primary>
@@ -1229,9 +1229,9 @@ CREATE TRIGGER test_valid_id_trig
<para>
Event trigger procedures can be written in PL/Perl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -1272,7 +1272,7 @@ CREATE TRIGGER test_valid_id_trig
<term><varname>$_TD->{objectname}</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
@@ -1289,7 +1289,7 @@ CREATE TRIGGER test_valid_id_trig
</varlistentry>
</variablelist>
</para>
- </sect2>
+ </sect2>
</sect1>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index fadd5d7..a21aaf2 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -3378,7 +3378,7 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
</indexterm>
<sect2 id="plpgsql-dml-trigger">
- <title>Triggers on data change</title>
+ <title>Triggers on data changes</title>
<para>
<application>PL/pgSQL</application> can be used to define trigger
@@ -3930,19 +3930,19 @@ SELECT * FROM sales_summary_bytime;
</sect2>
<sect2 id="plpgsql-event-trigger">
- <title>Triggers on event</title>
+ <title>Triggers on events</title>
<para>
- <application>PL/pgSQL</application> can be used to define command
- trigger procedures. An event trigger procedure is created with the
- <command>CREATE FUNCTION</> command, declaring it as a function with
- no arguments and a return type of <type>event trigger</type>.
+ <application>PL/pgSQL</application> can be used to define event
+ triggers. <productname>PostgreSQL</> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
- <para>
- When a <application>PL/pgSQL</application> function is called as a
- event trigger, several special variables are created automatically
- in the top-level block. They are:
+ <para>
+ When a <application>PL/pgSQL</application> function is called as a
+ event trigger, several special variables are created automatically
+ in the top-level block. They are:
<variablelist>
<varlistentry>
@@ -3999,10 +3999,6 @@ SELECT * FROM sales_summary_bytime;
</variablelist>
</para>
- <para>
- The event trigger function's return value is not used.
- </para>
-
<para>
<xref linkend="plpgsql-event-trigger-example"> shows an example of a
event trigger procedure in <application>PL/pgSQL</application>.
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index e2a8f41..99ce8f9 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -872,7 +872,7 @@ $$ LANGUAGE plpythonu;
</sect2>
<sect2 id="plpython-event-trigger">
- <title>Trigger Procedures on Events in PL/Python </title>
+ <title>Trigger Procedures on Events in PL/Python</title>
<indexterm>
<primary>event trigger</primary>
@@ -881,9 +881,9 @@ $$ LANGUAGE plpythonu;
<para>
Event trigger procedures can be written in PL/Python.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -924,7 +924,7 @@ $$ LANGUAGE plpythonu;
<term><varname>TD["objectname"]</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index c9a4e4c..c932ddb 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -725,9 +725,9 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<para>
Event trigger procedures can be written in PL/Tcl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -768,7 +768,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<term><varname>$TG_objectname</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e80265..4ef1fc1 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -208,6 +208,7 @@
&extend;
&trigger;
+ &event-trigger;
&rules;
&xplang;
diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index d4af986..db4727b 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -93,12 +93,12 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
</variablelist>
</refsect1>
- <refsect1>
+ <refsect1 id="sql-alterventtrigger-compatibility">
<title>Compatibility</title>
<para>
- <command>ALTER EVENT TRIGGER</command> is a <productname>PostgreSQL</>
- extension of the SQL standard.
+ There is no <command>ALTER EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 0e3b0f8..3b3d474 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -24,7 +24,7 @@ PostgreSQL documentation
CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
ON <replaceable class="PARAMETER">event</replaceable>
[ WHEN <replaceable class="PARAMETER">filter_variable</replaceable> IN (filter_value [ , ... ] ) ]
- EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable> ()
+ EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable>()
</synopsis>
</refsynopsisdiv>
@@ -33,35 +33,11 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
<command>CREATE EVENT TRIGGER</command> creates a new event trigger.
- The trigger will be associated with the specified event and will
- execute the specified
- function <replaceable class="parameter">function_name</replaceable> when
- that event occurs.
- </para>
-
- <para>
- The event trigger gives a procedure to fire before the event is
- executed. In some cases the procedure can be fired instead of the event
- code PostgreSQL would run itself. A event trigger's function must
- return <literal>event_trigger</literal> data type. It can then only
- abort the execution of the command by raising an exception.
- </para>
-
- <para>
- If multiple triggers of the same kind are defined for the same event,
- they will be fired in alphabetical order by name.
- </para>
-
- <para>
- Note that objects dropped by the effect of <literal>DROP
- CASCADE</literal> will not result in a event trigger firing, only the
- top-level event for the main object will fire a event trigger. That
- also applies to other dependencies following, as in <literal>DROP OWNED
- BY</literal>.
- </para>
-
- <para>
- Refer to <xref linkend="triggers"> for more information about triggers.
+ Whenever the designated event occurs and the <literal>WHEN</> condition
+ associated with the trigger, if any, is satisfied, the trigger function
+ will be executed. For a general introduction to event triggers, see
+ <xref linkend="event-triggers">. The user who creates an event trigger
+ becomes its owner.
</para>
</refsect1>
@@ -73,9 +49,8 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="parameter">name</replaceable></term>
<listitem>
<para>
- The name to give the new trigger. This must be distinct from the name
- of any other trigger for the same table. The name cannot be
- schema-qualified.
+ The name to give the new trigger. This name must be unique within
+ the database.
</para>
</listitem>
</varlistentry>
@@ -85,8 +60,8 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<listitem>
<para>
The name of the event that triggers a call to the given function.
- Currently only <literal>command_start</literal> is supported, that
- event is run before the specific command code takes control.
+ See <xref linkend="event-trigger-definition"> for more information
+ on event names.
</para>
</listitem>
</varlistentry>
@@ -95,12 +70,11 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="parameter">filter_variable</replaceable></term>
<listitem>
<para>
- The name of a variable used to filter events, allowing to control
- which cases must be handled by the trigger function. Currently the
- only
- provided <replaceable class="parameter">filter_variable</replaceable>
- is named <literal>TAG</literal> and contains the command tag for which
- the trigger is about to fire.
+ The name of a variable used to filter events. This makes it possible
+ to restrict the firing of the trigger to a subset of the cases in which
+ it is supported. Currently the only suppoted
+ <replaceable class="parameter">filter_variable</replaceable>
+ is <literal>TAG</literal>.
</para>
</listitem>
</varlistentry>
@@ -109,364 +83,10 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="parameter">filter_value</replaceable></term>
<listitem>
<para>
- A list of values that the
+ A list of values for the
associated <replaceable class="parameter">filter_variable</replaceable>
- can have to allow for the trigger to fire.
- </para>
- <para>
- The <literal>TAG</literal> variable accepts the following values:
- </para>
- <informaltable id="supported-command-tags">
- <tgroup cols="2">
- <thead>
- <row>
- <entry>command tag</entry>
- <entry>supported events, in order</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry align="left"><literal>ALTER AGGREGATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER COLLATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER CONVERSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER DOMAIN</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER EXTENSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER FUNCTION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER LANGUAGE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER OPERATOR</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER SCHEMA</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER SEQUENCE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER SERVER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TRIGGER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER TYPE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER USER MAPPING</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>ALTER VIEW</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CLUSTER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE AGGREGATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE CAST</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE COLLATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE CONVERSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE DOMAIN</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE EXTENSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE FUNCTION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE INDEX</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE LANGUAGE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE OPERATOR</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE RULE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE SCHEMA</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE SEQUENCE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE SERVER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TABLE AS</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TRIGGER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE TYPE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE USER MAPPING</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>CREATE VIEW</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP AGGREGATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP CAST</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP COLLATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP CONVERSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP DOMAIN</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP EXTENSION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP FUNCTION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP INDEX</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP LANGUAGE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP OPERATOR</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP RULE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP SCHEMA</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP SEQUENCE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP SERVER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TABLE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TRIGGER</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP TYPE</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP USER MAPPING</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>DROP VIEW</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>LOAD</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>REINDEX</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>SELECT INTO</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- <row>
- <entry align="left"><literal>VACUUM</literal></entry>
- <entry align="left"><literal>command_start</literal></entry>
- </row>
- </tbody>
- </tgroup>
- </informaltable>
- <para>
- That list is also the list of supported commands on which you can
- attach an event trigger.
+ for which the trigger should fire. For <literal>TAG</>, this means a
+ list of command tags (e.g. <literal>'DROP FUNCTION'</>).
</para>
</listitem>
</varlistentry>
@@ -476,7 +96,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<listitem>
<para>
A user-supplied function that is declared as taking no argument and
- returning type <literal>event trigger</literal>.
+ returning type <literal>event_trigger</literal>.
</para>
<para>
If your event trigger is implemented in <literal>C</literal> then it
@@ -490,24 +110,20 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
</variablelist>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-notes">
+ <refsect1 id="sql-createeventtrigger-notes">
<title>Notes</title>
<para>
To create a trigger on a event, the user must be superuser.
</para>
-
- <para>
- Use <xref linkend="sql-dropeventtrigger"> to remove a event trigger.
- </para>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-examples">
+ <refsect1 id="sql-createeventtrigger-examples">
<title>Examples</title>
<para>
- Forbids the execution of any command supported by the event trigger
- mechanism, which includes all commands listed above:
+ Forbid the execution of any command supported by the event trigger
+ mechanism:
<programlisting>
CREATE OR REPLACE FUNCTION abort_any_command()
@@ -547,13 +163,12 @@ $$;
</para>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-compatibility">
+ <refsect1 id="sql-createeventtrigger-compatibility">
<title>Compatibility</title>
<para>
- <command>CREATE EVENT TRIGGER</command> is a
- <productname>PostgreSQL</productname> extension of the <acronym>SQL</>
- standard.
+ There is no <command>CREATE EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/drop_event_trigger.sgml b/doc/src/sgml/ref/drop_event_trigger.sgml
index 6b30dd9..86f9628 100644
--- a/doc/src/sgml/ref/drop_event_trigger.sgml
+++ b/doc/src/sgml/ref/drop_event_trigger.sgml
@@ -29,8 +29,9 @@ DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceabl
<title>Description</title>
<para>
- <command>DROP EVENT TRIGGER</command> removes an existing trigger definition.
- To execute this command, the current user must be superuser.
+ <command>DROP EVENT TRIGGER</command> removes an existing event trigger.
+ To execute this command, the current user must be the owner of the event
+ trigger.
</para>
</refsect1>
@@ -79,7 +80,7 @@ DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceabl
</variablelist>
</refsect1>
- <refsect1 id="SQL-DROPEVENTTRIGGER-examples">
+ <refsect1 id="sql-dropeventtrigger-examples">
<title>Examples</title>
<para>
@@ -90,13 +91,14 @@ DROP EVENT TRIGGER snitch;
</programlisting></para>
</refsect1>
- <refsect1 id="SQL-DROPEVENTTRIGGER-compatibility">
+ <refsect1 id="sql-dropeventtrigger-compatibility">
<title>Compatibility</title>
<para>
- The <command>DROP EVENT TRIGGER</command> statement is a
- <productname>PostgreSQL</productname> extension.
+ There is no <command>DROP EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
+
</refsect1>
<refsect1>
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 32994b9..f579340 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -27,24 +27,6 @@
plain SQL function language.
</para>
- <para>
- <productname>PostgreSQL</productname> offers both triggers on commands
- (see <xref linkend="ddl">) and triggers on data manipulation
- (see <xref linkend="dml">).
- </para>
-
- <sect1 id="command-triggers">
- <title>Overview of Event Trigger Behavior</title>
-
- <para>
- A trigger is a specification that the database should automatically
- execute a particular function whenever a certain command is performed.
- The whole set of <productname>PostgreSQL</productname> commands is not
- supported for triggers, see <xref linkend="sql-createeventtrigger">
- for details.
- </para>
- </sect1>
-
<sect1 id="trigger-definition">
<title>Overview of Trigger Behavior</title>
Robert Haas <robertmhaas@gmail.com> writes:
Here is an incremental documentation patch which I hope you will like.
Definitely, it's better this way. I'm not thrilled with separating it
into its own top level chapter, but I can see how it makes sense indeed.
This part is strange though:
+ A trigger definition can also specify a <literal>WHEN</literal>
+ condition so that, for example, a <literal>command_start</literal>
+ tag can be fired only for particular commands which the user wishes
+ to intercept. A common use of such triggers is to restrict the range of
+ DDL operations which users may perform.
I don't think of that as firing a command tag, so it's hard for me to
parse that sentence.
the matrix somewhat. I think as we add more firing points it will be
clearer and easier to read if we have all the commands arranged in
columns rather than listing a bunch of firing points on each line. I
+1
also made a bunch of minor edits to improve readability and improve
the English (which wasn't bad, but I touched it up a bit); and I tried
to add some extra detail here and there (some of it recycled from
previous patch versions). Assuming this all seems reasonably
agreeable, can you merge it on your side?
Done, thanks !
This took the last several hours, so I haven't looked at your latest
code changes yet. However, in the course of editing the
documentation, it occurred to me that we seem to be fairly arbitrarily
excluding a large number of commands from the event trigger mechanism.
As many as that? I'm surprised about the quantity. Yes I did not add all
and any command we have, on purpose, and I agree that the new turn of
things allow us to add a new set.
For example, GRANT and REVOKE. In earlier patches, we needed
specific changes for every command, so there was some reason not to
try to support everything right out of the gate. But ISTM that the
argument for this is much less now; presumably it's just a few extra
lines of code per command, so maybe we ought to go ahead and try to
make this as complete as possible. I attempt to explain in the
Will do soon™.
attached patch the reasons why we don't support certain classes of
commands, but I can't come up with any explanation for supporting
GRANT and REVOKE that doesn't fall flat. I can't even really see a
reason not to support things like LISTEN and NOTIFY, and it would
certainly be more consistent with the notion of a command_start
trigger to support as many commands as we can.
I would think that NOTIFY is on a fast track not to be disturbed by
calling into used defined code, and that would explain why we don't
support event triggers here.
I had an interesting experience while testing this patch. I
accidentally redefined my event trigger function to something which
errored out. That of course precluded me from using CREATE OR REPLACE
FUNCTION to fix it. This makes me feel rather glad that we decided to
exclude CREATE/ALTER/DROP EVENT TRIGGER from the event trigger
mechanism, else recovery would have had to involve system catalog
hackery.
Yeah, we have some places were it's not very hard to shoot oneself in
the foot, here the resulting hole is a little too big and offers no real
benefits. Event triggers on create|alter|drop event triggers, really?
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Fri, Jul 6, 2012 at 12:00 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, Jul 6, 2012 at 7:21 AM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Attached is a incremental patch with a bunch of minor cleanups,
including reverts of a few spurious white space changes. Could you
merge this into your version?Thank you very much for that, yes it's included now. So you have 3
attachments here, the whole new patch revision (v1.7), the incremental
patch to go from 1.6 to 1.7 and the incremental patch that should apply
cleanly on top of your cleanups.Here is an incremental documentation patch which I hope you will like.
And here is another incremental patch, this one doing some more
cleanup. Some of this is cosmetic, but it also:
- Fixes the new event_trigger type so that it passes the type sanity
test, instead of adding the failure as expected output.
- Fixes DROP EVENT TRIGGER IF EXISTS on a non-existent trigger.
- Fleshes out the ownership handling so that it's more similar to what
we do for other types of objects.
I'm feeling pretty good about this at this point, although I think
there is still some more work to do before we call it done and go
home.
I have a large remaining maintainability concern about the way we're
mapping back and forth between node tags, event tags, and command
tags. Right now we've got parse_event_tag, which parses something
like 'ALTER AGGREGATE' into E_AlterAggregate; and then we've got
command_to_string, which turns E_AlterAggregate back into 'ALTER
AGGREGATE', and then we've got InitEventContext(), which turns
T_RenameStmt or T_AlterObjectSchemaStmt with OBJECT_AGGREGATE into
E_AlterAggregate. I can't easily verify that all three of these
things are consistent with each other, and even if they are right now
I estimate the chances of that remaining true as other people patch
the code as near-zero. You didn't like my last proposal for dealing
with this, which is fine: it might not have been the best way of
dealing with it. But I think we have to figure out something better
than what we've got now, or this is almost guaranteed to get broken.
If you don't have a brilliant idea I'll hack on it and see what I can
come up with.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
event-trigger-morecleanup.patchapplication/octet-stream; name=event-trigger-morecleanup.patchDownload
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 56c40b1..b097813 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -29,6 +29,7 @@
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
+#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
@@ -277,6 +278,10 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case ACL_KIND_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
+ case ACL_KIND_EVENT_TRIGGER:
+ elog(ERROR, "grantable rights not supported for event triggers");
+ /* not reached, but keep compiler quiet */
+ return ACL_NO_RIGHTS;
case ACL_KIND_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
@@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("permission denied for foreign server %s"),
+ /* ACL_KIND_EVENT_TRIGGER */
+ gettext_noop("permission denied for event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("permission denied for extension %s"),
};
@@ -3330,6 +3337,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
gettext_noop("must be owner of foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("must be owner of foreign server %s"),
+ /* ACL_KIND_EVENT_TRIGGER */
+ gettext_noop("must be owner of event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("must be owner of extension %s"),
};
@@ -3455,6 +3464,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_FOREIGN_SERVER:
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_EVENT_TRIGGER:
+ elog(ERROR, "grantable rights not supported for event triggers");
+ /* not reached, but keep compiler quiet */
+ return ACL_NO_RIGHTS;
case ACL_KIND_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
default:
@@ -4876,6 +4889,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
}
/*
+ * Ownership check for an event trigger (specified by OID).
+ */
+bool
+pg_event_trigger_ownercheck(Oid et_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("event trigger with OID %u does not exist",
+ et_oid)));
+
+ ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+/*
* Ownership check for a database (specified by OID).
*/
bool
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 40ae60d..5b8140b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -997,6 +997,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
NameListToString(objname));
break;
+ case OBJECT_EVENT_TRIGGER:
+ if (!pg_event_trigger_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ NameListToString(objname));
+ break;
case OBJECT_LANGUAGE:
if (!pg_language_ownercheck(address.objectId, roleid))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
@@ -1075,7 +1080,6 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
break;
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
- case OBJECT_EVENT_TRIGGER:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 78ef831..19f9895 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -70,10 +70,6 @@ ExecRenameStmt(RenameStmt *stmt)
RenameDatabase(stmt->subname, stmt->newname);
break;
- case OBJECT_EVENT_TRIGGER:
- RenameEventTrigger(stmt->subname, stmt->newname);
- break;
-
case OBJECT_FDW:
RenameForeignDataWrapper(stmt->subname, stmt->newname);
break;
@@ -82,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt)
RenameForeignServer(stmt->subname, stmt->newname);
break;
+ case OBJECT_EVENT_TRIGGER:
+ RenameEventTrigger(stmt->subname, stmt->newname);
+ break;
+
case OBJECT_FUNCTION:
RenameFunction(stmt->object, stmt->objarg, stmt->newname);
break;
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 1b8529e..8f5d7e0 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
args = NameListToString(list_truncate(objname,
list_length(objname) - 1));
break;
+ case OBJECT_EVENT_TRIGGER:
+ msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
+ name = NameListToString(objname);
+ break;
case OBJECT_RULE:
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
name = strVal(llast(objname));
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 561ce42..f9a3ea2 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -49,23 +49,6 @@ static void AlterEventTriggerOwner_internal(Relation rel,
Oid newOwnerId);
/*
- * Check permission: event triggers are only available for superusers. Raise
- * an exception when requirements are not fullfilled.
- *
- * It's not clear how to accept that database owners be able to create command
- * triggers, a superuser could run a command that fires a trigger's procedure
- * written by the database owner and now running with superuser privileges.
- */
-static void
-CheckEventTriggerPrivileges()
-{
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to use event triggers"))));
-}
-
-/*
* Insert Command Trigger Tuple
*
* Insert the new pg_event_trigger row, and return the OID assigned to the new
@@ -158,7 +141,16 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
Oid funcrettype;
Oid evtowner = GetUserId();
- CheckEventTriggerPrivileges();
+ /*
+ * It would be nice to allow database owners or even regular users to do
+ * this, but there are obvious privilege escalation risks which would have
+ * to somehow be plugged first.
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errmsg("permission denied to create event trigger \"%s\"",
+ stmt->trigname),
+ errhint("Must be superuser to create an event trigger.")));
/*
* Find and validate the trigger function.
@@ -171,7 +163,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
if (funcrettype != EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("function \"%s\" must return type \"command_trigger\"",
+ errmsg("function \"%s\" must return type \"event_trigger\"",
NameListToString(stmt->funcname))));
/*
@@ -224,15 +216,18 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
Form_pg_event_trigger evtForm;
char tgenabled = stmt->tgenabled;
- CheckEventTriggerPrivileges();
-
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
- tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
+ tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
+ CStringGetDatum(stmt->trigname));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("event trigger \"%s\" does not exist", stmt->trigname)));
+ errmsg("event trigger \"%s\" does not exist",
+ stmt->trigname)));
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ stmt->trigname);
/* tuple is a copy, so we can modify it below */
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
@@ -257,8 +252,6 @@ RenameEventTrigger(const char *trigname, const char *newname)
Relation rel;
Form_pg_event_trigger evtForm;
- CheckEventTriggerPrivileges();
-
rel = heap_open(EventTriggerRelationId, RowExclusiveLock);
/* newname must be available */
@@ -273,6 +266,9 @@ RenameEventTrigger(const char *trigname, const char *newname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("event trigger \"%s\" does not exist", trigname)));
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ trigname);
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
@@ -344,22 +340,31 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_event_trigger form;
- CheckEventTriggerPrivileges();
-
form = (Form_pg_event_trigger) GETSTRUCT(tup);
- if (form->evtowner != newOwnerId)
- {
- form->evtowner = newOwnerId;
+ if (form->evtowner == newOwnerId)
+ return;
- simple_heap_update(rel, &tup->t_self, tup);
- CatalogUpdateIndexes(rel, tup);
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ NameStr(form->evtname));
- /* Update owner dependency reference */
- changeDependencyOnOwner(EventTriggerRelationId,
- HeapTupleGetOid(tup),
- newOwnerId);
- }
+ /* New owner must be a superuser */
+ if (!superuser_arg(newOwnerId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of event trigger \"%s\"",
+ NameStr(form->evtname)),
+ errhint("The owner of an event trigger must be a superuser.")));
+
+ form->evtowner = newOwnerId;
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(EventTriggerRelationId,
+ HeapTupleGetOid(tup),
+ newOwnerId);
}
/*
@@ -412,14 +417,14 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
* Prepare the event trigger function context from the Command Context.
* We prepare a dedicated Node here so as not to publish internal data.
*/
- trigdata.type = T_EventTriggerData;
- trigdata.toplevel = ev_ctx->toplevel;
- trigdata.tag = ev_ctx->tag;
- trigdata.objectId = ev_ctx->objectId;
+ trigdata.type = T_EventTriggerData;
+ trigdata.toplevel = ev_ctx->toplevel;
+ trigdata.tag = ev_ctx->tag;
+ trigdata.objectId = ev_ctx->objectId;
trigdata.schemaname = ev_ctx->schemaname;
trigdata.objectname = ev_ctx->objectname;
- trigdata.parsetree = ev_ctx->parsetree;
- trigdata.when = pstrdup(event_to_string(tev));
+ trigdata.parsetree = ev_ctx->parsetree;
+ trigdata.when = pstrdup(event_to_string(tev));
/*
* Call the function, passing no arguments but setting a context.
@@ -448,12 +453,6 @@ InitEventContext(EventContext evt, const Node *parsetree)
evt->objectname = NULL;
evt->schemaname = NULL;
- /*
- * Fill in the event command, which is an enum constant to match against
- * what's stored into catalogs. As we are storing that on disk, we need the
- * enum values to be stable, see src/include/catalog/pg_event_trigger.h for
- * details.
- */
switch (nodeTag(parsetree))
{
case T_CreateSchemaStmt:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ec5c8f8..068834b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -214,7 +214,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
- DropAssertStmt DropTrigStmt DropEventTrigStmt DropRuleStmt DropCastStmt
+ DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
@@ -755,7 +755,6 @@ stmt :
| DropStmt
| DropTableSpaceStmt
| DropTrigStmt
- | DropEventTrigStmt
| DropRoleStmt
| DropUserStmt
| DropUserMappingStmt
@@ -4378,27 +4377,6 @@ trigger_command:
;
-DropEventTrigStmt:
- DROP EVENT TRIGGER name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($4)));
- n->behavior = $5;
- n->missing_ok = false;
- $$ = (Node *) n;
- }
- | DROP EVENT TRIGGER IF_P EXISTS name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($6)));
- n->behavior = $7;
- n->missing_ok = true;
- $$ = (Node *) n;
- }
- ;
-
AlterEventTrigStmt:
ALTER EVENT TRIGGER name enable_trigger
{
@@ -5002,6 +4980,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
+ | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
| COLLATION { $$ = OBJECT_COLLATION; }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index c8c1a81..b772f2b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -356,7 +356,7 @@ standard_ProcessUtility(Node *parsetree,
completionTag[0] = '\0';
/* Event Trigger support for command_start */
- InitEventContext(&evt, (Node *)parsetree);
+ InitEventContext(&evt, parsetree);
if (CommandFiresTriggersForEvent(&evt, E_CommandStart))
ExecEventTriggers(&evt, E_CommandStart);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index d7770b8..8590f3c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -293,6 +293,33 @@ trigger_out(PG_FUNCTION_ARGS)
/*
+ * event_trigger_in - input routine for pseudo-type event_trigger.
+ */
+Datum
+event_trigger_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type event_trigger")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * event_trigger_out - output routine for pseudo-type event_trigger.
+ */
+Datum
+event_trigger_out(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot display a value of type event_trigger")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
* language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER.
*/
Datum
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bcd042e..09ca6dd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5313,7 +5313,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
i_oid,
i_evtname,
i_evtevent,
- i_evtowner,
+ i_evtowner,
i_evttags,
i_evtfname,
i_evtenabled;
@@ -5333,7 +5333,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
"SELECT e.tableoid, e.oid, evtname, evtenabled, "
"evtevent, (%s evtowner) AS evtowner, "
"array_to_string(array("
- "select '''' || x || '''' "
+ "select quote_literal(x) "
" from unnest(evttags) as t(x)), ', ') as evttags, "
"e.evtfoid::regproc as evtfname "
"FROM pg_event_trigger e "
@@ -13756,7 +13756,7 @@ dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
appendPQExpBuffer(query, "CREATE EVENT TRIGGER ");
appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name));
appendPQExpBuffer(query, " ON ");
- appendPQExpBufferStr(query, evtinfo->evtevent);
+ appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
appendPQExpBufferStr(query, " ");
if (strcmp("", evtinfo->evttags) != 0)
@@ -13768,7 +13768,7 @@ dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
appendPQExpBuffer(query, "\n EXECUTE PROCEDURE ");
appendPQExpBufferStr(query, evtinfo->evtfname);
- appendPQExpBuffer(query, " ();\n");
+ appendPQExpBuffer(query, "();\n");
if (evtinfo->evtenabled != 'O')
{
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6d4c2ba..5318e7a 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter");
* Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: collations,
- * extensions, text search, foreign-data, and default ACL objects can't really
- * happen here, so the rather bogus priorities for them don't matter.
+ * extensions, text search, foreign-data, event trigger, and default ACL
+ * objects can't really happen here, so the rather bogus priorities for them
+ * don't matter.
*
* NOTE: object-type priorities must match the section assignments made in
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
@@ -114,7 +115,7 @@ static const int newObjectTypePriority[] =
24, /* DO_BLOB_DATA */
22, /* DO_PRE_DATA_BOUNDARY */
25, /* DO_POST_DATA_BOUNDARY */
- 30 /* DO_EVENT_TRIGGER */
+ 32 /* DO_EVENT_TRIGGER */
};
static DumpId preDataBoundId;
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bee7154..743f890 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3456,6 +3456,10 @@ DATA(insert OID = 2300 ( trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2
DESCR("I/O");
DATA(insert OID = 2301 ( trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2279" _null_ _null_ _null_ _null_ trigger_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3594 ( event_trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3838 "2275" _null_ _null_ _null_ _null_ event_trigger_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3595 ( event_trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3838" _null_ _null_ _null_ _null_ event_trigger_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 2302 ( language_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2280 "2275" _null_ _null_ _null_ _null_ language_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 2303 ( language_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2280" _null_ _null_ _null_ _null_ language_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 98c7dd5..86be998 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -650,7 +650,7 @@ DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void
#define VOIDOID 2278
DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 2d1cccb..5700e54 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -195,6 +195,7 @@ typedef enum AclObjectKind
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
ACL_KIND_FDW, /* pg_foreign_data_wrapper */
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
+ ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */
ACL_KIND_EXTENSION, /* pg_extension */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
@@ -322,6 +323,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
+extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 1063403..8b9291c 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS);
extern Datum void_send(PG_FUNCTION_ARGS);
extern Datum trigger_in(PG_FUNCTION_ARGS);
extern Datum trigger_out(PG_FUNCTION_ARGS);
+extern Datum event_trigger_in(PG_FUNCTION_ARGS);
+extern Datum event_trigger_out(PG_FUNCTION_ARGS);
extern Datum language_handler_in(PG_FUNCTION_ARGS);
extern Datum language_handler_out(PG_FUNCTION_ARGS);
extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5b456aa..f772fd4 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1571,7 +1571,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
}
-/* Set up the arguments for a command trigger call. */
+/* Set up the arguments for an event trigger call. */
static SV *
plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
{
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 77c5ed9..9d17686 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -536,7 +536,7 @@ do_compile(FunctionCallInfo fcinfo,
if (rettypeid == VOIDOID ||
rettypeid == RECORDOID)
/* okay */ ;
- else if (rettypeid == TRIGGEROID)
+ else if (rettypeid == TRIGGEROID || rettypeid == EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
@@ -689,7 +689,7 @@ do_compile(FunctionCallInfo fcinfo,
if (procStruct->pronargs != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("command trigger functions cannot have declared arguments")));
+ errmsg("event trigger functions cannot have declared arguments")));
/* Add the variable tg_when */
var = plpgsql_build_variable("tg_when", 0,
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd59d75..373facf 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -773,8 +773,8 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
return rettup;
}
-void plpgsql_exec_event_trigger(PLpgSQL_function *func,
- EventTriggerData *trigdata)
+void
+plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
diff --git a/src/test/regress/expected/event_triggers.out b/src/test/regress/expected/event_triggers.out
index 415605b..6eac7e9 100644
--- a/src/test/regress/expected/event_triggers.out
+++ b/src/test/regress/expected/event_triggers.out
@@ -10,9 +10,6 @@ begin
raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
end;
$$;
---
--- TODO: REASSIGN OWNED and DROP OWNED
---
create event trigger any_t on command_start
execute procedure snitch();
create event trigger foo_t on command_start
@@ -67,8 +64,12 @@ NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
create schema cmd2;
NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
-create role regbob;
-alter event trigger snitch owner to regbob;
+create role regression_bob;
+alter event trigger snitch owner to regression_bob;
+ERROR: permission denied to change owner of event trigger "snitch"
+HINT: The owner of an event trigger must be a superuser.
+alter role regression_bob superuser;
+alter event trigger snitch owner to regression_bob;
create table cmd.foo(id bigserial primary key);
NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
@@ -158,7 +159,7 @@ NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
create sequence test_seq_;
NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
-alter sequence test_seq_ owner to regbob;
+alter sequence test_seq_ owner to regression_bob;
NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
alter sequence test_seq_ rename to test_seq;
@@ -197,7 +198,7 @@ NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
create view view_test as select id, things from cmd.test;
NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
-alter view view_test owner to regbob;
+alter view view_test owner to regression_bob;
NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
alter view view_test rename to view_test2;
@@ -248,7 +249,7 @@ NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
alter function notfun(int) set schema cmd;
NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-alter function cmd.notfun(int) owner to regbob;
+alter function cmd.notfun(int) owner to regression_bob;
NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
alter function cmd.notfun(int) cost 77;
@@ -297,7 +298,7 @@ NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
create type cmd.type_test AS (a integer, b integer, c text);
NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
-alter type cmd.type_test owner to regbob;
+alter type cmd.type_test owner to regression_bob;
NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
alter type cmd.type_test rename to type_test2;
@@ -349,7 +350,7 @@ NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
alter domain cmd.us_postal_code drop constraint dummy_constraint;
NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-alter domain cmd.us_postal_code owner to regbob;
+alter domain cmd.us_postal_code owner to regression_bob;
NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
alter domain cmd.us_postal_code set schema cmd2;
@@ -455,12 +456,14 @@ drop schema cmd2 cascade;
NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
-- fail because owning event trigger snitch
-drop role regbob;
-ERROR: role "regbob" cannot be dropped because some objects depend on it
+drop role regression_bob;
+ERROR: role "regression_bob" cannot be dropped because some objects depend on it
DETAIL: owner of event trigger snitch
drop event trigger any_t;
-drop event trigger snitch;
-drop role regbob;
+drop event trigger if exists snitch;
+drop event trigger if exists snitch;
+NOTICE: event trigger "snitch" does not exist, skipping
+drop role regression_bob;
create table onerow(id integer);
create or replace function insert_one_row()
returns event_trigger
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 0dac40f..70eab92 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -134,11 +134,10 @@ WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
- oid | typname | oid | proname
-------+---------------+------+------------
- 1790 | refcursor | 46 | textin
- 3838 | event_trigger | 2300 | trigger_in
-(2 rows)
+ oid | typname | oid | proname
+------+-----------+-----+---------
+ 1790 | refcursor | 46 | textin
+(1 row)
-- Varlena array types will point to array_in
-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
@@ -178,11 +177,10 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1)))
ORDER BY 1;
- oid | typname | oid | proname
-------+---------------+------+-------------
- 1790 | refcursor | 47 | textout
- 3838 | event_trigger | 2301 | trigger_out
-(2 rows)
+ oid | typname | oid | proname
+------+-----------+-----+---------
+ 1790 | refcursor | 47 | textout
+(1 row)
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
diff --git a/src/test/regress/sql/event_triggers.sql b/src/test/regress/sql/event_triggers.sql
index cf25ee7..84a00e4 100644
--- a/src/test/regress/sql/event_triggers.sql
+++ b/src/test/regress/sql/event_triggers.sql
@@ -11,10 +11,6 @@ begin
end;
$$;
---
--- TODO: REASSIGN OWNED and DROP OWNED
---
-
create event trigger any_t on command_start
execute procedure snitch();
@@ -68,9 +64,11 @@ alter event trigger foo_t rename to snitch;
create schema cmd;
create schema cmd2;
-create role regbob;
-alter event trigger snitch owner to regbob;
+create role regression_bob;
+alter event trigger snitch owner to regression_bob;
+alter role regression_bob superuser;
+alter event trigger snitch owner to regression_bob;
create table cmd.foo(id bigserial primary key);
create view cmd.v as select * from cmd.foo;
@@ -103,7 +101,7 @@ alter table cmd.test set with oids;
alter table cmd.test set without oids;
create sequence test_seq_;
-alter sequence test_seq_ owner to regbob;
+alter sequence test_seq_ owner to regression_bob;
alter sequence test_seq_ rename to test_seq;
alter sequence test_seq set schema cmd;
alter sequence cmd.test_seq start with 3;
@@ -117,7 +115,7 @@ alter sequence cmd.test_seq cycle;
alter sequence cmd.test_seq no cycle;
create view view_test as select id, things from cmd.test;
-alter view view_test owner to regbob;
+alter view view_test owner to regression_bob;
alter view view_test rename to view_test2;
alter view view_test2 set schema cmd;
alter view cmd.view_test2 alter column id set default 9;
@@ -142,7 +140,7 @@ as $$ select t from cmd.foo where id = $1; $$;
alter function fun(int) strict;
alter function fun(int) rename to notfun;
alter function notfun(int) set schema cmd;
-alter function cmd.notfun(int) owner to regbob;
+alter function cmd.notfun(int) owner to regression_bob;
alter function cmd.notfun(int) cost 77;
drop function cmd.notfun(int);
@@ -170,7 +168,7 @@ create type cmd.compfoo AS (f1 int, f2 text);
alter type cmd.compfoo add attribute f3 text;
create type cmd.type_test AS (a integer, b integer, c text);
-alter type cmd.type_test owner to regbob;
+alter type cmd.type_test owner to regression_bob;
alter type cmd.type_test rename to type_test2;
alter type cmd.type_test2 set schema public;
alter type public.type_test2 rename attribute a to z;
@@ -191,7 +189,7 @@ alter domain cmd.us_postal_code drop default;
alter domain cmd.us_postal_code drop not null;
alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$');
alter domain cmd.us_postal_code drop constraint dummy_constraint;
-alter domain cmd.us_postal_code owner to regbob;
+alter domain cmd.us_postal_code owner to regression_bob;
alter domain cmd.us_postal_code set schema cmd2;
drop domain cmd2.us_postal_code;
@@ -248,11 +246,12 @@ drop schema cmd1 cascade;
drop schema cmd2 cascade;
-- fail because owning event trigger snitch
-drop role regbob;
+drop role regression_bob;
drop event trigger any_t;
-drop event trigger snitch;
-drop role regbob;
+drop event trigger if exists snitch;
+drop event trigger if exists snitch;
+drop role regression_bob;
create table onerow(id integer);
Robert Haas <robertmhaas@gmail.com> writes:
And here is another incremental patch, this one doing some more
cleanup. Some of this is cosmetic, but it also:
Thanks, applied in my github repository!
I'm feeling pretty good about this at this point, although I think
there is still some more work to do before we call it done and go
home.
Nice reading that :)
I have a large remaining maintainability concern about the way we're
mapping back and forth between node tags, event tags, and command
tags. Right now we've got parse_event_tag, which parses something
[…valid concern…]
If you don't have a brilliant idea I'll hack on it and see what I can
come up with.
I think we might be able to install a static array for the setup where
we would find the different elements, and then code up some procedures
doing different kind of look ups in that array.
like 'ALTER AGGREGATE' into E_AlterAggregate; and then we've got
command_to_string, which turns E_AlterAggregate back into 'ALTER
AGGREGATE', and then we've got InitEventContext(), which turns
T_RenameStmt or T_AlterObjectSchemaStmt with OBJECT_AGGREGATE into
E_AlterAggregate. I can't easily verify that all three of these
{
E_AlterAggregate, // TrigEventCommand
"ALTER AGGREGATE", // command tag
T_RenameStmt, // nodeTag
-1 // object type
},
{
E_AlterAggregate,
"ALTER AGGREGATE",
T_AlterObjectSchemaStmt,
OBJECT_AGGREGATE
}
The problem is coming up with a way of writing the code that does not
incur a full array scan for each step of parsing or rewriting. And I
don't see that it merits yet another cache. Given the existing event
trigger cache it might be that we don't care about having a full scan of
this table twice per event trigger related commands, as I don't think it
would happen when executing other DDLs.
Scratch that we need to parse command tags when we build the event
cache, so scanning the full array each time would make that O(n²) and we
want to avoid that. So we could install the contents of the array in
another hash table in BuildEventTriggerCache() then use that to lookup
the TrigEventCommand from the command tag…
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
On Fri, Jul 6, 2012 at 3:29 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Here is an incremental documentation patch which I hope you will like.
Definitely, it's better this way. I'm not thrilled with separating it
into its own top level chapter, but I can see how it makes sense indeed.
Oh, really? I thought that was a huge readability improvement. There
are some things that are the same between triggers and event triggers,
but there's an awful lotta stuff that is completely different.
This part is strange though:
+ A trigger definition can also specify a <literal>WHEN</literal> + condition so that, for example, a <literal>command_start</literal> + tag can be fired only for particular commands which the user wishes + to intercept. A common use of such triggers is to restrict the range of + DDL operations which users may perform.I don't think of that as firing a command tag, so it's hard for me to
parse that sentence.
Oh, that should say "a command_start trigger" rather than "a
command_start tag". Good catch.
This took the last several hours, so I haven't looked at your latest
code changes yet. However, in the course of editing the
documentation, it occurred to me that we seem to be fairly arbitrarily
excluding a large number of commands from the event trigger mechanism.As many as that? I'm surprised about the quantity. Yes I did not add all
and any command we have, on purpose, and I agree that the new turn of
things allow us to add a new set.
I admit I didn't count them up. :-) Maybe there aren't that many.
I think we might want to extend the support matrix to include every
command that appears in sql-commands.html and have either an X if it's
supported or blank if it's not. That would make it easier to judge
how many commands are not supported, not just for us but for users who
may be trying to answer the same questions we are.
I would think that NOTIFY is on a fast track not to be disturbed by
calling into used defined code, and that would explain why we don't
support event triggers here.
If the DBA is allowed to restrict CREATE FUNCTION, why not NOTIFY? I
guess I don't see why that one's deserving of special treatment.
Mind you, if I ran the world, this would probably be broken up
differently: I'd have ddl_command_start covering all the
CREATE/ALTER/DROP commands and nothing else; and separate firing
points for anything else I wanted to support. It's not too late to
make that change, hint, hint. But if we're not gonna do that then I
think that we'd better try to cast the net as broadly as reasonably
possible. It seems to me that our excuse for not including things
like UPDATE and DELETE is a bit thin; surely there are people who
would like a sort of universal trigger that applies to every relation
in the system. Of course there are recursion problems there that need
to be thought long hard about, and no I don't really want to go there
right now, but I'll bet you a nickle that someone is going to ask why
it doesn't work that way.
Another advantage to recasting this as ddl_command_start is that we
quite easily pass the operation (CREATE, ALTER, DROP) and the named
object type (TABLE, FUNCTION, CAST) as separate arguments. I think
that would be a usability improvement, since it would then be dead
easy to write an event trigger that prohibits DROP (and only DROP) of
any sort. Of course it's not that hard to do it right now, but you
have to parse the command tag. It would likely simplify the code for
mapping between node and command tags, too.
One other thought: if we're NOT going to do what I suggested above,
then how about renaming TG_WHEN to TG_EVENT? Seems like that would
fit better.
Also: now that the E_WhatEver constants don't get stored on disk, I
don't think they should live in pg_event_trigger.h any more; can we
move them to someplace more appropriate? Possibly make them private
to event_trigger.c? And, on a related note, I don't think it's a good
idea to use E_ as a prefix for both the event types and the command
tags. It's too short, and hard to grep for, and we don't want the
same one for both, I think. How above things like EVT_CommandStart
for the events and ECT_CreateAggregate for the command tags?
I had an interesting experience while testing this patch. I
accidentally redefined my event trigger function to something which
errored out. That of course precluded me from using CREATE OR REPLACE
FUNCTION to fix it. This makes me feel rather glad that we decided to
exclude CREATE/ALTER/DROP EVENT TRIGGER from the event trigger
mechanism, else recovery would have had to involve system catalog
hackery.Yeah, we have some places were it's not very hard to shoot oneself in
the foot, here the resulting hole is a little too big and offers no real
benefits. Event triggers on create|alter|drop event triggers, really?
Indeed.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Jul 6, 2012 at 4:00 PM, Dimitri Fontaine <dimitri@2ndquadrant.fr> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
And here is another incremental patch, this one doing some more
cleanup. Some of this is cosmetic, but it also:Thanks, applied in my github repository!
Thanks.
I have a large remaining maintainability concern about the way we're
mapping back and forth between node tags, event tags, and command
tags. Right now we've got parse_event_tag, which parses something[…valid concern…]
If you don't have a brilliant idea I'll hack on it and see what I can
come up with.I think we might be able to install a static array for the setup where
we would find the different elements, and then code up some procedures
doing different kind of look ups in that array.
+1.
like 'ALTER AGGREGATE' into E_AlterAggregate; and then we've got
command_to_string, which turns E_AlterAggregate back into 'ALTER
AGGREGATE', and then we've got InitEventContext(), which turns
T_RenameStmt or T_AlterObjectSchemaStmt with OBJECT_AGGREGATE into
E_AlterAggregate. I can't easily verify that all three of these{
E_AlterAggregate, // TrigEventCommand
"ALTER AGGREGATE", // command tag
T_RenameStmt, // nodeTag
-1 // object type
},
{
E_AlterAggregate,
"ALTER AGGREGATE",
T_AlterObjectSchemaStmt,
OBJECT_AGGREGATE
}The problem is coming up with a way of writing the code that does not
incur a full array scan for each step of parsing or rewriting. And I
don't see that it merits yet another cache. Given the existing event
trigger cache it might be that we don't care about having a full scan of
this table twice per event trigger related commands, as I don't think it
would happen when executing other DDLs.Scratch that we need to parse command tags when we build the event
cache, so scanning the full array each time would make that O(n²) and we
want to avoid that. So we could install the contents of the array in
another hash table in BuildEventTriggerCache() then use that to lookup
the TrigEventCommand from the command tag…
Ugh. Yeah, obviously the most important thing I think is that
InitEventContext() needs to be lightning-fast, but we don't want
BuildEventTriggerCache() to be pathologically slow either.
I think the best thing to do with InitEventContext() might be to get
rid of it. It's just a big switch over node tags, and we've already
got one of those in standard_ProcessUtility. Maybe every case that
already exists in that function should either (a) get a comment of the
form /* does not support event triggers */ or (b) get a call of the
form EventTriggerStartup(&evt, parsetree, E_WhateverCommandThisIs).
EventTriggerStartup() could call InitEventContext() and then if
CommandFiresTriggersForEvent(..., E_CommandStart) it could also call
ExecEventTriggers(). This might seem like it's just moving the wood
around, but if someone adds a new case in standard_ProcessUtility,
they're going to model it on one of the existing cases, which greatly
decreases the likelihood that they're going to screw it up. And if
they do screw it up it will be obviously non-parallel to the rest of
what's there, so somebody can notice and fix it. As a side benefit,
this would probably be faster than having two separate switches that
are executed more or less consecutively.
Now that leaves the question of how to translate between
E_AlterAggregate and "ALTER AGGREGATE"; I think your idea of a hash
table (or two?) might be the most practical option. We'd only need to
build the hash table(s) if an index-scan of pg_event_trigger finds it
non-empty, and then only once per session.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Thom Brown <thom@linux.com> writes:
I also attach various typo/grammar fixes.
In fact Robert's cleanup of the docs make that patch of yours not apply
anymore, and I think a part of it is maybe already fixed. Do you have
time to look at this with the new v1.8 patch that you will receive in a
minute, or with the github branch if you're tracking that?
Sorry about that.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Hi,
Please find attached a new revision of the patch, v1.8, and an
incremental diff from the previous one, that includes the patches you
sent me.
Robert Haas <robertmhaas@gmail.com> writes:
Oh, really? I thought that was a huge readability improvement. There
are some things that are the same between triggers and event triggers,
but there's an awful lotta stuff that is completely different.
True.
Oh, that should say "a command_start trigger" rather than "a
command_start tag". Good catch.
Fixed in the attached.
I think we might want to extend the support matrix to include every
command that appears in sql-commands.html and have either an X if it's
supported or blank if it's not. That would make it easier to judge
how many commands are not supported, not just for us but for users who
may be trying to answer the same questions we are.
Yes, not done yet, see below.
Mind you, if I ran the world, this would probably be broken up
differently: I'd have ddl_command_start covering all the
CREATE/ALTER/DROP commands and nothing else; and separate firing
points for anything else I wanted to support. It's not too late to
make that change, hint, hint. But if we're not gonna do that then I
Let's see about doing that. I guess we would have ddl_command_start and
command_start, and I would think that the later is the most generic. So
we would certainly want DDL commands to run first the command_start
event triggers then the ddl_command_start event triggers, whereas a
NOTIFY would only run command_start triggers, and a GRANT command would
run maybe command_start then dcl_command_start triggers?
If that's where we're going to, we can commit as-is and expand later.
think that we'd better try to cast the net as broadly as reasonably
possible. It seems to me that our excuse for not including things
like UPDATE and DELETE is a bit thin; surely there are people who
would like a sort of universal trigger that applies to every relation
in the system. Of course there are recursion problems there that need
to be thought long hard about, and no I don't really want to go there
right now, but I'll bet you a nickle that someone is going to ask why
it doesn't work that way.
The current reason why we only support 149 SQL commands and variations
is because we want a patch that's easy enough to review and agree on. So
I think we will in the future be able to add new firing point at places
where maybe some discussion is needed.
Such places, in my mind, include the NOTIFY mechanism, DCLs, and global
objects such as databases and tablespaces and roles. I'd be happy to see
event triggers embrace support for those. Maybe in v2 though?
Another advantage to recasting this as ddl_command_start is that we
quite easily pass the operation (CREATE, ALTER, DROP) and the named
object type (TABLE, FUNCTION, CAST) as separate arguments. I think
That's a good idea. I don't think we should replace the current tag
support with that though, because some commands are harder to stow into
the operation and type model (in supported commands, mainly LOAD).
So I've included partial support for that in the attached patch, in the
simplest way possible, just so that we can see where it leads in term of
using the feature. The next step here is to actually go in each branch
of the process utility switch and manually decorate the command context
with the current operation and objecttype when relevant.
One other thought: if we're NOT going to do what I suggested above,
then how about renaming TG_WHEN to TG_EVENT? Seems like that would
fit better.
That seems like the right thing to do in either case, done.
Also: now that the E_WhatEver constants don't get stored on disk, I
don't think they should live in pg_event_trigger.h any more; can we
move them to someplace more appropriate? Possibly make them private
to event_trigger.c? And, on a related note, I don't think it's a good
Done, they now live in evtcache.h, where they belong.
idea to use E_ as a prefix for both the event types and the command
tags. It's too short, and hard to grep for, and we don't want the
same one for both, I think. How above things like EVT_CommandStart
for the events and ECT_CreateAggregate for the command tags?
Done this way.
Robert Haas <robertmhaas@gmail.com> writes:
I think we might be able to install a static array for the setup where
we would find the different elements, and then code up some procedures
doing different kind of look ups in that array.+1.
Done in the attached. Filling that array was… an interesting use case
for Emacs Keyboard Macros spanning 3 different buffers, maintaining it
should be easy enough now.
Ugh. Yeah, obviously the most important thing I think is that
InitEventContext() needs to be lightning-fast, but we don't want
BuildEventTriggerCache() to be pathologically slow either.
So I've built two new hash tables so that we can either search internal
command enum number by command tag or by parse tree nodeTag and object
type. The hash table is only built when first needed, I didn't add any
trick to only build it when the pg_event_trigger table is empty.
I think the best thing to do with InitEventContext() might be to get
rid of it. It's just a big switch over node tags, and we've already
got one of those in standard_ProcessUtility. Maybe every case that
Given that second hash table (EventTriggerCommandNodeCache) we can now
move that look-up to later in the course of events. I kept
InitEventContext() as the place where to stuff default values in the
EventContext structure, though.
Also, as we're optimizing things, there's no longer any call sites to
the function CommandFiresTriggersForEvent() in the attached patch.
That's because this function main use case is allowing the specific
command support code to avoid some possibly costly setup when there's in
fact no trigger to fire. That's never going to be the case with
command_start triggers as they have no specific variable support.
A typical integration now looks like this:
case T_CreateCastStmt:
ExecEventTriggers(&evt, EVT_CommandStart);
CreateCast((CreateCastStmt *) parsetree);
break;
Now that leaves the question of how to translate between
E_AlterAggregate and "ALTER AGGREGATE"; I think your idea of a hash
table (or two?) might be the most practical option. We'd only need to
build the hash table(s) if an index-scan of pg_event_trigger finds it
non-empty, and then only once per session.
As said earlier, I implemented that without the non-empty trick.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
Attachments:
event_triggers_v1.7--v1.8.patchtext/x-patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2b46b75..b100a42 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -99,11 +99,6 @@
</row>
<row>
- <entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
- <entry>event triggers</entry>
- </row>
-
- <row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
</row>
@@ -149,6 +144,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-event-trigger"><structname>pg_event_trigger</structname></link></entry>
+ <entry>event triggers</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
<entry>installed extensions</entry>
</row>
@@ -1871,6 +1871,7 @@
<para>
The catalog <structname>pg_event_trigger</structname> stores event triggers.
+ See <xref linkend="event-triggers"> for more information.
</para>
<table>
@@ -1891,23 +1892,28 @@
<entry><structfield>evtname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
- <entry>Trigger name (unique)</entry>
+ <entry>Trigger name (must be unique)</entry>
</row>
<row>
<entry><structfield>evtevent</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
- <entry>The event this trigger fires for.</entry>
+ <entry>Identifies the event for which this trigger fires</entry>
+ </row>
+
+ <row>
+ <entry><structfield>evtowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the event trigger</entry>
</row>
<row>
<entry><structfield>evtfoid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>
- The OID of the function called by this event trigger.
- </entry>
+ <entry>The function to be called</entry>
</row>
<row>
@@ -1928,7 +1934,10 @@
<entry><structfield>evttags</structfield></entry>
<entry><type>text[]</type></entry>
<entry></entry>
- <entry>Command tags of the commands this trigger is restricted to.</entry>
+ <entry>
+ Command tags for which this trigger will fire. If NULL, the firing
+ of this trigger is not restricted on the basis of the command tag.
+ </entry>
</row>
</tbody>
</tgroup>
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
new file mode 100644
index 0000000..52e9764
--- /dev/null
+++ b/doc/src/sgml/event-trigger.sgml
@@ -0,0 +1,466 @@
+<!-- doc/src/sgml/event-trigger.sgml -->
+
+ <chapter id="event-triggers">
+ <title>Event Triggers</title>
+
+ <indexterm zone="event-triggers">
+ <primary>event trigger</primary>
+ </indexterm>
+
+ <para>
+ To supplement the trigger mechanism discussed in <xref linkend="triggers">,
+ <productname>PostgreSQL</> also provides event triggers. Unlike regular
+ triggers, which are attached to a single table and capture only DML events,
+ event triggers are global to a particular database and are capable of
+ capturing DDL events.
+ </para>
+
+ <para>
+ Like regular triggers, event triggers can be written in any procedural
+ language that includes event trigger support, or in C, but not in plain
+ SQL.
+ </para>
+
+ <sect1 id="event-trigger-definition">
+ <title>Overview of Event Trigger Behavior</title>
+
+ <para>
+ An event trigger fires whenever the event with which it is
+ associated occurs in the database in which it is defined. Currently,
+ the only supported event is <literal>command_start</>. Support for
+ additional events may be added in future releases.
+ </para>
+
+ <para>
+ The <literal>command_start</> event occurs just before the execution of
+ many SQL commands, most of which can be broadly described as
+ <link linkend="ddl">DDL</link>. There are a number of commands that,
+ for various reasons, are specifically excluded from the event trigger
+ system:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands that
+ access or modify data within a particular table, such
+ <xref linkend="sql-select">, <xref linkend="sql-insert">,
+ <xref linkend="sql-update">, <xref linkend="sql-delete">, and
+ <xref linkend="sql-truncate">. This also includes commands related
+ to prepared plans, such as <xref linkend="sql-prepare">,
+ <xref linkend="sql-deallocate">, <xref linkend="sql-declare">,
+ and <xref linkend="sql-fetch">. Ordinary triggers or rules should
+ be used in these cases.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands that
+ create, alter, or drop global objects. Unlike the event trigger, these
+ objects are not part of the current database; they are accessible from
+ all databases in the cluster, and are therefore unaffected by event
+ triggers. Such objects include databases, tablespaces, and roles.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>command_start</> event does not occur for commands where
+ firing a trigger might result either result in system instability or
+ interfere with the administrator's ability to regain control of the
+ database. These include transaction control commands, such as
+ <xref linkend="sql-begin"> or <xref linkend="sql-commit">; configuration
+ commands, such as <xref linkend="sql-set"> or <xref linkend="sql-show">;
+ and commands related to the event trigger mechanism itself, such as
+ <xref linkend="sql-createeventtrigger">,
+ <xref linkend="sql-altereventtrigger">, and
+ <xref linkend="sql-dropeventtrigger">.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ For a complete list of commands supported by the event trigger mechanism,
+ see <xref linkend="event-trigger-matrix">.
+ </para>
+
+ <para>
+ In order to create an event trigger, you must first create a function with
+ the special return type <literal>event_trigger</literal>. This function
+ need not (and may not) return a value; the return type serves merely as
+ a signal that the function is to be invoked as an event trigger.
+ </para>
+
+ <para>
+ If more than one event trigger is defined for a particular event, they will
+ fire in alphabetical order by trigger name.
+ </para>
+
+ <para>
+ A trigger definition can also specify a <literal>WHEN</literal>
+ condition so that, for example, a <literal>command_start</literal>
+ trigger can be fired only for particular commands which the user wishes
+ to intercept. A common use of such triggers is to restrict the range of
+ DDL operations which users may perform.
+ </para>
+ </sect1>
+
+ <sect1 id="event-trigger-matrix">
+ <title>Event Trigger Firing Matrix</title>
+
+ <para>
+ <xref linkend="event-trigger-by-command-tag"> lists all commands
+ for which event triggers are supported.
+ </para>
+
+ <table id="event-trigger-by-command-tag">
+ <title>Event Trigger Support by Command Tag</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>command tag</entry>
+ <entry><literal>command_start</literal></entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry align="left"><literal>ALTER AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>ALTER VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CLUSTER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE CAST</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE INDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE RULE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TABLE AS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>CREATE VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP AGGREGATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP CAST</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP COLLATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP CONVERSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP DOMAIN</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP EXTENSION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP FUNCTION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP INDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP LANGUAGE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP RULE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SCHEMA</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SEQUENCE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP SERVER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TABLE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TRIGGER</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP TYPE</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP USER MAPPING</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>DROP VIEW</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>LOAD</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>REINDEX</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>SELECT INTO</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ <row>
+ <entry align="left"><literal>VACUUM</literal></entry>
+ <entry align="center"><literal>X</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 82b9e39..db4cc3a 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -61,6 +61,7 @@
<!ENTITY rules SYSTEM "rules.sgml">
<!ENTITY spi SYSTEM "spi.sgml">
<!ENTITY trigger SYSTEM "trigger.sgml">
+<!ENTITY event-trigger SYSTEM "event-trigger.sgml">
<!ENTITY xaggr SYSTEM "xaggr.sgml">
<!ENTITY xfunc SYSTEM "xfunc.sgml">
<!ENTITY xindex SYSTEM "xindex.sgml">
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index b783e86..e490086 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -634,7 +634,7 @@ SELECT init_hosts_query();
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();
- query_hosts
+ query_hosts
-----------------
(1,192.168.1.1)
(2,192.168.1.2)
@@ -1220,7 +1220,7 @@ CREATE TRIGGER test_valid_id_trig
</sect2>
<sect2 id="plperl-event-trigger">
- <title>Trigger Procedures on Events in PL/Perl </title>
+ <title>Trigger Procedures on Events in PL/Perl</title>
<indexterm>
<primary>event trigger</primary>
@@ -1229,9 +1229,9 @@ CREATE TRIGGER test_valid_id_trig
<para>
Event trigger procedures can be written in PL/Perl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -1272,7 +1272,7 @@ CREATE TRIGGER test_valid_id_trig
<term><varname>$_TD->{objectname}</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
@@ -1289,7 +1289,7 @@ CREATE TRIGGER test_valid_id_trig
</varlistentry>
</variablelist>
</para>
- </sect2>
+ </sect2>
</sect1>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index fadd5d7..a21aaf2 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -3378,7 +3378,7 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
</indexterm>
<sect2 id="plpgsql-dml-trigger">
- <title>Triggers on data change</title>
+ <title>Triggers on data changes</title>
<para>
<application>PL/pgSQL</application> can be used to define trigger
@@ -3930,19 +3930,19 @@ SELECT * FROM sales_summary_bytime;
</sect2>
<sect2 id="plpgsql-event-trigger">
- <title>Triggers on event</title>
+ <title>Triggers on events</title>
<para>
- <application>PL/pgSQL</application> can be used to define command
- trigger procedures. An event trigger procedure is created with the
- <command>CREATE FUNCTION</> command, declaring it as a function with
- no arguments and a return type of <type>event trigger</type>.
+ <application>PL/pgSQL</application> can be used to define event
+ triggers. <productname>PostgreSQL</> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
- <para>
- When a <application>PL/pgSQL</application> function is called as a
- event trigger, several special variables are created automatically
- in the top-level block. They are:
+ <para>
+ When a <application>PL/pgSQL</application> function is called as a
+ event trigger, several special variables are created automatically
+ in the top-level block. They are:
<variablelist>
<varlistentry>
@@ -3999,10 +3999,6 @@ SELECT * FROM sales_summary_bytime;
</variablelist>
</para>
- <para>
- The event trigger function's return value is not used.
- </para>
-
<para>
<xref linkend="plpgsql-event-trigger-example"> shows an example of a
event trigger procedure in <application>PL/pgSQL</application>.
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index 07f017a..99ce8f9 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -441,7 +441,7 @@ return (1, 2, 3, 4, 5)
$$ LANGUAGE plpythonu;
SELECT return_arr();
- return_arr
+ return_arr
-------------
{1,2,3,4,5}
(1 row)
@@ -872,7 +872,7 @@ $$ LANGUAGE plpythonu;
</sect2>
<sect2 id="plpython-event-trigger">
- <title>Trigger Procedures on Events in PL/Python </title>
+ <title>Trigger Procedures on Events in PL/Python</title>
<indexterm>
<primary>event trigger</primary>
@@ -881,9 +881,9 @@ $$ LANGUAGE plpythonu;
<para>
Event trigger procedures can be written in PL/Python.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -924,7 +924,7 @@ $$ LANGUAGE plpythonu;
<term><varname>TD["objectname"]</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index c9a4e4c..c932ddb 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -725,9 +725,9 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<para>
Event trigger procedures can be written in PL/Tcl.
- <productname>PostgreSQL</productname> requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of <literal>event_trigger</>.
+ <productname>PostgreSQL</productname> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of <literal>event_trigger</>.
</para>
<para>
@@ -768,7 +768,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
<term><varname>$TG_objectname</varname></term>
<listitem>
<para>
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
</para>
</listitem>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e80265..4ef1fc1 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -208,6 +208,7 @@
&extend;
&trigger;
+ &event-trigger;
&rules;
&xplang;
diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index e236921..db4727b 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>ALTER EVENT TRIGGER</refname>
- <refpurpose>change the definition of a trigger</refpurpose>
+ <refpurpose>change the definition of an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-altereventtrigger">
@@ -46,7 +46,7 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
</para>
<para>
- You must be superuser to alter a event trigger.
+ You must be superuser to alter an event trigger.
</para>
</refsect1>
@@ -93,12 +93,12 @@ ALTER EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable> RENAME TO
</variablelist>
</refsect1>
- <refsect1>
+ <refsect1 id="sql-alterventtrigger-compatibility">
<title>Compatibility</title>
<para>
- <command>ALTER EVENT TRIGGER</command> is a <productname>PostgreSQL</>
- extension of the SQL standard.
+ There is no <command>ALTER EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index af06c88..3b3d474 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>CREATE EVENT TRIGGER</refname>
- <refpurpose>define a new trigger</refpurpose>
+ <refpurpose>define a new event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-createeventtrigger">
@@ -23,100 +23,8 @@ PostgreSQL documentation
<synopsis>
CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
ON <replaceable class="PARAMETER">event</replaceable>
- [ WHEN <replaceable class="PARAMETER">variable</replaceable> IN (tag [, tag [, ...] ] ) ]
- EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable> ()
-
-<phrase>where <replaceable class="parameter">event</replaceable> can be one of:</phrase>
-
- command_start
-
-<phrase>where <replaceable class="parameter">tag</replaceable> can be one of:</phrase>
-
- ALTER AGGREGATE
- ALTER COLLATION
- ALTER CONVERSION
- ALTER DOMAIN
- ALTER EXTENSION
- ALTER FOREIGN DATA WRAPPER
- ALTER FOREIGN TABLE
- ALTER FUNCTION
- ALTER LANGUAGE
- ALTER OPERATOR
- ALTER OPERATOR CLASS
- ALTER OPERATOR FAMILY
- ALTER SCHEMA
- ALTER SEQUENCE
- ALTER SERVER
- ALTER TABLE
- ALTER TEXT SEARCH CONFIGURATION
- ALTER TEXT SEARCH DICTIONARY
- ALTER TEXT SEARCH PARSER
- ALTER TEXT SEARCH TEMPLATE
- ALTER TRIGGER
- ALTER TYPE
- ALTER USER MAPPING
- ALTER VIEW
- CLUSTER
- CREATE AGGREGATE
- CREATE CAST
- CREATE COLLATION
- CREATE CONVERSION
- CREATE DOMAIN
- CREATE EXTENSION
- CREATE FOREIGN DATA WRAPPER
- CREATE FOREIGN TABLE
- CREATE FUNCTION
- CREATE INDEX
- CREATE LANGUAGE
- CREATE OPERATOR
- CREATE OPERATOR CLASS
- CREATE OPERATOR FAMILY
- CREATE RULE
- CREATE SCHEMA
- CREATE SEQUENCE
- CREATE SERVER
- CREATE TABLE
- CREATE TABLE AS
- CREATE TEXT SEARCH CONFIGURATION
- CREATE TEXT SEARCH DICTIONARY
- CREATE TEXT SEARCH PARSER
- CREATE TEXT SEARCH TEMPLATE
- CREATE TRIGGER
- CREATE TYPE
- CREATE USER MAPPING
- CREATE VIEW
- DROP AGGREGATE
- DROP CAST
- DROP COLLATION
- DROP CONVERSION
- DROP DOMAIN
- DROP EXTENSION
- DROP FOREIGN DATA WRAPPER
- DROP FOREIGN TABLE
- DROP FUNCTION
- DROP INDEX
- DROP LANGUAGE
- DROP OPERATOR
- DROP OPERATOR CLASS
- DROP OPERATOR FAMILY
- DROP RULE
- DROP SCHEMA
- DROP SEQUENCE
- DROP SERVER
- DROP TABLE
- DROP TEXT SEARCH CONFIGURATION
- DROP TEXT SEARCH DICTIONARY
- DROP TEXT SEARCH PARSER
- DROP TEXT SEARCH TEMPLATE
- DROP TRIGGER
- DROP TYPE
- DROP USER MAPPING
- DROP VIEW
- LOAD
- REINDEX
- SELECT INTO
- VACUUM
-
+ [ WHEN <replaceable class="PARAMETER">filter_variable</replaceable> IN (filter_value [ , ... ] ) ]
+ EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable>()
</synopsis>
</refsynopsisdiv>
@@ -125,35 +33,11 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<para>
<command>CREATE EVENT TRIGGER</command> creates a new event trigger.
- The trigger will be associated with the specified event and will
- execute the specified
- function <replaceable class="parameter">function_name</replaceable> when
- that event is run.
- </para>
-
- <para>
- The command trigger gives a procedure to fire before the event is
- executed. In some cases the procedure can be fired instead of the event
- code PostgreSQL would run itself. A command trigger's function must
- return <literal>event_trigger</literal> data type. It can then only
- abort the execution of the command by raising an exception.
- </para>
-
- <para>
- If multiple triggers of the same kind are defined for the same event,
- they will be fired in alphabetical order by name.
- </para>
-
- <para>
- Note that objects dropped by the effect of <literal>DROP
- CASCADE</literal> will not result in a event trigger firing, only the
- top-level event for the main object will fire a event trigger. That
- also applies to other dependencies following, as in <literal>DROP OWNED
- BY</literal>.
- </para>
-
- <para>
- Refer to <xref linkend="triggers"> for more information about triggers.
+ Whenever the designated event occurs and the <literal>WHEN</> condition
+ associated with the trigger, if any, is satisfied, the trigger function
+ will be executed. For a general introduction to event triggers, see
+ <xref linkend="event-triggers">. The user who creates an event trigger
+ becomes its owner.
</para>
</refsect1>
@@ -165,52 +49,44 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="parameter">name</replaceable></term>
<listitem>
<para>
- The name to give the new trigger. This must be distinct from the name
- of any other trigger for the same table. The name cannot be
- schema-qualified.
+ The name to give the new trigger. This name must be unique within
+ the database.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><replaceable class="parameter">tag</replaceable></term>
+ <term><replaceable class="parameter">event</replaceable></term>
<listitem>
<para>
- The tag of the command the trigger is for. Supported commands are
- mainly those acting on database objects, plus some more facilities.
- That leaves out the following list of non supported commands.
- </para>
- <para>
- Commands that refer to global objects, such as databases, tablespaces
- and roles, are not supported.
- </para>
- <para>
- The command <literal>ALTER TYPE ... ADD VALUE ...</literal> prevents
- transaction control entirely, thus no command trigger will get fired
- when it's used.
- </para>
- <para>
- Commands that are related to transaction control (such
- as <literal>BEGIN</literal> or <literal>COMMIT</literal>), related to
- prepared plans
- (e.g. <literal>PREPARE</literal>, <literal>DEALLOCATE</literal>),
- cursors management
- (e.g. <literal>DECLARE</literal>, <literal>FETCH</literal>), setting
- variables (<literal>SET</literal>), the <literal>LISTEN</literal>
- feature, and security are not supported either.
+ The name of the event that triggers a call to the given function.
+ See <xref linkend="event-trigger-definition"> for more information
+ on event names.
</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">filter_variable</replaceable></term>
+ <listitem>
<para>
- Event triggers on <literal>CREATE EVENT
- TRIGGER</literal>, <literal>ALTER EVENT TRIGGER</literal>
- and <literal>DROP EVENT TRIGGER</literal> are not supported so as
- not to be able to take over control from a superuser.
+ The name of a variable used to filter events. This makes it possible
+ to restrict the firing of the trigger to a subset of the cases in which
+ it is supported. Currently the only suppoted
+ <replaceable class="parameter">filter_variable</replaceable>
+ is <literal>TAG</literal>.
</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">filter_value</replaceable></term>
+ <listitem>
<para>
- Triggers on <literal>ANY</literal> command support more commands than
- just this list, and will only provide the <literal>command
- tag</literal> argument as <literal>NOT NULL</literal>. Supporting more
- commands is made so that you can actually block <xref linkend="ddl">
- commands in one go.
+ A list of values for the
+ associated <replaceable class="parameter">filter_variable</replaceable>
+ for which the trigger should fire. For <literal>TAG</>, this means a
+ list of command tags (e.g. <literal>'DROP FUNCTION'</>).
</para>
</listitem>
</varlistentry>
@@ -220,7 +96,7 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
<listitem>
<para>
A user-supplied function that is declared as taking no argument and
- returning type <literal>event trigger</literal>.
+ returning type <literal>event_trigger</literal>.
</para>
<para>
If your event trigger is implemented in <literal>C</literal> then it
@@ -234,24 +110,20 @@ CREATE EVENT TRIGGER <replaceable class="PARAMETER">name</replaceable>
</variablelist>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-notes">
+ <refsect1 id="sql-createeventtrigger-notes">
<title>Notes</title>
<para>
To create a trigger on a event, the user must be superuser.
</para>
-
- <para>
- Use <xref linkend="sql-dropeventtrigger"> to remove a event trigger.
- </para>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-examples">
+ <refsect1 id="sql-createeventtrigger-examples">
<title>Examples</title>
<para>
- Forbids the execution of any command supported by the event trigger
- mechanism, which includes all commands listed above:
+ Forbid the execution of any command supported by the event trigger
+ mechanism:
<programlisting>
CREATE OR REPLACE FUNCTION abort_any_command()
@@ -291,13 +163,12 @@ $$;
</para>
</refsect1>
- <refsect1 id="SQL-CREATEEVENTTRIGGER-compatibility">
+ <refsect1 id="sql-createeventtrigger-compatibility">
<title>Compatibility</title>
<para>
- <command>CREATE EVENT TRIGGER</command> is a
- <productname>PostgreSQL</productname> extension of the <acronym>SQL</>
- standard.
+ There is no <command>CREATE EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/drop_event_trigger.sgml b/doc/src/sgml/ref/drop_event_trigger.sgml
index fc45dff..86f9628 100644
--- a/doc/src/sgml/ref/drop_event_trigger.sgml
+++ b/doc/src/sgml/ref/drop_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
<refnamediv>
<refname>DROP EVENT TRIGGER</refname>
- <refpurpose>remove a event trigger</refpurpose>
+ <refpurpose>remove an event trigger</refpurpose>
</refnamediv>
<indexterm zone="sql-dropeventtrigger">
@@ -29,8 +29,9 @@ DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceabl
<title>Description</title>
<para>
- <command>DROP EVENT TRIGGER</command> removes an existing trigger definition.
- To execute this command, the current user must be superuser.
+ <command>DROP EVENT TRIGGER</command> removes an existing event trigger.
+ To execute this command, the current user must be the owner of the event
+ trigger.
</para>
</refsect1>
@@ -79,7 +80,7 @@ DROP EVENT TRIGGER [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceabl
</variablelist>
</refsect1>
- <refsect1 id="SQL-DROPEVENTTRIGGER-examples">
+ <refsect1 id="sql-dropeventtrigger-examples">
<title>Examples</title>
<para>
@@ -90,13 +91,14 @@ DROP EVENT TRIGGER snitch;
</programlisting></para>
</refsect1>
- <refsect1 id="SQL-DROPEVENTTRIGGER-compatibility">
+ <refsect1 id="sql-dropeventtrigger-compatibility">
<title>Compatibility</title>
<para>
- The <command>DROP EVENT TRIGGER</command> statement is a
- <productname>PostgreSQL</productname> extension.
+ There is no <command>DROP EVENT TRIGGER</command> statement in the
+ SQL standard.
</para>
+
</refsect1>
<refsect1>
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 32994b9..f579340 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -27,24 +27,6 @@
plain SQL function language.
</para>
- <para>
- <productname>PostgreSQL</productname> offers both triggers on commands
- (see <xref linkend="ddl">) and triggers on data manipulation
- (see <xref linkend="dml">).
- </para>
-
- <sect1 id="command-triggers">
- <title>Overview of Event Trigger Behavior</title>
-
- <para>
- A trigger is a specification that the database should automatically
- execute a particular function whenever a certain command is performed.
- The whole set of <productname>PostgreSQL</productname> commands is not
- supported for triggers, see <xref linkend="sql-createeventtrigger">
- for details.
- </para>
- </sect1>
-
<sect1 id="trigger-definition">
<title>Overview of Trigger Behavior</title>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 6e29758..df6da1f 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -12,8 +12,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
- pg_depend.o pg_enum.o pg_event_trigger.o pg_inherits.o pg_largeobject.o \
- pg_namespace.o \
+ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
pg_type.o storage.o toasting.o
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 56c40b1..b097813 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -29,6 +29,7 @@
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
+#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
@@ -277,6 +278,10 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case ACL_KIND_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
+ case ACL_KIND_EVENT_TRIGGER:
+ elog(ERROR, "grantable rights not supported for event triggers");
+ /* not reached, but keep compiler quiet */
+ return ACL_NO_RIGHTS;
case ACL_KIND_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
@@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("permission denied for foreign server %s"),
+ /* ACL_KIND_EVENT_TRIGGER */
+ gettext_noop("permission denied for event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("permission denied for extension %s"),
};
@@ -3330,6 +3337,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
gettext_noop("must be owner of foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("must be owner of foreign server %s"),
+ /* ACL_KIND_EVENT_TRIGGER */
+ gettext_noop("must be owner of event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("must be owner of extension %s"),
};
@@ -3455,6 +3464,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_FOREIGN_SERVER:
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_EVENT_TRIGGER:
+ elog(ERROR, "grantable rights not supported for event triggers");
+ /* not reached, but keep compiler quiet */
+ return ACL_NO_RIGHTS;
case ACL_KIND_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
default:
@@ -4876,6 +4889,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
}
/*
+ * Ownership check for an event trigger (specified by OID).
+ */
+bool
+pg_event_trigger_ownercheck(Oid et_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("event trigger with OID %u does not exist",
+ et_oid)));
+
+ ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+/*
* Ownership check for a database (specified by OID).
*/
bool
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 40ae60d..5b8140b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -997,6 +997,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
NameListToString(objname));
break;
+ case OBJECT_EVENT_TRIGGER:
+ if (!pg_event_trigger_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ NameListToString(objname));
+ break;
case OBJECT_LANGUAGE:
if (!pg_language_ownercheck(address.objectId, roleid))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
@@ -1075,7 +1080,6 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
break;
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
- case OBJECT_EVENT_TRIGGER:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
diff --git a/src/backend/catalog/pg_event_trigger.c b/src/backend/catalog/pg_event_trigger.c
deleted file mode 100644
index 4995d2b..0000000
--- a/src/backend/catalog/pg_event_trigger.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * pg_event_trigger.c
- * routines to support manipulation of the pg_event_trigger relation
- *
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/catalog/pg_event_trigger.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "catalog/pg_event_trigger.h"
-#include "utils/builtins.h"
-
-char *
-event_to_string(TrigEvent event)
-{
- switch (event)
- {
- case E_CommandStart:
- return "command_start";
- }
- return NULL;
-}
-
-TrigEvent
-parse_event_name(char *event)
-{
- if (pg_strcasecmp(event, "command_start") == 0)
- return E_CommandStart;
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized event \"%s\"", event)));
-
- /* make compiler happy */
- return -1;
-}
-
-TrigEventCommand
-parse_event_tag(char *command, bool noerror)
-{
- if (pg_strcasecmp(command, "ALTER AGGREGATE") == 0)
- return E_AlterAggregate;
- else if (pg_strcasecmp(command, "ALTER COLLATION") == 0)
- return E_AlterCollation;
- else if (pg_strcasecmp(command, "ALTER CONVERSION") == 0)
- return E_AlterConversion;
- else if (pg_strcasecmp(command, "ALTER DOMAIN") == 0)
- return E_AlterDomain;
- else if (pg_strcasecmp(command, "ALTER EXTENSION") == 0)
- return E_AlterExtension;
- else if (pg_strcasecmp(command, "ALTER FOREIGN DATA WRAPPER") == 0)
- return E_AlterForeignDataWrapper;
- else if (pg_strcasecmp(command, "ALTER FOREIGN TABLE") == 0)
- return E_AlterForeignTable;
- else if (pg_strcasecmp(command, "ALTER FUNCTION") == 0)
- return E_AlterFunction;
- else if (pg_strcasecmp(command, "ALTER LANGUAGE") == 0)
- return E_AlterLanguage;
- else if (pg_strcasecmp(command, "ALTER OPERATOR") == 0)
- return E_AlterOperator;
- else if (pg_strcasecmp(command, "ALTER OPERATOR CLASS") == 0)
- return E_AlterOperatorClass;
- else if (pg_strcasecmp(command, "ALTER OPERATOR FAMILY") == 0)
- return E_AlterOperatorFamily;
- else if (pg_strcasecmp(command, "ALTER SEQUENCE") == 0)
- return E_AlterSequence;
- else if (pg_strcasecmp(command, "ALTER SERVER") == 0)
- return E_AlterServer;
- else if (pg_strcasecmp(command, "ALTER SCHEMA") == 0)
- return E_AlterSchema;
- else if (pg_strcasecmp(command, "ALTER TABLE") == 0)
- return E_AlterTable;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH CONFIGURATION") == 0)
- return E_AlterTextSearchConfiguration;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH DICTIONARY") == 0)
- return E_AlterTextSearchDictionary;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH PARSER") == 0)
- return E_AlterTextSearchParser;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH TEMPLATE") == 0)
- return E_AlterTextSearchTemplate;
- else if (pg_strcasecmp(command, "ALTER TRIGGER") == 0)
- return E_AlterTrigger;
- else if (pg_strcasecmp(command, "ALTER TYPE") == 0)
- return E_AlterType;
- else if (pg_strcasecmp(command, "ALTER USER MAPPING") == 0)
- return E_AlterUserMapping;
- else if (pg_strcasecmp(command, "ALTER VIEW") == 0)
- return E_AlterView;
- else if (pg_strcasecmp(command, "CLUSTER") == 0)
- return E_Cluster;
- else if (pg_strcasecmp(command, "CREATE AGGREGATE") == 0)
- return E_CreateAggregate;
- else if (pg_strcasecmp(command, "CREATE CAST") == 0)
- return E_CreateCast;
- else if (pg_strcasecmp(command, "CREATE COLLATION") == 0)
- return E_CreateCollation;
- else if (pg_strcasecmp(command, "CREATE CONVERSION") == 0)
- return E_CreateConversion;
- else if (pg_strcasecmp(command, "CREATE DOMAIN") == 0)
- return E_CreateDomain;
- else if (pg_strcasecmp(command, "CREATE EXTENSION") == 0)
- return E_CreateExtension;
- else if (pg_strcasecmp(command, "CREATE FOREIGN DATA WRAPPER") == 0)
- return E_CreateForeignDataWrapper;
- else if (pg_strcasecmp(command, "CREATE FOREIGN TABLE") == 0)
- return E_CreateForeignTable;
- else if (pg_strcasecmp(command, "CREATE FUNCTION") == 0)
- return E_CreateFunction;
- else if (pg_strcasecmp(command, "CREATE INDEX") == 0)
- return E_CreateIndex;
- else if (pg_strcasecmp(command, "CREATE LANGUAGE") == 0)
- return E_CreateLanguage;
- else if (pg_strcasecmp(command, "CREATE OPERATOR") == 0)
- return E_CreateOperator;
- else if (pg_strcasecmp(command, "CREATE OPERATOR CLASS") == 0)
- return E_CreateOperatorClass;
- else if (pg_strcasecmp(command, "CREATE OPERATOR FAMILY") == 0)
- return E_CreateOperatorFamily;
- else if (pg_strcasecmp(command, "CREATE RULE") == 0)
- return E_CreateRule;
- else if (pg_strcasecmp(command, "CREATE SEQUENCE") == 0)
- return E_CreateSequence;
- else if (pg_strcasecmp(command, "CREATE SERVER") == 0)
- return E_CreateServer;
- else if (pg_strcasecmp(command, "CREATE SCHEMA") == 0)
- return E_CreateSchema;
- else if (pg_strcasecmp(command, "CREATE TABLE") == 0)
- return E_CreateTable;
- else if (pg_strcasecmp(command, "CREATE TABLE AS") == 0)
- return E_CreateTableAs;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH CONFIGURATION") == 0)
- return E_CreateTextSearchConfiguration;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH DICTIONARY") == 0)
- return E_CreateTextSearchDictionary;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH PARSER") == 0)
- return E_CreateTextSearchParser;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH TEMPLATE") == 0)
- return E_CreateTextSearchTemplate;
- else if (pg_strcasecmp(command, "CREATE TRIGGER") == 0)
- return E_CreateTrigger;
- else if (pg_strcasecmp(command, "CREATE TYPE") == 0)
- return E_CreateType;
- else if (pg_strcasecmp(command, "CREATE USER MAPPING") == 0)
- return E_CreateUserMapping;
- else if (pg_strcasecmp(command, "CREATE VIEW") == 0)
- return E_CreateView;
- else if (pg_strcasecmp(command, "DROP AGGREGATE") == 0)
- return E_DropAggregate;
- else if (pg_strcasecmp(command, "DROP CAST") == 0)
- return E_DropCast;
- else if (pg_strcasecmp(command, "DROP COLLATION") == 0)
- return E_DropCollation;
- else if (pg_strcasecmp(command, "DROP CONVERSION") == 0)
- return E_DropConversion;
- else if (pg_strcasecmp(command, "DROP DOMAIN") == 0)
- return E_DropDomain;
- else if (pg_strcasecmp(command, "DROP EXTENSION") == 0)
- return E_DropExtension;
- else if (pg_strcasecmp(command, "DROP FOREIGN DATA WRAPPER") == 0)
- return E_DropForeignDataWrapper;
- else if (pg_strcasecmp(command, "DROP FOREIGN TABLE") == 0)
- return E_DropForeignTable;
- else if (pg_strcasecmp(command, "DROP FUNCTION") == 0)
- return E_DropFunction;
- else if (pg_strcasecmp(command, "DROP INDEX") == 0)
- return E_DropIndex;
- else if (pg_strcasecmp(command, "DROP LANGUAGE") == 0)
- return E_DropLanguage;
- else if (pg_strcasecmp(command, "DROP OPERATOR") == 0)
- return E_DropOperator;
- else if (pg_strcasecmp(command, "DROP OPERATOR CLASS") == 0)
- return E_DropOperatorClass;
- else if (pg_strcasecmp(command, "DROP OPERATOR FAMILY") == 0)
- return E_DropOperatorFamily;
- else if (pg_strcasecmp(command, "DROP RULE") == 0)
- return E_DropRule;
- else if (pg_strcasecmp(command, "DROP SCHEMA") == 0)
- return E_DropSchema;
- else if (pg_strcasecmp(command, "DROP SEQUENCE") == 0)
- return E_DropSequence;
- else if (pg_strcasecmp(command, "DROP SERVER") == 0)
- return E_DropServer;
- else if (pg_strcasecmp(command, "DROP TABLE") == 0)
- return E_DropTable;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH CONFIGURATION") == 0)
- return E_DropTextSearchConfiguration;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH DICTIONARY") == 0)
- return E_DropTextSearchDictionary;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH PARSER") == 0)
- return E_DropTextSearchParser;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH TEMPLATE") == 0)
- return E_DropTextSearchTemplate;
- else if (pg_strcasecmp(command, "DROP TRIGGER") == 0)
- return E_DropTrigger;
- else if (pg_strcasecmp(command, "DROP TYPE") == 0)
- return E_DropType;
- else if (pg_strcasecmp(command, "DROP USER MAPPING") == 0)
- return E_DropUserMapping;
- else if (pg_strcasecmp(command, "DROP VIEW") == 0)
- return E_DropView;
- else if (pg_strcasecmp(command, "LOAD") == 0)
- return E_Load;
- else if (pg_strcasecmp(command, "REINDEX") == 0)
- return E_Reindex;
- else if (pg_strcasecmp(command, "SELECT INTO") == 0)
- return E_SelectInto;
- else if (pg_strcasecmp(command, "VACUUM") == 0)
- return E_Vacuum;
- else
- {
- if (!noerror)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized command \"%s\"", command)));
- }
- return E_UNKNOWN;
-}
-
-char *
-command_to_string(TrigEventCommand command)
-{
- switch (command)
- {
- case E_UNKNOWN:
- return "UNKNOWN";
- case E_ANY:
- return "ANY";
- case E_AlterCast:
- return "ALTER CAST";
- case E_AlterIndex:
- return "ALTER INDEX";
- case E_AlterAggregate:
- return "ALTER AGGREGATE";
- case E_AlterCollation:
- return "ALTER COLLATION";
- case E_AlterConversion:
- return "ALTER CONVERSION";
- case E_AlterDomain:
- return "ALTER DOMAIN";
- case E_AlterExtension:
- return "ALTER EXTENSION";
- case E_AlterForeignDataWrapper:
- return "ALTER FOREIGN DATA WRAPPER";
- case E_AlterForeignTable:
- return "ALTER FOREIGN TABLE";
- case E_AlterFunction:
- return "ALTER FUNCTION";
- case E_AlterLanguage:
- return "ALTER LANGUAGE";
- case E_AlterOperator:
- return "ALTER OPERATOR";
- case E_AlterOperatorClass:
- return "ALTER OPERATOR CLASS";
- case E_AlterOperatorFamily:
- return "ALTER OPERATOR FAMILY";
- case E_AlterSequence:
- return "ALTER SEQUENCE";
- case E_AlterServer:
- return "ALTER SERVER";
- case E_AlterSchema:
- return "ALTER SCHEMA";
- case E_AlterTable:
- return "ALTER TABLE";
- case E_AlterTextSearchConfiguration:
- return "ALTER TEXT SEARCH CONFIGURATION";
- case E_AlterTextSearchDictionary:
- return "ALTER TEXT SEARCH DICTIONARY";
- case E_AlterTextSearchParser:
- return "ALTER TEXT SEARCH PARSER";
- case E_AlterTextSearchTemplate:
- return "ALTER TEXT SEARCH TEMPLATE";
- case E_AlterTrigger:
- return "ALTER TRIGGER";
- case E_AlterType:
- return "ALTER TYPE";
- case E_AlterUserMapping:
- return "ALTER USER MAPPING";
- case E_AlterView:
- return "ALTER VIEW";
- case E_Cluster:
- return "CLUSTER";
- case E_CreateAggregate:
- return "CREATE AGGREGATE";
- case E_CreateCast:
- return "CREATE CAST";
- case E_CreateCollation:
- return "CREATE COLLATION";
- case E_CreateConversion:
- return "CREATE CONVERSION";
- case E_CreateDomain:
- return "CREATE DOMAIN";
- case E_CreateExtension:
- return "CREATE EXTENSION";
- case E_CreateForeignDataWrapper:
- return "CREATE FOREIGN DATA WRAPPER";
- case E_CreateForeignTable:
- return "CREATE FOREIGN TABLE";
- case E_CreateFunction:
- return "CREATE FUNCTION";
- case E_CreateIndex:
- return "CREATE INDEX";
- case E_CreateLanguage:
- return "CREATE LANGUAGE";
- case E_CreateOperator:
- return "CREATE OPERATOR";
- case E_CreateOperatorClass:
- return "CREATE OPERATOR CLASS";
- case E_CreateOperatorFamily:
- return "CREATE OPERATOR FAMILY";
- case E_CreateRule:
- return "CREATE RULE";
- case E_CreateSequence:
- return "CREATE SEQUENCE";
- case E_CreateServer:
- return "CREATE SERVER";
- case E_CreateSchema:
- return "CREATE SCHEMA";
- case E_CreateTable:
- return "CREATE TABLE";
- case E_CreateTableAs:
- return "CREATE TABLE AS";
- case E_CreateTextSearchConfiguration:
- return "CREATE TEXT SEARCH CONFIGURATION";
- case E_CreateTextSearchDictionary:
- return "CREATE TEXT SEARCH DICTIONARY";
- case E_CreateTextSearchParser:
- return "CREATE TEXT SEARCH PARSER";
- case E_CreateTextSearchTemplate:
- return "CREATE TEXT SEARCH TEMPLATE";
- case E_CreateTrigger:
- return "CREATE TRIGGER";
- case E_CreateType:
- return "CREATE TYPE";
- case E_CreateUserMapping:
- return "CREATE USER MAPPING";
- case E_CreateView:
- return "CREATE VIEW";
- case E_DropAggregate:
- return "DROP AGGREGATE";
- case E_DropCast:
- return "DROP CAST";
- case E_DropCollation:
- return "DROP COLLATION";
- case E_DropConversion:
- return "DROP CONVERSION";
- case E_DropDomain:
- return "DROP DOMAIN";
- case E_DropExtension:
- return "DROP EXTENSION";
- case E_DropForeignDataWrapper:
- return "DROP FOREIGN DATA WRAPPER";
- case E_DropForeignTable:
- return "DROP FOREIGN TABLE";
- case E_DropFunction:
- return "DROP FUNCTION";
- case E_DropIndex:
- return "DROP INDEX";
- case E_DropLanguage:
- return "DROP LANGUAGE";
- case E_DropOperator:
- return "DROP OPERATOR";
- case E_DropOperatorClass:
- return "DROP OPERATOR CLASS";
- case E_DropOperatorFamily:
- return "DROP OPERATOR FAMILY";
- case E_DropRule:
- return "DROP RULE";
- case E_DropSchema:
- return "DROP SCHEMA";
- case E_DropSequence:
- return "DROP SEQUENCE";
- case E_DropServer:
- return "DROP SERVER";
- case E_DropTable:
- return "DROP TABLE";
- case E_DropTextSearchConfiguration:
- return "DROP TEXT SEARCH CONFIGURATION";
- case E_DropTextSearchDictionary:
- return "DROP TEXT SEARCH DICTIONARY";
- case E_DropTextSearchParser:
- return "DROP TEXT SEARCH PARSER";
- case E_DropTextSearchTemplate:
- return "DROP TEXT SEARCH TEMPLATE";
- case E_DropTrigger:
- return "DROP TRIGGER";
- case E_DropType:
- return "DROP TYPE";
- case E_DropUserMapping:
- return "DROP USER MAPPING";
- case E_DropView:
- return "DROP VIEW";
- case E_Load:
- return "LOAD";
- case E_Reindex:
- return "REINDEX";
- case E_SelectInto:
- return "SELECT INTO";
- case E_Vacuum:
- return "VACUUM";
- }
- return NULL;
-}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 78ef831..19f9895 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -70,10 +70,6 @@ ExecRenameStmt(RenameStmt *stmt)
RenameDatabase(stmt->subname, stmt->newname);
break;
- case OBJECT_EVENT_TRIGGER:
- RenameEventTrigger(stmt->subname, stmt->newname);
- break;
-
case OBJECT_FDW:
RenameForeignDataWrapper(stmt->subname, stmt->newname);
break;
@@ -82,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt)
RenameForeignServer(stmt->subname, stmt->newname);
break;
+ case OBJECT_EVENT_TRIGGER:
+ RenameEventTrigger(stmt->subname, stmt->newname);
+ break;
+
case OBJECT_FUNCTION:
RenameFunction(stmt->object, stmt->objarg, stmt->newname);
break;
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 1b8529e..8f5d7e0 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
args = NameListToString(list_truncate(objname,
list_length(objname) - 1));
break;
+ case OBJECT_EVENT_TRIGGER:
+ msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
+ name = NameListToString(objname);
+ break;
case OBJECT_RULE:
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
name = strVal(llast(objname));
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4ea2848..502e34b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -32,7 +32,6 @@
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
-#include "utils/evtcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -49,23 +48,6 @@ static void AlterEventTriggerOwner_internal(Relation rel,
Oid newOwnerId);
/*
- * Check permission: command triggers are only available for superusers. Raise
- * an exception when requirements are not fullfilled.
- *
- * It's not clear how to accept that database owners be able to create command
- * triggers, a superuser could run a command that fires a trigger's procedure
- * written by the database owner and now running with superuser privileges.
- */
-static void
-CheckEventTriggerPrivileges()
-{
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to use command triggers"))));
-}
-
-/*
* Insert Command Trigger Tuple
*
* Insert the new pg_event_trigger row, and return the OID assigned to the new
@@ -111,7 +93,7 @@ InsertEventTriggerTuple(char *trigname, TrigEvent event, Oid evtOwner,
{
TrigEventCommand cmd = lfirst_int(lc);
char *cmdstr = command_to_string(cmd);
- if (cmd == E_UNKNOWN || cmdstr == NULL)
+ if (cmd == ETC_UNKNOWN || cmdstr == NULL)
elog(ERROR, "Unknown command %d", cmd);
tags[i++] = PointerGetDatum(cstring_to_text(cmdstr));
}
@@ -158,7 +140,16 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
Oid funcrettype;
Oid evtowner = GetUserId();
- CheckEventTriggerPrivileges();
+ /*
+ * It would be nice to allow database owners or even regular users to do
+ * this, but there are obvious privilege escalation risks which would have
+ * to somehow be plugged first.
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errmsg("permission denied to create event trigger \"%s\"",
+ stmt->trigname),
+ errhint("Must be superuser to create an event trigger.")));
/*
* Find and validate the trigger function.
@@ -171,7 +162,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
if (funcrettype != EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("function \"%s\" must return type \"command_trigger\"",
+ errmsg("function \"%s\" must return type \"event_trigger\"",
NameListToString(stmt->funcname))));
/*
@@ -214,7 +205,7 @@ RemoveEventTriggerById(Oid trigOid)
}
/*
- * ALTER EVENT TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
+ * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
*/
void
AlterEventTrigger(AlterEventTrigStmt *stmt)
@@ -224,15 +215,18 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
Form_pg_event_trigger evtForm;
char tgenabled = stmt->tgenabled;
- CheckEventTriggerPrivileges();
-
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
- tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
+ tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
+ CStringGetDatum(stmt->trigname));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("event trigger \"%s\" does not exist", stmt->trigname)));
+ errmsg("event trigger \"%s\" does not exist",
+ stmt->trigname)));
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ stmt->trigname);
/* tuple is a copy, so we can modify it below */
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
@@ -248,7 +242,7 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
/*
- * Rename command trigger
+ * Rename event trigger
*/
void
RenameEventTrigger(const char *trigname, const char *newname)
@@ -257,8 +251,6 @@ RenameEventTrigger(const char *trigname, const char *newname)
Relation rel;
Form_pg_event_trigger evtForm;
- CheckEventTriggerPrivileges();
-
rel = heap_open(EventTriggerRelationId, RowExclusiveLock);
/* newname must be available */
@@ -273,6 +265,9 @@ RenameEventTrigger(const char *trigname, const char *newname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("event trigger \"%s\" does not exist", trigname)));
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ trigname);
evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
@@ -344,22 +339,31 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_event_trigger form;
- CheckEventTriggerPrivileges();
-
form = (Form_pg_event_trigger) GETSTRUCT(tup);
- if (form->evtowner != newOwnerId)
- {
- form->evtowner = newOwnerId;
+ if (form->evtowner == newOwnerId)
+ return;
- simple_heap_update(rel, &tup->t_self, tup);
- CatalogUpdateIndexes(rel, tup);
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ NameStr(form->evtname));
- /* Update owner dependency reference */
- changeDependencyOnOwner(EventTriggerRelationId,
- HeapTupleGetOid(tup),
- newOwnerId);
- }
+ /* New owner must be a superuser */
+ if (!superuser_arg(newOwnerId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to change owner of event trigger \"%s\"",
+ NameStr(form->evtname)),
+ errhint("The owner of an event trigger must be a superuser.")));
+
+ form->evtowner = newOwnerId;
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(EventTriggerRelationId,
+ HeapTupleGetOid(tup),
+ newOwnerId);
}
/*
@@ -382,9 +386,9 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
}
/*
- * Functions to execute the command triggers.
+ * Functions to execute the event triggers.
*
- * We call the functions that matches the command triggers definitions in
+ * We call the functions that matches the event triggers definitions in
* alphabetical order, and give them those arguments:
*
* toplevel command tag, text
@@ -394,7 +398,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* objectname, text
*
* Those are passed down as special "context" magic variables and need specific
- * support in each PL that wants to support command triggers. All core PL do.
+ * support in each PL that wants to support event triggers. All core PL do.
*/
static void
@@ -409,17 +413,27 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
fmgr_info(proc, &flinfo);
/*
- * Prepare the command trigger function context from the Command Context.
+ * Prepare the event trigger function context from the Command Context.
* We prepare a dedicated Node here so as not to publish internal data.
*/
- trigdata.type = T_EventTriggerData;
- trigdata.toplevel = ev_ctx->toplevel;
- trigdata.tag = ev_ctx->tag;
- trigdata.objectId = ev_ctx->objectId;
+ trigdata.type = T_EventTriggerData;
+ trigdata.event = pstrdup(event_to_string(tev));
+ trigdata.toplevel = ev_ctx->toplevel;
+ trigdata.tag = ev_ctx->tag;
+ trigdata.objectId = ev_ctx->objectId;
trigdata.schemaname = ev_ctx->schemaname;
trigdata.objectname = ev_ctx->objectname;
- trigdata.parsetree = ev_ctx->parsetree;
- trigdata.when = pstrdup(event_to_string(tev));
+ trigdata.parsetree = ev_ctx->parsetree;
+
+ if (ev_ctx->operation == NULL)
+ trigdata.operation = NULL;
+ else
+ trigdata.operation = pstrdup(ev_ctx->operation);
+
+ if (ev_ctx->objecttype == -1)
+ trigdata.objecttype = NULL;
+ else
+ trigdata.objecttype = pstrdup(objecttype_to_string(ev_ctx->objecttype));
/*
* Call the function, passing no arguments but setting a context.
@@ -436,599 +450,55 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
/*
* Routine to call to setup a EventContextData evt.
+ *
+ * The fields 'objecttype' must be set before calling other entry points. The
+ * fields 'operation', 'objectId', 'objectname' and 'schemaname' might be set
+ * to interesting values.
*/
void
InitEventContext(EventContext evt, const Node *parsetree)
{
- evt->command = E_UNKNOWN;
- evt->toplevel = NULL;
- evt->tag = (char *) CreateCommandTag((Node *)parsetree);
- evt->parsetree = (Node *)parsetree;
- evt->objectId = InvalidOid;
+ evt->command = ETC_UNSET;
+ evt->toplevel = NULL;
+ evt->tag = (char *) CreateCommandTag((Node *)parsetree);
+ evt->parsetree = (Node *)parsetree;
+ evt->operation = NULL;
+ evt->objecttype = -1;
+ evt->objectId = InvalidOid;
evt->objectname = NULL;
evt->schemaname = NULL;
- /*
- * Fill in the event command, which is an enum constant to match against
- * what's stored into catalogs. As we are storing that on disk, we need the
- * enum values to be stable, see src/include/catalog/pg_event_trigger.h for
- * details.
- */
- switch (nodeTag(parsetree))
- {
- case T_CreateSchemaStmt:
- evt->command = E_CreateSchema;
- break;
-
- case T_CreateStmt:
- evt->command = E_CreateTable;
- break;
-
- case T_CreateForeignTableStmt:
- evt->command = E_CreateForeignTable;
- break;
-
- case T_CreateExtensionStmt:
- evt->command = E_CreateExtension;
- break;
-
- case T_AlterExtensionStmt:
- case T_AlterExtensionContentsStmt:
- evt->command = E_AlterExtension;
- break;
-
- case T_CreateFdwStmt:
- evt->command = E_CreateForeignDataWrapper;
- break;
-
- case T_AlterFdwStmt:
- evt->command = E_AlterForeignDataWrapper;
- break;
-
- case T_CreateForeignServerStmt:
- evt->command = E_CreateServer;
- break;
-
- case T_AlterForeignServerStmt:
- evt->command = E_AlterServer;
- break;
-
- case T_CreateUserMappingStmt:
- evt->command = E_CreateUserMapping;
- break;
-
- case T_AlterUserMappingStmt:
- evt->command = E_AlterUserMapping;
- break;
-
- case T_DropUserMappingStmt:
- evt->command = E_DropUserMapping;
- break;
-
- case T_DropStmt:
- switch (((DropStmt *) parsetree)->removeType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_DropAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_DropCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_DropCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_DropConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_DropDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_DropExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_DropForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_DropServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_DropForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_DropFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_DropIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_DropLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_DropOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_DropOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_DropOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_DropSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_DropSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_DropTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_DropTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_DropTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_DropTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_DropTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_DropTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_DropType;
- break;
- case OBJECT_VIEW:
- evt->command = E_DropView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_RenameStmt:
- switch (((RenameStmt *) parsetree)->renameType)
- {
- case OBJECT_ATTRIBUTE:
- evt->command = E_AlterType;
- break;
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_COLUMN:
- evt->command = E_AlterTable;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterObjectSchemaStmt:
- switch (((AlterObjectSchemaStmt *) parsetree)->objectType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterOwnerStmt:
- switch (((AlterOwnerStmt *) parsetree)->objectType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterTableStmt:
- evt->command = E_AlterTable;
- break;
-
- case T_AlterDomainStmt:
- evt->command = E_AlterDomain;
- break;
-
- case T_DefineStmt:
- switch (((DefineStmt *) parsetree)->kind)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_CreateAggregate;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_CreateOperator;
- break;
- case OBJECT_TYPE:
- evt->command = E_CreateType;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_CreateTextSearchParser;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_CreateTextSearchDictionary;;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_CreateTextSearchTemplate;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_CreateTextSearchConfiguration;
- break;
- case OBJECT_COLLATION:
- evt->command = E_CreateCollation;
- break;
- default:
- elog(ERROR, "unrecognized define stmt type: %d",
- (int) ((DefineStmt *) parsetree)->kind);
- break;
- }
- break;
-
- case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
- case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
- case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
- evt->command = E_CreateType;
- break;
-
- case T_AlterEnumStmt: /* ALTER TYPE (enum) */
- evt->command = E_AlterType;
- break;
-
- case T_ViewStmt: /* CREATE VIEW */
- evt->command = E_CreateView;
- break;
-
- case T_CreateFunctionStmt: /* CREATE FUNCTION */
- evt->command = E_CreateFunction;
- break;
-
- case T_AlterFunctionStmt: /* ALTER FUNCTION */
- evt->command = E_AlterFunction;
- break;
-
- case T_IndexStmt: /* CREATE INDEX */
- evt->command = E_CreateIndex;
- break;
-
- case T_CreateSeqStmt:
- evt->command = E_CreateSequence;
- break;
-
- case T_AlterSeqStmt:
- evt->command = E_AlterSequence;
- break;
-
- case T_LoadStmt:
- evt->command = E_Load;
- break;
-
- case T_ClusterStmt:
- evt->command = E_Cluster;
- break;
-
- case T_VacuumStmt:
- evt->command = E_Vacuum;
- break;
-
- case T_CreateTableAsStmt:
- evt->command = E_CreateTableAs;
- break;
-
- case T_CreateTrigStmt:
- evt->command = E_CreateTrigger;
- break;
-
- case T_CreateDomainStmt:
- evt->command = E_CreateDomain;
- break;
-
- case T_ReindexStmt:
- evt->command = E_Reindex;
- break;
-
- case T_CreateConversionStmt:
- evt->command = E_CreateConversion;
- break;
-
- case T_CreateCastStmt:
- evt->command = E_CreateCast;
- break;
-
- case T_CreateOpClassStmt:
- evt->command = E_CreateOperatorClass;
- break;
-
- case T_CreateOpFamilyStmt:
- evt->command = E_CreateOperatorFamily;
- break;
-
- case T_AlterOpFamilyStmt:
- evt->command = E_AlterOperatorFamily;
- break;
-
- case T_AlterTSDictionaryStmt:
- evt->command = E_AlterTextSearchDictionary;
- break;
-
- case T_AlterTSConfigurationStmt:
- evt->command = E_AlterTextSearchConfiguration;
- break;
-
- default:
- /* reaching that part of the code only means that we are not
- * supporting command triggers for the given command, which still
- * needs to execute.
- */
- break;
- }
+ /* guess the ongoing operation from the command tag */
+ if (strncmp(evt->tag, "CREATE ", 7) == 0)
+ evt->operation = pstrdup("CREATE");
+ else if (strncmp(evt->tag, "DROP ", 5) == 0)
+ evt->operation = pstrdup("DROP");
+ else if (strncmp(evt->tag, "ALTER ", 6) == 0)
+ evt->operation = pstrdup("ALTER");
}
/*
- * InitEventContext() must have been called first. When
- * CommandFiresTriggersForEvent() returns false, the EventContext structure
- * needs not be initialized further.
+ * InitEventContext() must have been called first, then the event context field
+ * 'objectype' must have been "manually" for command tags supporting several
+ * kinds of object, such as T_DropStmt, T_RenameStmt, T_AlterObjectSchemaStmt,
+ * T_AlterOwnerStmt or T_DefineStmt.
+ *
+ * When CommandFiresTriggersForEvent() returns false, the EventContext
+ * structure needs not be initialized further.
*/
bool
CommandFiresTriggersForEvent(EventContext ev_ctx, TrigEvent tev)
{
EventCommandTriggers *triggers;
- if (ev_ctx == NULL || ev_ctx->command == E_UNKNOWN)
+ if (ev_ctx == NULL)
+ return false;
+
+ if (ev_ctx->command == ETC_UNSET)
+ ev_ctx->command = get_command_from_nodetag(nodeTag(ev_ctx->parsetree),
+ ev_ctx->objecttype, true);
+
+ if (ev_ctx->command == ETC_UNKNOWN)
return false;
triggers = get_event_triggers(tev, ev_ctx->command);
@@ -1037,7 +507,7 @@ CommandFiresTriggersForEvent(EventContext ev_ctx, TrigEvent tev)
}
/*
- * Actually run command triggers of a specific command. We first run ANY
+ * Actually run event triggers for a specific command. We first run ANY
* command triggers.
*/
void
@@ -1046,7 +516,14 @@ ExecEventTriggers(EventContext ev_ctx, TrigEvent tev)
EventCommandTriggers *triggers;
ListCell *lc;
- if (ev_ctx == NULL || ev_ctx->command == E_UNKNOWN)
+ if (ev_ctx == NULL)
+ return;
+
+ if (ev_ctx->command == ETC_UNSET)
+ ev_ctx->command = get_command_from_nodetag(nodeTag(ev_ctx->parsetree),
+ ev_ctx->objecttype, true);
+
+ if (ev_ctx->command == ETC_UNKNOWN)
return;
triggers = get_event_triggers(tev, ev_ctx->command);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ec5c8f8..f3ab8e9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -214,7 +214,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
- DropAssertStmt DropTrigStmt DropEventTrigStmt DropRuleStmt DropCastStmt
+ DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
@@ -755,7 +755,6 @@ stmt :
| DropStmt
| DropTableSpaceStmt
| DropTrigStmt
- | DropEventTrigStmt
| DropRoleStmt
| DropUserStmt
| DropUserMappingStmt
@@ -4368,7 +4367,7 @@ trigger_command:
SCONST
{
TrigEventCommand cmdtag = parse_event_tag($1, true);
- if (cmdtag == E_UNKNOWN)
+ if (cmdtag == ETC_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized command \"%s\"", $1),
@@ -4378,27 +4377,6 @@ trigger_command:
;
-DropEventTrigStmt:
- DROP EVENT TRIGGER name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($4)));
- n->behavior = $5;
- n->missing_ok = false;
- $$ = (Node *) n;
- }
- | DROP EVENT TRIGGER IF_P EXISTS name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($6)));
- n->behavior = $7;
- n->missing_ok = true;
- $$ = (Node *) n;
- }
- ;
-
AlterEventTrigStmt:
ALTER EVENT TRIGGER name enable_trigger
{
@@ -5002,6 +4980,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
+ | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
| TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
| COLLATION { $$ = OBJECT_COLLATION; }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 29eec11..4e1a8c0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
@@ -355,12 +356,7 @@ standard_ProcessUtility(Node *parsetree,
completionTag[0] = '\0';
/* Event Trigger support for command_start */
- InitEventContext(&evt, (Node *)parsetree);
-
- if (CommandFiresTriggersForEvent(&evt, E_CommandStart))
- {
- ExecEventTriggers(&evt, E_CommandStart);
- }
+ InitEventContext(&evt, parsetree);
switch (nodeTag(parsetree))
{
@@ -513,6 +509,7 @@ standard_ProcessUtility(Node *parsetree,
* relation and attribute manipulation
*/
case T_CreateSchemaStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
queryString);
break;
@@ -525,6 +522,9 @@ standard_ProcessUtility(Node *parsetree,
Oid relOid = InvalidOid;
CreateStmt *stmt = (CreateStmt *) parsetree;
+ /* possibly run event triggers */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/* Run parse analysis ... */
stmts = transformCreateStmt(stmt, queryString);
@@ -589,59 +589,75 @@ standard_ProcessUtility(Node *parsetree,
case T_CreateTableSpaceStmt:
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
break;
case T_DropTableSpaceStmt:
PreventTransactionChain(isTopLevel, "DROP TABLESPACE");
+ ExecEventTriggers(&evt, EVT_CommandStart);
DropTableSpace((DropTableSpaceStmt *) parsetree);
break;
case T_AlterTableSpaceOptionsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
break;
case T_CreateExtensionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateExtension((CreateExtensionStmt *) parsetree);
break;
case T_AlterExtensionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
break;
case T_AlterExtensionContentsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
break;
case T_CreateFdwStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
break;
case T_AlterFdwStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
break;
case T_CreateForeignServerStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateForeignServer((CreateForeignServerStmt *) parsetree);
break;
case T_AlterForeignServerStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterForeignServer((AlterForeignServerStmt *) parsetree);
break;
case T_CreateUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateUserMapping((CreateUserMappingStmt *) parsetree);
break;
case T_AlterUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterUserMapping((AlterUserMappingStmt *) parsetree);
break;
case T_DropUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
RemoveUserMapping((DropUserMappingStmt *) parsetree);
break;
case T_DropStmt:
+ evt.objecttype = ((DropStmt *) parsetree)->removeType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_INDEX:
@@ -705,14 +721,20 @@ standard_ProcessUtility(Node *parsetree,
* schema
*/
case T_RenameStmt:
+ evt.objecttype = ((RenameStmt *) parsetree)->renameType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecRenameStmt((RenameStmt *) parsetree);
break;
case T_AlterObjectSchemaStmt:
+ evt.objecttype = ((AlterObjectSchemaStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
break;
case T_AlterOwnerStmt:
+ evt.objecttype = ((AlterOwnerStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
break;
@@ -724,6 +746,9 @@ standard_ProcessUtility(Node *parsetree,
ListCell *l;
LOCKMODE lockmode;
+ /* run command_start event triggers, if any */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/*
* Figure out lock mode, and acquire lock. This also does
* basic permissions checks, so that we won't wait for a lock
@@ -775,6 +800,9 @@ standard_ProcessUtility(Node *parsetree,
{
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
+ /* run command_start event triggers, if any */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/*
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
@@ -839,6 +867,9 @@ standard_ProcessUtility(Node *parsetree,
{
DefineStmt *stmt = (DefineStmt *) parsetree;
+ evt.objecttype = stmt->kind;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
switch (stmt->kind)
{
case OBJECT_AGGREGATE:
@@ -885,15 +916,18 @@ standard_ProcessUtility(Node *parsetree,
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineCompositeType(stmt->typevar, stmt->coldeflist);
}
break;
case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineEnum((CreateEnumStmt *) parsetree);
break;
case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineRange((CreateRangeStmt *) parsetree);
break;
@@ -905,18 +939,22 @@ standard_ProcessUtility(Node *parsetree,
* defining pg_enum entries go away.
*/
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterEnum((AlterEnumStmt *) parsetree);
break;
case T_ViewStmt: /* CREATE VIEW */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineView((ViewStmt *) parsetree, queryString);
break;
case T_CreateFunctionStmt: /* CREATE FUNCTION */
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateFunction((CreateFunctionStmt *) parsetree, queryString);
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterFunction((AlterFunctionStmt *) parsetree);
break;
@@ -924,6 +962,8 @@ standard_ProcessUtility(Node *parsetree,
{
IndexStmt *stmt = (IndexStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
if (stmt->concurrent)
PreventTransactionChain(isTopLevel,
"CREATE INDEX CONCURRENTLY");
@@ -958,14 +998,17 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_RuleStmt: /* CREATE RULE */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineRule((RuleStmt *) parsetree, queryString);
break;
case T_CreateSeqStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineSequence((CreateSeqStmt *) parsetree);
break;
case T_AlterSeqStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterSequence((AlterSeqStmt *) parsetree);
break;
@@ -1032,6 +1075,8 @@ standard_ProcessUtility(Node *parsetree,
{
LoadStmt *stmt = (LoadStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
closeAllVfds(); /* probably not necessary... */
/* Allowed names are restricted if you're not superuser */
load_file(stmt->filename, !superuser());
@@ -1040,12 +1085,14 @@ standard_ProcessUtility(Node *parsetree,
case T_ClusterStmt:
/* we choose to allow this during "read only" transactions */
+ ExecEventTriggers(&evt, EVT_CommandStart);
PreventCommandDuringRecovery("CLUSTER");
cluster((ClusterStmt *) parsetree, isTopLevel);
break;
case T_VacuumStmt:
/* we choose to allow this during "read only" transactions */
+ ExecEventTriggers(&evt, EVT_CommandStart);
PreventCommandDuringRecovery("VACUUM");
vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false,
isTopLevel);
@@ -1056,6 +1103,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTableAsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, completionTag);
break;
@@ -1079,6 +1127,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
(void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
InvalidOid, InvalidOid, false);
break;
@@ -1092,6 +1141,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreatePLangStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
@@ -1099,6 +1149,7 @@ standard_ProcessUtility(Node *parsetree,
* ******************************** DOMAIN statements ****
*/
case T_CreateDomainStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineDomain((CreateDomainStmt *) parsetree);
break;
@@ -1164,6 +1215,8 @@ standard_ProcessUtility(Node *parsetree,
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery("REINDEX");
switch (stmt->kind)
@@ -1197,30 +1250,37 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateConversionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateConversionCommand((CreateConversionStmt *) parsetree);
break;
case T_CreateCastStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateCast((CreateCastStmt *) parsetree);
break;
case T_CreateOpClassStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineOpClass((CreateOpClassStmt *) parsetree);
break;
case T_CreateOpFamilyStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineOpFamily((CreateOpFamilyStmt *) parsetree);
break;
case T_AlterOpFamilyStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterOpFamily((AlterOpFamilyStmt *) parsetree);
break;
case T_AlterTSDictionaryStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
break;
case T_AlterTSConfigurationStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
break;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index d7770b8..8590f3c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -293,6 +293,33 @@ trigger_out(PG_FUNCTION_ARGS)
/*
+ * event_trigger_in - input routine for pseudo-type event_trigger.
+ */
+Datum
+event_trigger_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type event_trigger")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * event_trigger_out - output routine for pseudo-type event_trigger.
+ */
+Datum
+event_trigger_out(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot display a value of type event_trigger")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
* language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER.
*/
Datum
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 17b62b6..ec93149 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -39,11 +39,9 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
-#include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
-#include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
@@ -261,6 +259,7 @@ static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
+
/* ----------
* get_ruledef - Do it all and return a text
* that could be used as a statement
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 65bc6be..bced1e0 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/pg_collation.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
@@ -28,9 +29,11 @@
#include "catalog/pg_type.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
+#include "nodes/parsenodes.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/evtcache.h"
+#include "utils/formatting.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/memutils.h"
@@ -39,6 +42,962 @@
#include "utils/syscache.h"
/*
+ * EventTriggerCommandTags
+ *
+ * This array provides meta data allowing to parse and rewrite command tags
+ * from the command and catalogs to the internal integers we use to have fast
+ * lookups.
+ *
+ * Lookups have to be fast because they are done for each and every DDL as soon
+ * as some Event Triggers are defined.
+ */
+typedef struct
+{
+ TrigEventCommand command; /* internal command value */
+ char *tag; /* command tag */
+ NodeTag node; /* internal parser node tag */
+ ObjectType type; /* internal object type */
+} EventTriggerCommandTagsType;
+
+/*
+ * Hash table to cache the content of EventTriggerCommandTags, which is
+ * searched by command tag when building the EventTriggerProcsCache, and by
+ * NodeTag and ObjectType from ProcessUtility.
+ *
+ * In both cases we want to avoid to have to scan the whole array each time, so
+ * we cache a dedicated hash table in the session's memory.
+ */
+static HTAB *EventTriggerCommandTagsCache = NULL;
+static HTAB *EventTriggerCommandNodeCache = NULL;
+
+/* entry for the Tags cache (key is NameData of NAMEDATALEN) */
+typedef struct
+{
+ NameData tag;
+ TrigEventCommand command;
+} EventTriggerCommandTagsEntry;
+
+/* key and entry for the Node cache */
+typedef struct
+{
+ NodeTag node; /* internal parser node tag */
+ ObjectType type; /* internal object type */
+} EventTriggerCommandNodeKey;
+
+typedef struct
+{
+ EventTriggerCommandNodeKey key; /* lookup key, must be first */
+ TrigEventCommand command; /* internal command value */
+} EventTriggerCommandNodeEntry;
+
+static EventTriggerCommandTagsType EventTriggerCommandTags[] =
+{
+ {
+ ETC_CreateAggregate,
+ "CREATE AGGREGATE",
+ T_DefineStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_CreateCast,
+ "CREATE CAST",
+ T_CreateCastStmt,
+ -1
+ },
+ {
+ ETC_CreateCollation,
+ "CREATE COLLATION",
+ T_DefineStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_CreateConversion,
+ "CREATE CONVERSION",
+ T_CreateConversionStmt,
+ -1
+ },
+ {
+ ETC_CreateDomain,
+ "CREATE DOMAIN",
+ T_CreateDomainStmt,
+ -1
+ },
+ {
+ ETC_CreateExtension,
+ "CREATE EXTENSION",
+ T_CreateExtensionStmt,
+ -1
+ },
+ {
+ ETC_CreateForeignDataWrapper,
+ "CREATE FOREIGN DATA WRAPPER",
+ T_CreateFdwStmt,
+ -1
+ },
+ {
+ ETC_CreateForeignTable,
+ "CREATE FOREIGN TABLE",
+ T_CreateForeignTableStmt,
+ -1
+ },
+ {
+ ETC_CreateFunction,
+ "CREATE FUNCTION",
+ T_CreateFunctionStmt,
+ -1
+ },
+ {
+ ETC_CreateIndex,
+ "CREATE INDEX",
+ T_IndexStmt,
+ -1
+ },
+ {
+ ETC_CreateLanguage,
+ "CREATE LANGUAGE",
+ T_CreatePLangStmt,
+ -1
+ },
+ {
+ ETC_CreateOperator,
+ "CREATE OPERATOR",
+ T_DefineStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_CreateOperatorClass,
+ "CREATE OPERATOR CLASS",
+ T_CreateOpClassStmt,
+ -1
+ },
+ {
+ ETC_CreateOperatorFamily,
+ "CREATE OPERATOR FAMILY",
+ T_CreateOpFamilyStmt,
+ -1
+ },
+ {
+ ETC_CreateRule,
+ "CREATE RULE",
+ T_RuleStmt,
+ -1
+ },
+ {
+ ETC_CreateSchema,
+ "CREATE SCHEMA",
+ T_CreateSchemaStmt,
+ -1
+ },
+ {
+ ETC_CreateSequence,
+ "CREATE SEQUENCE",
+ T_CreateSeqStmt,
+ -1
+ },
+ {
+ ETC_CreateServer,
+ "CREATE SERVER",
+ T_CreateForeignServerStmt,
+ -1
+ },
+ {
+ ETC_CreateTable,
+ "CREATE TABLE",
+ T_CreateStmt,
+ -1
+ },
+ {
+ ETC_CreateTableAs,
+ "CREATE TABLE AS",
+ T_CreateTableAsStmt,
+ -1
+ },
+ {
+ ETC_SelectInto,
+ "SELECT INTO",
+ T_CreateTableAsStmt,
+ -1
+ },
+ {
+ ETC_CreateTextSearchParser,
+ "CREATE TEXT SEARCH PARSER",
+ T_DefineStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_CreateTextSearchConfiguration,
+ "CREATE TEXT SEARCH CONFIGURATION",
+ T_DefineStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_CreateTextSearchDictionary,
+ "CREATE TEXT SEARCH DICTIONARY",
+ T_DefineStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_CreateTextSearchTemplate,
+ "CREATE TEXT SEARCH TEMPLATE",
+ T_DefineStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_CreateTrigger,
+ "CREATE TRIGGER",
+ T_CreateTrigStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_DefineStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CompositeTypeStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateEnumStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateRangeStmt,
+ -1
+ },
+ {
+ ETC_CreateUserMapping,
+ "CREATE USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_CreateView,
+ "CREATE VIEW",
+ T_ViewStmt,
+ -1
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_AlterTableStmt,
+ -1
+ },
+ {
+ ETC_DropAggregate,
+ "DROP AGGREGATE",
+ T_DropStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_DropCast,
+ "DROP CAST",
+ T_DropStmt,
+ OBJECT_CAST
+ },
+ {
+ ETC_DropCollation,
+ "DROP COLLATION",
+ T_DropStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_DropConversion,
+ "DROP CONVERSION",
+ T_DropStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_DropDomain,
+ "DROP DOMAIN",
+ T_DropStmt,
+ OBJECT_DOMAIN
+ },
+ {
+ ETC_DropExtension,
+ "DROP EXTENSION",
+ T_DropStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_DropForeignDataWrapper,
+ "DROP FOREIGN DATA WRAPPER",
+ T_DropStmt,
+ OBJECT_FDW
+ },
+ {
+ ETC_DropForeignTable,
+ "DROP FOREIGN TABLE",
+ T_DropStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_DropFunction,
+ "DROP FUNCTION",
+ T_DropStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_DropIndex,
+ "DROP INDEX",
+ T_DropStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_DropLanguage,
+ "DROP LANGUAGE",
+ T_DropStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_DropOperator,
+ "DROP OPERATOR",
+ T_DropStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_DropOperatorClass,
+ "DROP OPERATOR CLASS",
+ T_DropStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_DropOperatorFamily,
+ "DROP OPERATOR FAMILY",
+ T_DropStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_DropRule,
+ "DROP RULE",
+ T_DropStmt,
+ OBJECT_RULE
+ },
+ {
+ ETC_DropSchema,
+ "DROP SCHEMA",
+ T_DropStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_DropSequence,
+ "DROP SEQUENCE",
+ T_DropStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_DropServer,
+ "DROP SERVER",
+ T_DropStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_DropTable,
+ "DROP TABLE",
+ T_DropStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_DropTextSearchParser,
+ "DROP TEXT SEARCH PARSER",
+ T_DropStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_DropTextSearchConfiguration,
+ "DROP TEXT SEARCH CONFIGURATION",
+ T_DropStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_DropTextSearchDictionary,
+ "DROP TEXT SEARCH DICTIONARY",
+ T_DropStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_DropTextSearchTemplate,
+ "DROP TEXT SEARCH TEMPLATE",
+ T_DropStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_DropTrigger,
+ "DROP TRIGGER",
+ T_DropStmt,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_DropType,
+ "DROP TYPE",
+ T_DropStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_DropUserMapping,
+ "DROP USER MAPPING",
+ T_DropUserMappingStmt,
+ -1
+ },
+ {
+ ETC_DropView,
+ "DROP VIEW",
+ T_DropStmt,
+ OBJECT_VIEW
+ },
+ {
+ ETC_Vacuum,
+ "VACUUM",
+ T_VacuumStmt,
+ -1
+ },
+ {
+ ETC_Cluster,
+ "CLUSTER",
+ T_ClusterStmt,
+ -1
+ },
+ {
+ ETC_Load,
+ "LOAD",
+ T_LoadStmt,
+ -1
+ },
+ {
+ ETC_Reindex,
+ "REINDEX",
+ T_ReindexStmt,
+ -1
+ },
+ {
+ ETC_AlterSequence,
+ "ALTER SEQUENCE",
+ T_AlterSeqStmt,
+ -1
+ },
+ {
+ ETC_AlterUserMapping,
+ "ALTER USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_AlterFunctionStmt,
+ -1
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ T_AlterDomainStmt,
+ -1
+ },
+ /* ALTER <OBJECT> name RENAME TO */
+ {
+ ETC_AlterAggregate,
+ "ALTER AGGREGATE",
+ T_RenameStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_RenameStmt,
+ OBJECT_ATTRIBUTE
+ },
+ {
+ ETC_AlterCast,
+ "ALTER CAST",
+ T_RenameStmt,
+ OBJECT_CAST
+ },
+ {
+ ETC_AlterCollation,
+ "ALTER COLLATION",
+ T_RenameStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_RenameStmt,
+ OBJECT_COLUMN
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_RenameStmt,
+ OBJECT_CONSTRAINT
+ },
+ {
+ ETC_AlterConversion,
+ "ALTER CONVERSION",
+ T_RenameStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ OBJECT_DOMAIN,
+ T_RenameStmt
+ },
+ {
+ ETC_AlterExtension,
+ "ALTER EXTENSION",
+ T_RenameStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_AlterForeignDataWrapper,
+ "ALTER FOREIGN DATA WRAPPER",
+ OBJECT_FDW,
+ T_RenameStmt
+ },
+ {
+ ETC_AlterServer,
+ "ALTER SERVER",
+ T_RenameStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_AlterForeignTable,
+ "ALTER FOREIGN TABLE",
+ T_RenameStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_RenameStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_AlterIndex,
+ "ALTER INDEX",
+ T_RenameStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_AlterLanguage,
+ "ALTER LANGUAGE",
+ T_RenameStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_AlterOperator,
+ "ALTER OPERATOR",
+ T_RenameStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_AlterOperatorClass,
+ "ALTER OPERATOR CLASS",
+ T_RenameStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_AlterOperatorFamily,
+ "ALTER OPERATOR FAMILY",
+ T_RenameStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_AlterRule,
+ "ALTER RULE",
+ T_RenameStmt,
+ OBJECT_RULE
+ },
+ {
+ ETC_AlterSchema,
+ "ALTER SCHEMA",
+ T_RenameStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_AlterSequence,
+ "ALTER SEQUENCE",
+ T_RenameStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_RenameStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_AlterTrigger,
+ "ALTER TRIGGER",
+ T_RenameStmt,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_AlterTextSearchParser,
+ "ALTER TEXT SEARCH PARSER",
+ T_RenameStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_AlterTextSearchConfiguration,
+ "ALTER TEXT SEARCH CONFIGURATION",
+ T_RenameStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_AlterTextSearchDictionary,
+ "ALTER TEXT SEARCH DICTIONARY",
+ T_RenameStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_AlterTextSearchTemplate,
+ "ALTER TEXT SEARCH TEMPLATE",
+ T_RenameStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_RenameStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_AlterView,
+ "ALTER VIEW",
+ T_RenameStmt,
+ OBJECT_VIEW
+ },
+ /* ALTER <OBJECT> name SET SCHEMA */
+ {
+ ETC_AlterAggregate,
+ "ALTER AGGREGATE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_AlterCast,
+ "ALTER CAST",
+ T_AlterObjectSchemaStmt,
+ OBJECT_CAST
+ },
+ {
+ ETC_AlterCollation,
+ "ALTER COLLATION",
+ T_AlterObjectSchemaStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_AlterConversion,
+ "ALTER CONVERSION",
+ T_AlterObjectSchemaStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ T_AlterObjectSchemaStmt,
+ OBJECT_DOMAIN
+ },
+ {
+ ETC_AlterExtension,
+ "ALTER EXTENSION",
+ T_AlterObjectSchemaStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_AlterForeignDataWrapper,
+ "ALTER FOREIGN DATA WRAPPER",
+ T_AlterObjectSchemaStmt,
+ OBJECT_FDW
+ },
+ {
+ ETC_AlterForeignTable,
+ "ALTER FOREIGN TABLE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_AlterObjectSchemaStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_AlterIndex,
+ "ALTER CAST",
+ T_AlterObjectSchemaStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_AlterLanguage,
+ "ALTER LANGUAGE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_AlterOperator,
+ "ALTER OPERATOR",
+ T_AlterObjectSchemaStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_AlterOperatorClass,
+ "ALTER OPERATOR CLASS",
+ T_AlterObjectSchemaStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_AlterOperatorFamily,
+ "ALTER OPERATOR FAMILY",
+ T_AlterObjectSchemaStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_AlterSchema,
+ "ALTER SCHEMA",
+ T_AlterObjectSchemaStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_AlterSequence,
+ "ALTER SEQUENCE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_AlterServer,
+ "ALTER SERVER",
+ T_AlterObjectSchemaStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_AlterTextSearchParser,
+ "ALTER TEXT SEARCH PARSER",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_AlterTextSearchConfiguration,
+ "ALTER TEXT SEARCH CONFIGURATION",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_AlterTextSearchDictionary,
+ "ALTER TEXT SEARCH DICTIONARY",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_AlterTextSearchTemplate,
+ "ALTER TEXT SEARCH TEMPLATE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_AlterTrigger,
+ "ALTER TRIGGER",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_AlterEnumStmt,
+ -1
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_ATTRIBUTE
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_AlterObjectSchemaStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_AlterView,
+ "ALTER VIEW",
+ T_AlterObjectSchemaStmt,
+ OBJECT_VIEW
+ },
+ {
+ ETC_AlterTextSearchDictionary,
+ "ALTER TEXT SEARCH DICTIONARY",
+ T_AlterTSDictionaryStmt,
+ -1
+ },
+ /* ALTER <OBJECT> name OWNER TO */
+ {
+ ETC_AlterAggregate,
+ "ALTER AGGREGATE",
+ T_AlterOwnerStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_AlterCast,
+ "ALTER CAST",
+ T_AlterOwnerStmt,
+ OBJECT_CAST
+ },
+ {
+ ETC_AlterCollation,
+ "ALTER COLLATION",
+ T_AlterOwnerStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_AlterConversion,
+ "ALTER CONVERSION",
+ T_AlterOwnerStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ T_AlterOwnerStmt,
+ OBJECT_DOMAIN
+ },
+ {
+ ETC_AlterExtension,
+ "ALTER EXTENSION",
+ T_AlterOwnerStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_AlterForeignDataWrapper,
+ "ALTER FOREIGN DATA WRAPPER",
+ T_AlterOwnerStmt,
+ OBJECT_FDW
+ },
+ {
+ ETC_AlterForeignTable,
+ "ALTER FOREIGN TABLE",
+ T_AlterOwnerStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_AlterOwnerStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_AlterIndex,
+ "ALTER CAST",
+ T_AlterOwnerStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_AlterLanguage,
+ "command_start",
+ T_AlterOwnerStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_AlterOperator,
+ "ALTER OPERATOR",
+ T_AlterOwnerStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_AlterOperatorClass,
+ "ALTER OPERATOR CLASS",
+ T_AlterOwnerStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_AlterOperatorFamily,
+ "ALTER OPERATOR FAMILY",
+ T_AlterOwnerStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_AlterSchema,
+ "ALTER SCHEMA",
+ T_AlterOwnerStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_AlterSequence,
+ "ALTER SEQUENCE",
+ T_AlterOwnerStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_AlterServer,
+ "ALTER SERVER",
+ T_AlterOwnerStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_AlterTextSearchParser,
+ "ALTER TEXT SEARCH PARSER",
+ T_AlterOwnerStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_AlterTextSearchConfiguration,
+ "ALTER TEXT SEARCH CONFIGURATION",
+ T_AlterOwnerStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_AlterTextSearchDictionary,
+ "ALTER TEXT SEARCH DICTIONARY",
+ T_AlterOwnerStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_AlterTextSearchTemplate,
+ "ALTER TEXT SEARCH TEMPLATE",
+ T_AlterOwnerStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_AlterTrigger,
+ "ALTER TRIGGER",
+ T_AlterOwnerStmt,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_AlterOwnerStmt,
+ OBJECT_ATTRIBUTE
+ },
+ {
+ ETC_AlterType,
+ "ALTER TYPE",
+ T_AlterOwnerStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_AlterView,
+ "ALTER VIEW",
+ T_AlterOwnerStmt,
+ OBJECT_VIEW
+ }
+};
+
+/*
* Cache the event triggers in a format that's suitable to finding which
* function to call at "hook" points in the code. The catalogs are not helpful
* at search time, because we can't both edit a single catalog entry per each
@@ -48,47 +1007,47 @@
* This cache is indexed by Event id then Event Command id (see
* pg_event_trigger.h). It's containing a list of function oid.
*/
-static HTAB *EventCommandTriggerCache = NULL;
+static HTAB *EventTriggerProcsCache = NULL;
/* event and command form the lookup key, and must appear first */
typedef struct
{
TrigEvent event;
TrigEventCommand command;
-} EventCommandTriggerCacheKey;
+} EventTriggerProcsCacheKey;
/* entry for command event trigger lookup hashtable */
typedef struct
{
- EventCommandTriggerCacheKey key; /* lookup key, must be first */
+ EventTriggerProcsCacheKey key; /* lookup key, must be first */
List *names; /* list of names of the triggers to call */
List *procs; /* list of triggers to call */
-} EventCommandTriggerCacheEntry;
+} EventTriggerProcsCacheEntry;
/*
- * Add a new function to EventCommandTriggerCache for given command and event,
+ * Add a new function to EventTriggerProcsCache for given command and event,
* creating a new hash table entry when necessary.
*
* Returns the new hash entry value.
*/
-static EventCommandTriggerCacheEntry *
+static EventTriggerProcsCacheEntry *
add_funcall_to_command_event(TrigEvent event,
TrigEventCommand command,
NameData evtname,
Oid proc)
{
bool found;
- EventCommandTriggerCacheKey key;
- EventCommandTriggerCacheEntry *hresult;
+ EventTriggerProcsCacheKey key;
+ EventTriggerProcsCacheEntry *hresult;
MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext);
memset(&key, 0, sizeof(key));
key.event = event;
key.command = command;
- hresult = (EventCommandTriggerCacheEntry *)
- hash_search(EventCommandTriggerCache, (void *)&key, HASH_ENTER, &found);
+ hresult = (EventTriggerProcsCacheEntry *)
+ hash_search(EventTriggerProcsCache, (void *)&key, HASH_ENTER, &found);
if (found)
{
@@ -113,10 +1072,10 @@ add_funcall_to_command_event(TrigEvent event,
* The idea is that the code to fetch the list of functions to process gets as
* simple as the following:
*
- * foreach(cell, EventCommandTriggerCache[TrigEventCommand][TrigEvent])
+ * foreach(cell, EventTriggerProcsCache[TrigEventCommand][TrigEvent])
*/
static void
-BuildEventTriggerCache()
+BuildEventTriggerCache(void)
{
HASHCTL info;
Relation rel, irel;
@@ -125,13 +1084,13 @@ BuildEventTriggerCache()
/* build the new hash table */
MemSet(&info, 0, sizeof(info));
- info.keysize = sizeof(EventCommandTriggerCacheKey);
- info.entrysize = sizeof(EventCommandTriggerCacheEntry);
+ info.keysize = sizeof(EventTriggerProcsCacheKey);
+ info.entrysize = sizeof(EventTriggerProcsCacheEntry);
info.hash = tag_hash;
info.hcxt = CacheMemoryContext;
/* Create the hash table holding our cache */
- EventCommandTriggerCache =
+ EventTriggerProcsCache =
hash_create("Event Trigger Command Cache",
1024,
&info,
@@ -144,8 +1103,9 @@ BuildEventTriggerCache()
indexScan = index_beginscan(rel, irel, SnapshotNow, 0, 0);
index_rescan(indexScan, NULL, 0, NULL, 0);
- /* we use a full indexscan to guarantee that we see event triggers ordered
- * by name, this way we only even have to append the trigger's function Oid
+ /*
+ * We use a full indexscan to guarantee that we see event triggers ordered
+ * by name. This way, we only even have to append the trigger's function Oid
* to the target cache Oid list.
*/
while (HeapTupleIsValid(tuple = index_getnext(indexScan, ForwardScanDirection)))
@@ -190,7 +1150,7 @@ BuildEventTriggerCache()
/* event triggers created without WHEN clause are targetting all
* commands (ANY command trigger)
*/
- add_funcall_to_command_event(event, E_ANY, name, proc);
+ add_funcall_to_command_event(event, ETC_ANY, name, proc);
}
else
{
@@ -232,8 +1192,8 @@ static void
InvalidateEvtTriggerCommandCacheCallback(Datum arg,
int cacheid, uint32 hashvalue)
{
- hash_destroy(EventCommandTriggerCache);
- EventCommandTriggerCache = NULL;
+ hash_destroy(EventTriggerProcsCache);
+ EventTriggerProcsCache = NULL;
}
/*
@@ -249,7 +1209,7 @@ InitEventTriggerCache(void)
if (!CacheMemoryContext)
CreateCacheMemoryContext();
- EventCommandTriggerCache = NULL;
+ EventTriggerProcsCache = NULL;
/* Watch for invalidation events. */
CacheRegisterSyscacheCallback(EVENTTRIGGERNAME,
@@ -265,30 +1225,30 @@ get_event_triggers(TrigEvent event, TrigEventCommand command)
{
EventCommandTriggers *triggers =
(EventCommandTriggers *) palloc(sizeof(EventCommandTriggers));
- EventCommandTriggerCacheKey anykey, cmdkey;
- EventCommandTriggerCacheEntry *any, *cmd;
+ EventTriggerProcsCacheKey anykey, cmdkey;
+ EventTriggerProcsCacheEntry *any, *cmd;
triggers->event = event;
triggers->command = command;
triggers->procs = NIL;
/* Find existing cache entry, if any. */
- if (!EventCommandTriggerCache)
+ if (!EventTriggerProcsCache)
BuildEventTriggerCache();
/* ANY command triggers */
memset(&anykey, 0, sizeof(anykey));
anykey.event = event;
- anykey.command = E_ANY;
- any = (EventCommandTriggerCacheEntry *)
- hash_search(EventCommandTriggerCache, (void *)&anykey, HASH_FIND, NULL);
+ anykey.command = ETC_ANY;
+ any = (EventTriggerProcsCacheEntry *)
+ hash_search(EventTriggerProcsCache, (void *)&anykey, HASH_FIND, NULL);
/* Specific command triggers */
memset(&cmdkey, 0, sizeof(cmdkey));
cmdkey.event = event;
cmdkey.command = command;
- cmd = (EventCommandTriggerCacheEntry *)
- hash_search(EventCommandTriggerCache, (void *)&cmdkey, HASH_FIND, NULL);
+ cmd = (EventTriggerProcsCacheEntry *)
+ hash_search(EventTriggerProcsCache, (void *)&cmdkey, HASH_FIND, NULL);
if (any == NULL && cmd == NULL)
return triggers;
@@ -327,7 +1287,8 @@ get_event_triggers(TrigEvent event, TrigEventCommand command)
lc_any_procs = lnext(lc_any_procs);
}
- /* now append as many elements from CMD list named before next ANY
+ /*
+ * now append as many elements from CMD list named before next ANY
* entry
*/
do
@@ -352,3 +1313,253 @@ get_event_triggers(TrigEvent event, TrigEventCommand command)
}
return triggers;
}
+
+char *
+event_to_string(TrigEvent event)
+{
+ switch (event)
+ {
+ case EVT_CommandStart:
+ return "command_start";
+ }
+ return NULL;
+}
+
+TrigEvent
+parse_event_name(char *event)
+{
+ if (pg_strcasecmp(event, "command_start") == 0)
+ return EVT_CommandStart;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized event \"%s\"", event)));
+
+ /* make compiler happy */
+ return -1;
+}
+
+TrigEventCommand
+parse_event_tag(char *cmdtag, bool noerror)
+{
+ char *uctag;
+ NameData key;
+ EventTriggerCommandTagsEntry *entry;
+
+ if (EventTriggerCommandTagsCache == NULL)
+ {
+ int index;
+ HASHCTL info;
+ MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext);
+
+ /* build the new hash table */
+ MemSet(&info, 0, sizeof(info));
+ info.keysize = NAMEDATALEN;
+ info.entrysize = sizeof(EventTriggerCommandTagsEntry);
+ info.hash = tag_hash;
+ info.hcxt = CacheMemoryContext;
+
+ /* Create the hash table holding our cache */
+ EventTriggerCommandTagsCache =
+ hash_create("Event Trigger Command Tags Cache",
+ 1024,
+ &info,
+ HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+
+ for (index = 0; index < lengthof(EventTriggerCommandTags); index++)
+ {
+ bool found;
+ char *tag = EventTriggerCommandTags[index].tag;
+ NameData key;
+ EventTriggerCommandTagsEntry *hresult;
+
+ memset(&key, 0, NAMEDATALEN);
+ key = *(DatumGetName(DirectFunctionCall1(namein,
+ CStringGetDatum(tag))));
+
+ hresult = (EventTriggerCommandTagsEntry *)
+ hash_search(EventTriggerCommandTagsCache,
+ (void *)&key, HASH_ENTER, &found);
+
+ hresult->command = EventTriggerCommandTags[index].command;
+ }
+ MemoryContextSwitchTo(old);
+ }
+
+ memset(&key, 0, NAMEDATALEN);
+ uctag = str_toupper(cmdtag, strlen(cmdtag), DEFAULT_COLLATION_OID);
+ key = *(DatumGetName(
+ DirectFunctionCall1(namein, CStringGetDatum(uctag))));
+
+ entry = (EventTriggerCommandTagsEntry *)
+ hash_search(EventTriggerCommandTagsCache,
+ (void *)&key, HASH_FIND, NULL);
+
+ if (entry == NULL)
+ {
+ if (!noerror)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized command \"%s\"", cmdtag)));
+ return ETC_UNKNOWN;
+ }
+ return entry->command;
+}
+
+char *
+command_to_string(TrigEventCommand command)
+{
+ int index;
+ for (index = 0; index < lengthof(EventTriggerCommandTags); index++)
+ {
+ if (command == EventTriggerCommandTags[index].command)
+ return EventTriggerCommandTags[index].tag;
+ }
+ return NULL;
+}
+
+/*
+ * Cache lookup support for ProcessUtility
+ */
+TrigEventCommand
+get_command_from_nodetag(NodeTag node, ObjectType type, bool noerror)
+{
+ EventTriggerCommandNodeKey key;
+ EventTriggerCommandNodeEntry *entry;
+
+ if (EventTriggerCommandNodeCache == NULL)
+ {
+ int index;
+ HASHCTL info;
+ MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext);
+
+ /* build the new hash table */
+ MemSet(&info, 0, sizeof(info));
+ info.keysize = sizeof(EventTriggerCommandNodeKey);
+ info.entrysize = sizeof(EventTriggerCommandNodeEntry);
+ info.hash = tag_hash;
+ info.hcxt = CacheMemoryContext;
+
+ /* Create the hash table holding our cache */
+ EventTriggerCommandNodeCache =
+ hash_create("Event Trigger Command Node Cache",
+ 1024,
+ &info,
+ HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+
+ for (index = 0; index < lengthof(EventTriggerCommandTags); index++)
+ {
+ bool found;
+ EventTriggerCommandNodeKey key;
+ EventTriggerCommandNodeEntry *hresult;
+
+ memset(&key, 0, sizeof(key));
+ key.node = EventTriggerCommandTags[index].node;
+ key.type = EventTriggerCommandTags[index].type;
+
+ hresult = (EventTriggerCommandNodeEntry *)
+ hash_search(EventTriggerCommandNodeCache,
+ (void *)&key, HASH_ENTER, &found);
+
+ hresult->command = EventTriggerCommandTags[index].command;
+ }
+ MemoryContextSwitchTo(old);
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.node = node;
+ key.type = type;
+
+ entry = (EventTriggerCommandNodeEntry *)
+ hash_search(EventTriggerCommandNodeCache,
+ (void *)&key, HASH_FIND, NULL);
+
+ if (entry == NULL)
+ {
+ if (!noerror)
+ /* fixme: should not happen, use elog? */
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized node %d and object %d", node, type)));
+ return ETC_UNKNOWN;
+ }
+ return entry->command;
+}
+
+char *
+objecttype_to_string(ObjectType type)
+{
+ switch(type)
+ {
+ case OBJECT_AGGREGATE:
+ return "AGGREGATE";
+ case OBJECT_ATTRIBUTE:
+ return "ATTRIBUTE";
+ case OBJECT_CAST:
+ return "CAST";
+ case OBJECT_COLUMN:
+ return "COLUMN";
+ case OBJECT_CONSTRAINT:
+ return "CONSTRAINT";
+ case OBJECT_COLLATION:
+ return "COLLATION";
+ case OBJECT_CONVERSION:
+ return "CONVERSION";
+ case OBJECT_DATABASE:
+ return "DATABASE";
+ case OBJECT_DOMAIN:
+ return "DOMAIN";
+ case OBJECT_EVENT_TRIGGER:
+ return "EVENT TRIGGER";
+ case OBJECT_EXTENSION:
+ return "EXTENSION";
+ case OBJECT_FDW:
+ return "FDW";
+ case OBJECT_FOREIGN_SERVER:
+ return "FOREIGN SERVER";
+ case OBJECT_FOREIGN_TABLE:
+ return "FOREIGN TABLE";
+ case OBJECT_FUNCTION:
+ return "FUNCTION";
+ case OBJECT_INDEX:
+ return "INDEX";
+ case OBJECT_LANGUAGE:
+ return "LANGUAGE";
+ case OBJECT_LARGEOBJECT:
+ return "LARGE OBJECT";
+ case OBJECT_OPCLASS:
+ return "OPERATOR CLASS";
+ case OBJECT_OPERATOR:
+ return "OPERATOR";
+ case OBJECT_OPFAMILY:
+ return "OPERATOR FAMILY";
+ case OBJECT_ROLE:
+ return "ROLE";
+ case OBJECT_RULE:
+ return "RULE";
+ case OBJECT_SCHEMA:
+ return "SCHEMA";
+ case OBJECT_SEQUENCE:
+ return "SEQUENCE";
+ case OBJECT_TABLE:
+ return "TABLE";
+ case OBJECT_TABLESPACE:
+ return "TABLESPACE";
+ case OBJECT_TRIGGER:
+ return "TRIGGER";
+ case OBJECT_TSCONFIGURATION:
+ return "TEXT SEARCH CONFIGURATION";
+ case OBJECT_TSDICTIONARY:
+ return "TEXT SEARCH DICTIONARY";
+ case OBJECT_TSPARSER:
+ return "TEXT SEARCH PARSER";
+ case OBJECT_TSTEMPLATE:
+ return "TEXT SEARCH TEMPLATE";
+ case OBJECT_TYPE:
+ return "TYPE";
+ case OBJECT_VIEW:
+ return "VIEW";
+ }
+ /* silence compiler */
+ return NULL;
+}
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 993053c..611c8e3 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -100,7 +100,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
int numForeignDataWrappers;
int numForeignServers;
int numDefaultACLs;
- int numEvtTriggers;
+ int numEventTriggers;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
@@ -242,8 +242,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
getTriggers(fout, tblinfo, numTables);
if (g_verbose)
- write_msg(NULL, "reading command triggers\n");
- getEvtTriggers(fout, &numEvtTriggers);
+ write_msg(NULL, "reading event triggers\n");
+ getEventTriggers(fout, &numEventTriggers);
*numTablesPtr = numTables;
return tblinfo;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 75b7ff0..09ca6dd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -187,7 +187,7 @@ static void dumpConversion(Archive *fout, ConvInfo *convinfo);
static void dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
-static void dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo);
+static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
@@ -5299,52 +5299,54 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
}
/*
- * getEvtTriggers
- * get information about every command trigger on a dumpable table
+ * getEventTriggers
+ * get information about event triggers
*/
-EvtTriggerInfo *
-getEvtTriggers(Archive *fout, int *numEvtTriggers)
+EventTriggerInfo *
+getEventTriggers(Archive *fout, int *numEventTriggers)
{
int i;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
- EvtTriggerInfo *evtinfo;
+ EventTriggerInfo *evtinfo;
int i_tableoid,
i_oid,
i_evtname,
i_evtevent,
- i_evtowner,
+ i_evtowner,
i_evttags,
i_evtfname,
i_evtenabled;
int ntups;
+ /* Before 9.3, there are no event triggers */
+ if (fout->remoteVersion < 90300)
+ {
+ *numEventTriggers = 0;
+ return NULL;
+ }
+
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
- if (fout->remoteVersion >= 90200)
- {
- appendPQExpBuffer(query,
- "SELECT e.tableoid, e.oid, evtname, evtenabled, "
- "evtevent, (%s evtowner) AS evtowner, "
- "array_to_string(array("
- "select '''' || x || '''' "
- " from unnest(evttags) as t(x)), ', ') as evttags, "
- "n.nspname || '.' || p.proname as evtfname "
- "FROM pg_event_trigger e "
- "JOIN pg_proc p on e.evtfoid = p.oid "
- "JOIN pg_namespace n ON p.pronamespace = n.oid "
- "ORDER BY e.oid",
- username_subquery);
- }
+ appendPQExpBuffer(query,
+ "SELECT e.tableoid, e.oid, evtname, evtenabled, "
+ "evtevent, (%s evtowner) AS evtowner, "
+ "array_to_string(array("
+ "select quote_literal(x) "
+ " from unnest(evttags) as t(x)), ', ') as evttags, "
+ "e.evtfoid::regproc as evtfname "
+ "FROM pg_event_trigger e "
+ "ORDER BY e.oid",
+ username_subquery);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
- *numEvtTriggers = ntups;
+ *numEventTriggers = ntups;
- evtinfo = (EvtTriggerInfo *) pg_malloc(ntups * sizeof(EvtTriggerInfo));
+ evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
@@ -7248,7 +7250,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
dumpTrigger(fout, (TriggerInfo *) dobj);
break;
case DO_EVENT_TRIGGER:
- dumpEvtTrigger(fout, (EvtTriggerInfo *) dobj);
+ dumpEventTrigger(fout, (EventTriggerInfo *) dobj);
break;
case DO_CONSTRAINT:
dumpConstraint(fout, (ConstraintInfo *) dobj);
@@ -13743,7 +13745,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
}
static void
-dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo)
+dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo)
{
PQExpBuffer query;
PQExpBuffer labelq;
@@ -13754,19 +13756,19 @@ dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo)
appendPQExpBuffer(query, "CREATE EVENT TRIGGER ");
appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name));
appendPQExpBuffer(query, " ON ");
- appendPQExpBufferStr(query, evtinfo->evtevent);
+ appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
appendPQExpBufferStr(query, " ");
if (strcmp("", evtinfo->evttags) != 0)
{
- appendPQExpBufferStr(query, "when tag in (");
+ appendPQExpBufferStr(query, "\n WHEN TAG IN (");
appendPQExpBufferStr(query, evtinfo->evttags);
appendPQExpBufferStr(query, ") ");
}
- appendPQExpBuffer(query, "EXECUTE PROCEDURE ");
+ appendPQExpBuffer(query, "\n EXECUTE PROCEDURE ");
appendPQExpBufferStr(query, evtinfo->evtfname);
- appendPQExpBuffer(query, " ();\n");
+ appendPQExpBuffer(query, "();\n");
if (evtinfo->evtenabled != 'O')
{
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 758a1df..5793bca 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -363,7 +363,7 @@ typedef struct _evttriggerInfo
char *evtfname;
char evttype;
char evtenabled;
-} EvtTriggerInfo;
+} EventTriggerInfo;
/*
* struct ConstraintInfo is used for all constraint types. However we
@@ -575,6 +575,6 @@ extern ForeignServerInfo *getForeignServers(Archive *fout,
extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions);
-extern EvtTriggerInfo *getEvtTriggers(Archive *fout, int *numEvtTriggers);
+extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 6d4c2ba..5318e7a 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter");
* Objects are sorted by priority levels, and within an equal priority level
* by OID. (This is a relatively crude hack to provide semi-reasonable
* behavior for old databases without full dependency info.) Note: collations,
- * extensions, text search, foreign-data, and default ACL objects can't really
- * happen here, so the rather bogus priorities for them don't matter.
+ * extensions, text search, foreign-data, event trigger, and default ACL
+ * objects can't really happen here, so the rather bogus priorities for them
+ * don't matter.
*
* NOTE: object-type priorities must match the section assignments made in
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
@@ -114,7 +115,7 @@ static const int newObjectTypePriority[] =
24, /* DO_BLOB_DATA */
22, /* DO_PRE_DATA_BOUNDARY */
25, /* DO_POST_DATA_BOUNDARY */
- 30 /* DO_EVENT_TRIGGER */
+ 32 /* DO_EVENT_TRIGGER */
};
static DumpId preDataBoundId;
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 7ae8049..939e88f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -491,7 +491,7 @@ exec_command(const char *cmd,
success = listExtensions(pattern);
break;
case 'y': /* Event Triggers */
- success = listEvtTriggers(pattern, show_verbose);
+ success = listEventTriggers(pattern, show_verbose);
break;
default:
status = PSQL_CMD_UNKNOWN;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 7e8c996..14c6fe3 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2958,7 +2958,7 @@ listConversions(const char *pattern, bool verbose, bool showSystem)
* Describes Event Triggers.
*/
bool
-listEvtTriggers(const char *pattern, bool verbose)
+listEventTriggers(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
@@ -2971,15 +2971,14 @@ listEvtTriggers(const char *pattern, bool verbose)
"select evtname as \"%s\", "
"evtevent as \"%s\", "
"pg_catalog.pg_get_userbyid(e.evtowner) AS \"%s\", "
- " case evtenabled when 'O' then 'enabled' "
+ "case evtenabled when 'O' then 'enabled' "
" when 'R' then 'replica' "
" when 'A' then 'always' "
" when 'D' then 'disabled' end as \"%s\", "
- "n.nspname || '.' || p.proname || '()' as \"%s\", "
- " array_to_string(array(select x "
+ "e.evtfoid::regproc as \"%s\", "
+ "array_to_string(array(select x "
" from unnest(evttags) as t(x)), ', ') as \"%s\" "
- "FROM pg_event_trigger e JOIN pg_proc p on e.evtfoid = p.oid "
- "JOIN pg_namespace n ON p.pronamespace = n.oid ",
+ "FROM pg_event_trigger e ",
gettext_noop("Name"),
gettext_noop("Event"),
gettext_noop("Owner"),
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index f2bc794..eef7733 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -97,6 +97,6 @@ extern bool listExtensions(const char *pattern);
extern bool listExtensionContents(const char *pattern);
/* \dy */
-extern bool listEvtTriggers(const char *pattern, bool verbose);
+extern bool listEventTriggers(const char *pattern, bool verbose);
#endif /* DESCRIBE_H */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 5807265..0843b65 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -229,7 +229,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
- fprintf(output, _(" \\dy [PATTERN] list command triggers\n"));
+ fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h
index 153d48a..a6caaee 100644
--- a/src/include/catalog/pg_event_trigger.h
+++ b/src/include/catalog/pg_event_trigger.h
@@ -60,122 +60,4 @@ typedef FormData_pg_event_trigger *Form_pg_event_trigger;
#define Anum_pg_event_trigger_evtenabled 5
#define Anum_pg_event_trigger_evttags 6
-/*
- * Times at which an event trigger can be fired. These are the
- * possible values for pg_event_trigger.evtevent.
- *
- * As of now we only implement the command_start firing point, we intend on
- * adding more firing points later.
- */
-typedef enum TrigEvent
-{
- E_CommandStart = 1,
-} TrigEvent;
-
-/*
- * Supported commands
- */
-typedef enum TrigEventCommand
-{
- E_UNKNOWN = 0,
- E_ANY = 1,
-
- E_AlterAggregate = 100,
- E_AlterCast,
- E_AlterCollation,
- E_AlterConversion,
- E_AlterDomain,
- E_AlterExtension,
- E_AlterForeignDataWrapper,
- E_AlterForeignTable,
- E_AlterFunction,
- E_AlterIndex,
- E_AlterLanguage,
- E_AlterOperator,
- E_AlterOperatorClass,
- E_AlterOperatorFamily,
- E_AlterSchema,
- E_AlterSequence,
- E_AlterServer,
- E_AlterTable,
- E_AlterTextSearchParser,
- E_AlterTextSearchConfiguration,
- E_AlterTextSearchDictionary,
- E_AlterTextSearchTemplate,
- E_AlterTrigger,
- E_AlterType,
- E_AlterUserMapping,
- E_AlterView,
-
- E_Cluster = 300,
- E_Load,
- E_Reindex,
- E_SelectInto,
- E_Vacuum,
-
- E_CreateAggregate = 400,
- E_CreateCast,
- E_CreateCollation,
- E_CreateConversion,
- E_CreateDomain,
- E_CreateExtension,
- E_CreateForeignDataWrapper,
- E_CreateForeignTable,
- E_CreateFunction,
- E_CreateIndex,
- E_CreateLanguage,
- E_CreateOperator,
- E_CreateOperatorClass,
- E_CreateOperatorFamily,
- E_CreateRule,
- E_CreateSchema,
- E_CreateSequence,
- E_CreateServer,
- E_CreateTable,
- E_CreateTableAs,
- E_CreateTextSearchParser,
- E_CreateTextSearchConfiguration,
- E_CreateTextSearchDictionary,
- E_CreateTextSearchTemplate,
- E_CreateTrigger,
- E_CreateType,
- E_CreateUserMapping,
- E_CreateView,
-
- E_DropAggregate = 600,
- E_DropCast,
- E_DropCollation,
- E_DropConversion,
- E_DropDomain,
- E_DropExtension,
- E_DropForeignDataWrapper,
- E_DropForeignTable,
- E_DropFunction,
- E_DropIndex,
- E_DropLanguage,
- E_DropOperator,
- E_DropOperatorClass,
- E_DropOperatorFamily,
- E_DropRule,
- E_DropSchema,
- E_DropSequence,
- E_DropServer,
- E_DropTable,
- E_DropTextSearchParser,
- E_DropTextSearchConfiguration,
- E_DropTextSearchDictionary,
- E_DropTextSearchTemplate,
- E_DropTrigger,
- E_DropType,
- E_DropUserMapping,
- E_DropView
-} TrigEventCommand;
-
-/* implemented in src/backend/catalog/pg_event_trigger.c */
-char * event_to_string(TrigEvent event);
-TrigEvent parse_event_name(char *event);
-
-char * command_to_string(TrigEventCommand command);
-TrigEventCommand parse_event_tag(char *command, bool noerror);
-
#endif /* PG_EVENT_TRIGGER_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 340de88..743f890 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3456,6 +3456,10 @@ DATA(insert OID = 2300 ( trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2
DESCR("I/O");
DATA(insert OID = 2301 ( trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2279" _null_ _null_ _null_ _null_ trigger_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3594 ( event_trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3838 "2275" _null_ _null_ _null_ _null_ event_trigger_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3595 ( event_trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3838" _null_ _null_ _null_ _null_ event_trigger_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 2302 ( language_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2280 "2275" _null_ _null_ _null_ _null_ language_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 2303 ( language_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2280" _null_ _null_ _null_ _null_ language_handler_out _null_ _null_ _null_ ));
@@ -4645,6 +4649,7 @@ DESCR("SP-GiST support for suffix tree over text");
DATA(insert OID = 4031 ( spg_text_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_text_leaf_consistent _null_ _null_ _null_ ));
DESCR("SP-GiST support for suffix tree over text");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 98c7dd5..86be998 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -650,7 +650,7 @@ DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void
#define VOIDOID 2278
DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5c4db5c..13be6bc 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -15,6 +15,7 @@
#include "catalog/pg_event_trigger.h"
#include "nodes/parsenodes.h"
+#include "utils/evtcache.h"
/*
* To be able to call user defined function on event triggers, the places in
@@ -30,6 +31,8 @@ typedef struct EventContextData
TrigEventCommand command; /* For command triggers */
char *toplevel; /* TopLevel Command Tag */
char *tag; /* Command Tag */
+ char *operation; /* CREATE / ALTER / DROP, or NULL */
+ ObjectType objecttype; /* TABLE, FUNCTION, CAST, etc, or NULL */
Oid objectId; /* oid of the existing object, if any */
char *schemaname; /* schemaname or NULL if not relevant */
char *objectname; /* objectname */
@@ -48,9 +51,11 @@ typedef struct EventContextData *EventContext;
typedef struct EventTriggerData
{
NodeTag type;
- char *when; /* Either BEFORE or AFTER */
+ char *event; /* command_start, etc */
char *toplevel; /* TopLevel Command Tag */
char *tag; /* Command Tag */
+ char *operation; /* CREATE / ALTER / DROP, or NULL */
+ char *objecttype; /* TABLE, FUNCTION, CAST, etc, or NULL */
Oid objectId; /* oid of the existing object, if any */
char *schemaname; /* schemaname or NULL if not relevant */
char *objectname; /* objectname */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 2d1cccb..5700e54 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -195,6 +195,7 @@ typedef enum AclObjectKind
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
ACL_KIND_FDW, /* pg_foreign_data_wrapper */
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
+ ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */
ACL_KIND_EXTENSION, /* pg_extension */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
@@ -322,6 +323,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
+extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 71563ad..8b9291c 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS);
extern Datum void_send(PG_FUNCTION_ARGS);
extern Datum trigger_in(PG_FUNCTION_ARGS);
extern Datum trigger_out(PG_FUNCTION_ARGS);
+extern Datum event_trigger_in(PG_FUNCTION_ARGS);
+extern Datum event_trigger_out(PG_FUNCTION_ARGS);
extern Datum language_handler_in(PG_FUNCTION_ARGS);
extern Datum language_handler_out(PG_FUNCTION_ARGS);
extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
@@ -1013,7 +1015,6 @@ extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS);
/* format_type.c */
extern Datum format_type(PG_FUNCTION_ARGS);
extern char *format_type_be(Oid type_oid);
-extern char *format_type_be_without_namespace(Oid type_oid);
extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
extern int32 type_maximum_size(Oid type_oid, int32 typemod);
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 51f6bec..ef84b90 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -15,6 +15,125 @@
#include "catalog/pg_event_trigger.h"
+
+/*
+ * Times at which an event trigger can be fired. These are the
+ * possible values for pg_event_trigger.evtevent.
+ *
+ * As of now we only implement the command_start firing point, we intend on
+ * adding more firing points later.
+ */
+typedef enum TrigEvent
+{
+ EVT_CommandStart = 1,
+} TrigEvent;
+
+/*
+ * Supported commands
+ *
+ * Those values are not going to disk, so can be shuffled around at will. We
+ * keep the number organized so that it's easier to debug, should it be needed.
+ *
+ * See also EventTriggerCommandTags in src/backend/utils/cache/evtcache.c
+ */
+typedef enum TrigEventCommand
+{
+ ETC_UNSET = -1,
+ ETC_UNKNOWN = 0,
+ ETC_ANY = 1,
+
+ ETC_AlterAggregate = 100,
+ ETC_AlterCast,
+ ETC_AlterCollation,
+ ETC_AlterConversion,
+ ETC_AlterDomain,
+ ETC_AlterExtension,
+ ETC_AlterForeignDataWrapper,
+ ETC_AlterForeignTable,
+ ETC_AlterFunction,
+ ETC_AlterIndex,
+ ETC_AlterLanguage,
+ ETC_AlterOperator,
+ ETC_AlterOperatorClass,
+ ETC_AlterOperatorFamily,
+ ETC_AlterRule,
+ ETC_AlterSchema,
+ ETC_AlterSequence,
+ ETC_AlterServer,
+ ETC_AlterTable,
+ ETC_AlterTextSearchParser,
+ ETC_AlterTextSearchConfiguration,
+ ETC_AlterTextSearchDictionary,
+ ETC_AlterTextSearchTemplate,
+ ETC_AlterTrigger,
+ ETC_AlterType,
+ ETC_AlterUserMapping,
+ ETC_AlterView,
+
+ ETC_Cluster = 300,
+ ETC_Load,
+ ETC_Reindex,
+ ETC_SelectInto,
+ ETC_Vacuum,
+
+ ETC_CreateAggregate = 400,
+ ETC_CreateCast,
+ ETC_CreateCollation,
+ ETC_CreateConversion,
+ ETC_CreateDomain,
+ ETC_CreateExtension,
+ ETC_CreateForeignDataWrapper,
+ ETC_CreateForeignTable,
+ ETC_CreateFunction,
+ ETC_CreateIndex,
+ ETC_CreateLanguage,
+ ETC_CreateOperator,
+ ETC_CreateOperatorClass,
+ ETC_CreateOperatorFamily,
+ ETC_CreateRule,
+ ETC_CreateSchema,
+ ETC_CreateSequence,
+ ETC_CreateServer,
+ ETC_CreateTable,
+ ETC_CreateTableAs,
+ ETC_CreateTextSearchParser,
+ ETC_CreateTextSearchConfiguration,
+ ETC_CreateTextSearchDictionary,
+ ETC_CreateTextSearchTemplate,
+ ETC_CreateTrigger,
+ ETC_CreateType,
+ ETC_CreateUserMapping,
+ ETC_CreateView,
+
+ ETC_DropAggregate = 600,
+ ETC_DropCast,
+ ETC_DropCollation,
+ ETC_DropConversion,
+ ETC_DropDomain,
+ ETC_DropExtension,
+ ETC_DropForeignDataWrapper,
+ ETC_DropForeignTable,
+ ETC_DropFunction,
+ ETC_DropIndex,
+ ETC_DropLanguage,
+ ETC_DropOperator,
+ ETC_DropOperatorClass,
+ ETC_DropOperatorFamily,
+ ETC_DropRule,
+ ETC_DropSchema,
+ ETC_DropSequence,
+ ETC_DropServer,
+ ETC_DropTable,
+ ETC_DropTextSearchParser,
+ ETC_DropTextSearchConfiguration,
+ ETC_DropTextSearchDictionary,
+ ETC_DropTextSearchTemplate,
+ ETC_DropTrigger,
+ ETC_DropType,
+ ETC_DropUserMapping,
+ ETC_DropView
+} TrigEventCommand;
+
/*
* Event Triggers to fire for a given event and command, including ANY command
* triggers.
@@ -30,5 +149,16 @@ void InitEventTriggerCache(void);
EventCommandTriggers *get_event_triggers(TrigEvent event,
TrigEventCommand command);
+char * event_to_string(TrigEvent event);
+TrigEvent parse_event_name(char *event);
+
+char * command_to_string(TrigEventCommand command);
+TrigEventCommand parse_event_tag(char *cmdtag, bool noerror);
+
+TrigEventCommand get_command_from_nodetag(NodeTag node,
+ ObjectType type,
+ bool noerror);
+
+char * objecttype_to_string(ObjectType type);
#endif /* EVTCACHE_H */
diff --git a/src/pl/plperl/expected/plperl_trigger.out b/src/pl/plperl/expected/plperl_trigger.out
index b61d47c..2d1ed56 100644
--- a/src/pl/plperl/expected/plperl_trigger.out
+++ b/src/pl/plperl/expected/plperl_trigger.out
@@ -309,10 +309,10 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
ERROR: trigger functions can only be called as triggers
CONTEXT: compilation of PL/Perl function "direct_trigger"
--- test plperl command triggers
+-- test plperl event triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: "
- . $_TD->{when} . " "
+ . $_TD->{event} . " "
. $_TD->{tag} . " "
. $_TD->{schemaname} . " "
. $_TD->{objectname});
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5b456aa..374a81e 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1571,7 +1571,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
}
-/* Set up the arguments for a command trigger call. */
+/* Set up the arguments for an event trigger call. */
static SV *
plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
{
@@ -1584,7 +1584,7 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
tdata = (EventTriggerData *) fcinfo->context;
- hv_store_string(hv, "when", cstr2sv(tdata->when));
+ hv_store_string(hv, "event", cstr2sv(tdata->event));
hv_store_string(hv, "tag", cstr2sv(tdata->tag));
if (tdata->objectId == InvalidOid)
@@ -1594,8 +1594,14 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->objectId)));
hv_store_string(hv, "objectid", cstr2sv(objectid));
- hv_store_string(hv, "objectname", cstr2sv(tdata->objectname == NULL ? "NULL" : tdata->objectname));
- hv_store_string(hv, "schemaname", cstr2sv(tdata->schemaname == NULL ? "NULL" : tdata->schemaname));
+ hv_store_string(hv, "operation",
+ cstr2sv(tdata->operation == NULL ? "NULL" : tdata->operation));
+ hv_store_string(hv, "objecttype",
+ cstr2sv(tdata->objecttype == NULL ? "NULL" : tdata->objecttype));
+ hv_store_string(hv, "objectname",
+ cstr2sv(tdata->objectname == NULL ? "NULL" : tdata->objectname));
+ hv_store_string(hv, "schemaname",
+ cstr2sv(tdata->schemaname == NULL ? "NULL" : tdata->schemaname));
return newRV_noinc((SV *) hv);
}
diff --git a/src/pl/plperl/sql/plperl_trigger.sql b/src/pl/plperl/sql/plperl_trigger.sql
index 9cc2408..75a9f11 100644
--- a/src/pl/plperl/sql/plperl_trigger.sql
+++ b/src/pl/plperl/sql/plperl_trigger.sql
@@ -170,10 +170,10 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
--- test plperl command triggers
+-- test plperl event triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: "
- . $_TD->{when} . " "
+ . $_TD->{event} . " "
. $_TD->{tag} . " "
. $_TD->{schemaname} . " "
. $_TD->{objectname});
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 77c5ed9..4fb1443 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -536,7 +536,7 @@ do_compile(FunctionCallInfo fcinfo,
if (rettypeid == VOIDOID ||
rettypeid == RECORDOID)
/* okay */ ;
- else if (rettypeid == TRIGGEROID)
+ else if (rettypeid == TRIGGEROID || rettypeid == EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
@@ -689,15 +689,15 @@ do_compile(FunctionCallInfo fcinfo,
if (procStruct->pronargs != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
- errmsg("command trigger functions cannot have declared arguments")));
+ errmsg("event trigger functions cannot have declared arguments")));
/* Add the variable tg_when */
- var = plpgsql_build_variable("tg_when", 0,
+ var = plpgsql_build_variable("tg_event", 0,
plpgsql_build_datatype(TEXTOID,
-1,
function->fn_input_collation),
true);
- function->tg_when_varno = var->dno;
+ function->tg_event_varno = var->dno;
/* Add the variable tg_tag */
var = plpgsql_build_variable("tg_tag", 0,
@@ -707,6 +707,22 @@ do_compile(FunctionCallInfo fcinfo,
true);
function->tg_tag_varno = var->dno;
+ /* Add the variable tg_operation */
+ var = plpgsql_build_variable("tg_operation", 0,
+ plpgsql_build_datatype(TEXTOID,
+ -1,
+ function->fn_input_collation),
+ true);
+ function->tg_operation_varno = var->dno;
+
+ /* Add the variable tg_objecttype */
+ var = plpgsql_build_variable("tg_objecttype", 0,
+ plpgsql_build_datatype(TEXTOID,
+ -1,
+ function->fn_input_collation),
+ true);
+ function->tg_objecttype_varno = var->dno;
+
/* Add the variable tg_objectid */
var = plpgsql_build_variable("tg_objectid", 0,
plpgsql_build_datatype(OIDOID,
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd59d75..a2cf506 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -773,8 +773,8 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
return rettup;
}
-void plpgsql_exec_event_trigger(PLpgSQL_function *func,
- EventTriggerData *trigdata)
+void
+plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
@@ -805,8 +805,8 @@ void plpgsql_exec_event_trigger(PLpgSQL_function *func,
/*
* Assign the special tg_ variables
*/
- var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
- var->value = CStringGetTextDatum(trigdata->when);
+ var = (PLpgSQL_var *) (estate.datums[func->tg_event_varno]);
+ var->value = CStringGetTextDatum(trigdata->event);
var->isnull = false;
var->freeval = true;
@@ -815,6 +815,30 @@ void plpgsql_exec_event_trigger(PLpgSQL_function *func,
var->isnull = false;
var->freeval = true;
+ var = (PLpgSQL_var *) (estate.datums[func->tg_operation_varno]);
+ if (trigdata->operation == NULL)
+ {
+ var->isnull = true;
+ }
+ else
+ {
+ var->value = CStringGetTextDatum(trigdata->operation);
+ var->isnull = false;
+ }
+ var->freeval = false;
+
+ var = (PLpgSQL_var *) (estate.datums[func->tg_objecttype_varno]);
+ if (trigdata->objecttype == NULL)
+ {
+ var->isnull = true;
+ }
+ else
+ {
+ var->value = CStringGetTextDatum(trigdata->objecttype);
+ var->isnull = false;
+ }
+ var->freeval = false;
+
var = (PLpgSQL_var *) (estate.datums[func->tg_objectid_varno]);
if (trigdata->objectId == InvalidOid)
{
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 57f556f..4e15ad8 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -721,6 +721,9 @@ typedef struct PLpgSQL_function
int tg_argv_varno;
int tg_tag_varno;
+ int tg_event_varno;
+ int tg_operation_varno;
+ int tg_objecttype_varno;
int tg_objectid_varno;
int tg_schemaname_varno;
int tg_objectname_varno;
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index f756462..5294dc7 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -610,10 +610,10 @@ SELECT * FROM composite_trigger_nested_test;
("(,t)","(1,f)",)
(3 rows)
--- test plpython command triggers
+-- test plpython event triggers
create or replace function pysnitch() returns event_trigger language plpythonu as $$
plpy.notice(" pysnitch: %s %s %s.%s" %
- (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ (TD["event"], TD["tag"], TD["schemaname"], TD["objectname"]));
$$;
create event trigger py_snitch on command_start execute procedure pysnitch();
create or replace function foobar() returns int language sql as $$select 1;$$;
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 63682bf..7405893 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -348,8 +348,10 @@ PLy_exec_event_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
PG_TRY();
{
/* build command trigger args */
- PyObject *pltwhen,
+ PyObject *pltevent,
*plttag,
+ *pltoperation,
+ *pltobjecttype,
*pltschemaname,
*pltobjectname;
char *stroid;
@@ -358,14 +360,32 @@ PLy_exec_event_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
if (!pltdata)
PLy_elog(ERROR, "could not create new dictionary while building command trigger arguments");
- pltwhen = PyString_FromString(tdata->when);
- PyDict_SetItemString(pltdata, "when", pltwhen);
- Py_DECREF(pltwhen);
+ pltevent = PyString_FromString(tdata->event);
+ PyDict_SetItemString(pltdata, "event", pltevent);
+ Py_DECREF(pltevent);
plttag = PyString_FromString(tdata->tag);
PyDict_SetItemString(pltdata, "tag", plttag);
Py_DECREF(plttag);
+ if (tdata->operation == NULL)
+ PyDict_SetItemString(pltdata, "operation", Py_None);
+ else
+ {
+ pltoperation = PyString_FromString(tdata->operation);
+ PyDict_SetItemString(pltdata, "operation", pltoperation);
+ Py_DECREF(pltoperation);
+ }
+
+ if (tdata->objecttype == NULL)
+ PyDict_SetItemString(pltdata, "objecttype", Py_None);
+ else
+ {
+ pltobjecttype = PyString_FromString(tdata->objecttype);
+ PyDict_SetItemString(pltdata, "objecttype", pltobjecttype);
+ Py_DECREF(pltobjecttype);
+ }
+
if (tdata->objectId == InvalidOid)
PyDict_SetItemString(pltdata, "objectId", Py_None);
else
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 7ec6e0b..aaa1731 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -388,10 +388,10 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
SELECT * FROM composite_trigger_nested_test;
--- test plpython command triggers
+-- test plpython event triggers
create or replace function pysnitch() returns event_trigger language plpythonu as $$
plpy.notice(" pysnitch: %s %s %s.%s" %
- (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ (TD["event"], TD["tag"], TD["schemaname"], TD["objectname"]));
$$;
create event trigger py_snitch on command_start execute procedure pysnitch();
diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out
index 9b9c157..cfe124c 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -519,9 +519,9 @@ select tcl_date_week(2001,10,24);
42
(1 row)
--- test pltcl command triggers
+-- test pltcl event triggers
create or replace function tclsnitch() returns event_trigger language pltcl as $$
- elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
+ elog NOTICE " tclsnitch: $TG_event $TG_tag $TG_schemaname $TG_objectname"
$$;
create event trigger tcl_snitch on command_start execute procedure tclsnitch();
create or replace function foobar() returns int language sql as $$select 1;$$;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 7df6467..3ee32f7 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1166,7 +1166,7 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
PG_TRY();
{
- Tcl_DStringAppendElement(&tcl_cmd, tdata->when);
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->event);
Tcl_DStringAppendElement(&tcl_cmd, tdata->tag);
if (tdata->objectId == InvalidOid)
@@ -1177,6 +1177,8 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
Tcl_DStringAppendElement(&tcl_cmd, stroid);
pfree(stroid);
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->operation);
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->objecttype);
Tcl_DStringAppendElement(&tcl_cmd, tdata->schemaname);
Tcl_DStringAppendElement(&tcl_cmd, tdata->objectname);
}
@@ -1488,7 +1490,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
{
/* event trigger procedure has fixed args */
strcpy(proc_internal_args,
- "TG_when TG_tag TG_objectid TG_schemaname TG_objectname");
+ "TG_event TG_tag TG_objectid TG_operation TG_objecttype TG_schemaname TG_objectname");
}
/************************************************************
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 864e23b..2cb4839 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -560,9 +560,9 @@ $$ language pltcl immutable;
select tcl_date_week(2010,1,24);
select tcl_date_week(2001,10,24);
--- test pltcl command triggers
+-- test pltcl event triggers
create or replace function tclsnitch() returns event_trigger language pltcl as $$
- elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
+ elog NOTICE " tclsnitch: $TG_event $TG_tag $TG_schemaname $TG_objectname"
$$;
create event trigger tcl_snitch on command_start execute procedure tclsnitch();
diff --git a/src/test/regress/expected/event_triggers.out b/src/test/regress/expected/event_triggers.out
index 415605b..687d1c2 100644
--- a/src/test/regress/expected/event_triggers.out
+++ b/src/test/regress/expected/event_triggers.out
@@ -7,12 +7,9 @@ create or replace function snitch()
as $$
begin
-- can't output tg_objectid here that would break pg_regress
- raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ raise notice 'snitch: % % [% - %]', tg_event, tg_tag, tg_operation, tg_objecttype;
end;
$$;
---
--- TODO: REASSIGN OWNED and DROP OWNED
---
create event trigger any_t on command_start
execute procedure snitch();
create event trigger foo_t on command_start
@@ -62,212 +59,216 @@ alter event trigger foo_t disable;
alter event trigger foo_t enable;
alter event trigger foo_t rename to snitch;
create schema cmd;
-NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE SCHEMA [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE SCHEMA [CREATE - <NULL>]
create schema cmd2;
-NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE SCHEMA <NULL>.<NULL>
-create role regbob;
-alter event trigger snitch owner to regbob;
+NOTICE: snitch: command_start CREATE SCHEMA [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE SCHEMA [CREATE - <NULL>]
+create role regression_bob;
+alter event trigger snitch owner to regression_bob;
+ERROR: permission denied to change owner of event trigger "snitch"
+HINT: The owner of an event trigger must be a superuser.
+alter role regression_bob superuser;
+alter event trigger snitch owner to regression_bob;
create table cmd.foo(id bigserial primary key);
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE INDEX <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE INDEX [CREATE - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
create view cmd.v as select * from cmd.foo;
-NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE VIEW [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE VIEW [CREATE - <NULL>]
alter table cmd.foo add column t text;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
create table cmd.bar as select 1;
-NOTICE: snitch: command_start CREATE TABLE AS <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TABLE AS <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TABLE AS [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TABLE AS [CREATE - <NULL>]
drop table cmd.bar;
-NOTICE: snitch: command_start DROP TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TABLE [DROP - TABLE]
+NOTICE: snitch: command_start DROP TABLE [DROP - TABLE]
select 1 into cmd.bar;
-NOTICE: snitch: command_start SELECT INTO <NULL>.<NULL>
-NOTICE: snitch: command_start SELECT INTO <NULL>.<NULL>
+NOTICE: snitch: command_start SELECT INTO [<NULL> - <NULL>]
+NOTICE: snitch: command_start SELECT INTO [<NULL> - <NULL>]
drop table cmd.bar;
-NOTICE: snitch: command_start DROP TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TABLE [DROP - TABLE]
+NOTICE: snitch: command_start DROP TABLE [DROP - TABLE]
create table test9 (id int, stuff text);
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
alter table test9 rename to test;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE]
alter table test set schema cmd;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE]
alter table cmd.test rename column stuff to things;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - COLUMN]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - COLUMN]
alter table cmd.test add column alpha text;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set data type varchar(300);
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set default 'test';
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha drop default;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set statistics 78;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set storage plain;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set not null;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha drop not null;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha set (n_distinct = -0.78);
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test alter column alpha reset (n_distinct);
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test drop column alpha;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test add check (id > 2) not valid;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test add check (id < 800000);
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test set without cluster;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test set with oids;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
alter table cmd.test set without oids;
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TABLE [ALTER - <NULL>]
create sequence test_seq_;
-NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE SEQUENCE <NULL>.<NULL>
-alter sequence test_seq_ owner to regbob;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - <NULL>]
+alter sequence test_seq_ owner to regression_bob;
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence test_seq_ rename to test_seq;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE]
alter sequence test_seq set schema cmd;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE]
alter sequence cmd.test_seq start with 3;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq restart with 4;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq minvalue 3;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq no minvalue;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq maxvalue 900000;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq no maxvalue;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq cache 876;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq cycle;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
alter sequence cmd.test_seq no cycle;
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SEQUENCE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - <NULL>]
create view view_test as select id, things from cmd.test;
-NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE VIEW <NULL>.<NULL>
-alter view view_test owner to regbob;
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE VIEW [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE VIEW [CREATE - <NULL>]
+alter view view_test owner to regression_bob;
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
alter view view_test rename to view_test2;
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW]
+NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW]
alter view view_test2 set schema cmd;
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW]
+NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW]
alter view cmd.view_test2 alter column id set default 9;
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
alter view cmd.view_test2 alter column id drop default;
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER VIEW <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER VIEW [ALTER - <NULL>]
cluster cmd.foo using foo_pkey;
-NOTICE: snitch: command_start CLUSTER <NULL>.<NULL>
+NOTICE: snitch: command_start CLUSTER [<NULL> - <NULL>]
vacuum cmd.foo;
-NOTICE: snitch: command_start VACUUM <NULL>.<NULL>
-NOTICE: snitch: command_start VACUUM <NULL>.<NULL>
+NOTICE: snitch: command_start VACUUM [<NULL> - <NULL>]
+NOTICE: snitch: command_start VACUUM [<NULL> - <NULL>]
vacuum;
-NOTICE: snitch: command_start VACUUM <NULL>.<NULL>
-NOTICE: snitch: command_start VACUUM <NULL>.<NULL>
+NOTICE: snitch: command_start VACUUM [<NULL> - <NULL>]
+NOTICE: snitch: command_start VACUUM [<NULL> - <NULL>]
reindex table cmd.foo;
-NOTICE: snitch: command_start REINDEX <NULL>.<NULL>
-NOTICE: snitch: command_start REINDEX <NULL>.<NULL>
+NOTICE: snitch: command_start REINDEX [<NULL> - <NULL>]
+NOTICE: snitch: command_start REINDEX [<NULL> - <NULL>]
set session_replication_role to replica;
create table cmd.bar();
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TABLE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TABLE [CREATE - <NULL>]
reset session_replication_role;
create index idx_foo on cmd.foo(t);
-NOTICE: snitch: command_start CREATE INDEX <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE INDEX [CREATE - <NULL>]
reindex index cmd.idx_foo;
-NOTICE: snitch: command_start REINDEX <NULL>.<NULL>
-NOTICE: snitch: command_start REINDEX <NULL>.<NULL>
+NOTICE: snitch: command_start REINDEX [<NULL> - <NULL>]
+NOTICE: snitch: command_start REINDEX [<NULL> - <NULL>]
drop index cmd.idx_foo;
-NOTICE: snitch: command_start DROP INDEX <NULL>.<NULL>
+NOTICE: snitch: command_start DROP INDEX [DROP - INDEX]
create function fun(int) returns text language sql
as $$ select t from cmd.foo where id = $1; $$;
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
alter function fun(int) strict;
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - <NULL>]
alter function fun(int) rename to notfun;
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
alter function notfun(int) set schema cmd;
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-alter function cmd.notfun(int) owner to regbob;
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
+alter function cmd.notfun(int) owner to regression_bob;
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION]
alter function cmd.notfun(int) cost 77;
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER FUNCTION [ALTER - <NULL>]
drop function cmd.notfun(int);
-NOTICE: snitch: command_start DROP FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start DROP FUNCTION [DROP - FUNCTION]
create function cmd.plus1(int) returns bigint language sql
as $$ select $1::bigint + 1; $$;
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
-NOTICE: snitch: command_start CREATE OPERATOR <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE OPERATOR <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE OPERATOR [CREATE - OPERATOR]
+NOTICE: snitch: command_start CREATE OPERATOR [CREATE - OPERATOR]
alter operator cmd.+!(int, NONE) set schema public;
-NOTICE: snitch: command_start ALTER OPERATOR <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER OPERATOR <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER OPERATOR [ALTER - OPERATOR]
+NOTICE: snitch: command_start ALTER OPERATOR [ALTER - OPERATOR]
drop operator public.+!(int, NONE);
-NOTICE: snitch: command_start DROP OPERATOR <NULL>.<NULL>
+NOTICE: snitch: command_start DROP OPERATOR [DROP - OPERATOR]
create aggregate cmd.avg (float8)
(
sfunc = float8_accum,
@@ -275,130 +276,130 @@ create aggregate cmd.avg (float8)
finalfunc = float8_avg,
initcond = '{0,0,0}'
);
-NOTICE: snitch: command_start CREATE AGGREGATE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE AGGREGATE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE AGGREGATE [CREATE - AGGREGATE]
+NOTICE: snitch: command_start CREATE AGGREGATE [CREATE - AGGREGATE]
alter aggregate cmd.avg(float8) set schema public;
-NOTICE: snitch: command_start ALTER AGGREGATE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER AGGREGATE [ALTER - AGGREGATE]
drop aggregate public.avg(float8);
-NOTICE: snitch: command_start DROP AGGREGATE <NULL>.<NULL>
-NOTICE: snitch: command_start DROP AGGREGATE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP AGGREGATE [DROP - AGGREGATE]
+NOTICE: snitch: command_start DROP AGGREGATE [DROP - AGGREGATE]
create collation cmd.french (LOCALE = 'fr_FR');
-NOTICE: snitch: command_start CREATE COLLATION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE COLLATION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE COLLATION [CREATE - COLLATION]
+NOTICE: snitch: command_start CREATE COLLATION [CREATE - COLLATION]
alter collation cmd.french rename to francais;
-NOTICE: snitch: command_start ALTER COLLATION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER COLLATION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER COLLATION [ALTER - COLLATION]
+NOTICE: snitch: command_start ALTER COLLATION [ALTER - COLLATION]
create type cmd.compfoo AS (f1 int, f2 text);
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
alter type cmd.compfoo add attribute f3 text;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
create type cmd.type_test AS (a integer, b integer, c text);
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
-alter type cmd.type_test owner to regbob;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
+alter type cmd.type_test owner to regression_bob;
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
alter type cmd.type_test rename to type_test2;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
alter type cmd.type_test2 set schema public;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE]
alter type public.type_test2 rename attribute a to z;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - ATTRIBUTE]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - ATTRIBUTE]
alter type public.type_test2 add attribute alpha text;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
alter type public.type_test2 alter attribute alpha set data type char(90);
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
alter type public.type_test2 drop attribute alpha;
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
drop type cmd.compfoo;
-NOTICE: snitch: command_start DROP TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TYPE [DROP - TYPE]
drop type public.type_test2;
-NOTICE: snitch: command_start DROP TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TYPE [DROP - TYPE]
create type cmd.bug_status as enum ('new', 'open', 'closed');
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TYPE [CREATE - <NULL>]
alter type cmd.bug_status add value 'wontfix';
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TYPE <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER TYPE [ALTER - <NULL>]
create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$');
-NOTICE: snitch: command_start CREATE DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE DOMAIN [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE DOMAIN [CREATE - <NULL>]
alter domain cmd.us_postal_code set not null;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
alter domain cmd.us_postal_code set default 90210;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
alter domain cmd.us_postal_code drop default;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
alter domain cmd.us_postal_code drop not null;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$');
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
alter domain cmd.us_postal_code drop constraint dummy_constraint;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-alter domain cmd.us_postal_code owner to regbob;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - <NULL>]
+alter domain cmd.us_postal_code owner to regression_bob;
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN]
alter domain cmd.us_postal_code set schema cmd2;
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN]
+NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN]
drop domain cmd2.us_postal_code;
-NOTICE: snitch: command_start DROP DOMAIN <NULL>.<NULL>
-NOTICE: snitch: command_start DROP DOMAIN <NULL>.<NULL>
+NOTICE: snitch: command_start DROP DOMAIN [DROP - DOMAIN]
+NOTICE: snitch: command_start DROP DOMAIN [DROP - DOMAIN]
create function cmd.trigfunc() returns trigger language plpgsql as
$$ begin raise notice 'trigfunc'; end;$$;
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
-NOTICE: snitch: command_start CREATE TRIGGER <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TRIGGER <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TRIGGER [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE TRIGGER [CREATE - <NULL>]
alter trigger footg on cmd.foo rename to foo_trigger;
-NOTICE: snitch: command_start ALTER TRIGGER <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER TRIGGER <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TRIGGER [ALTER - TRIGGER]
+NOTICE: snitch: command_start ALTER TRIGGER [ALTER - TRIGGER]
drop trigger foo_trigger on cmd.foo;
-NOTICE: snitch: command_start DROP TRIGGER <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TRIGGER <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TRIGGER [DROP - TRIGGER]
+NOTICE: snitch: command_start DROP TRIGGER [DROP - TRIGGER]
create conversion test for 'utf8' to 'sjis' from utf8_to_sjis;
-NOTICE: snitch: command_start CREATE CONVERSION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE CONVERSION [CREATE - <NULL>]
create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis;
-NOTICE: snitch: command_start CREATE CONVERSION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE CONVERSION [CREATE - <NULL>]
alter conversion test2 rename to test3;
-NOTICE: snitch: command_start ALTER CONVERSION <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER CONVERSION <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER CONVERSION [ALTER - CONVERSION]
+NOTICE: snitch: command_start ALTER CONVERSION [ALTER - CONVERSION]
drop conversion test3;
-NOTICE: snitch: command_start DROP CONVERSION <NULL>.<NULL>
+NOTICE: snitch: command_start DROP CONVERSION [DROP - CONVERSION]
drop conversion test;
-NOTICE: snitch: command_start DROP CONVERSION <NULL>.<NULL>
+NOTICE: snitch: command_start DROP CONVERSION [DROP - CONVERSION]
create operator class test_op_class
for type anyenum using hash as
operator 1 =,
function 1 hashenum(anyenum);
-NOTICE: snitch: command_start CREATE OPERATOR CLASS <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE OPERATOR CLASS <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE OPERATOR CLASS [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE OPERATOR CLASS [CREATE - <NULL>]
create text search configuration test (parser = "default");
-NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION [CREATE - TEXT SEARCH CONFIGURATION]
+NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION [CREATE - TEXT SEARCH CONFIGURATION]
create text search dictionary test_stem (
template = snowball,
language = 'english', stopwords = 'english'
);
-NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY [CREATE - TEXT SEARCH DICTIONARY]
+NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY [CREATE - TEXT SEARCH DICTIONARY]
alter text search dictionary test_stem (StopWords = dutch );
-NOTICE: snitch: command_start ALTER TEXT SEARCH DICTIONARY <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER TEXT SEARCH DICTIONARY [ALTER - <NULL>]
create text search parser test_parser (
start = prsd_start,
gettoken = prsd_nexttoken,
@@ -406,38 +407,38 @@ create text search parser test_parser (
lextypes = prsd_lextype,
headline = prsd_headline
);
-NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER [CREATE - TEXT SEARCH PARSER]
+NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER [CREATE - TEXT SEARCH PARSER]
create text search template test_template (
init = dsimple_init,
lexize = dsimple_lexize
);
-NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE [CREATE - TEXT SEARCH TEMPLATE]
+NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE [CREATE - TEXT SEARCH TEMPLATE]
drop text search configuration test;
-NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION [DROP - TEXT SEARCH CONFIGURATION]
+NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION [DROP - TEXT SEARCH CONFIGURATION]
drop text search dictionary test_stem;
-NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY [DROP - TEXT SEARCH DICTIONARY]
+NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY [DROP - TEXT SEARCH DICTIONARY]
drop text search parser test_parser;
-NOTICE: snitch: command_start DROP TEXT SEARCH PARSER <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TEXT SEARCH PARSER <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TEXT SEARCH PARSER [DROP - TEXT SEARCH PARSER]
+NOTICE: snitch: command_start DROP TEXT SEARCH PARSER [DROP - TEXT SEARCH PARSER]
drop text search template test_template;
-NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE <NULL>.<NULL>
-NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE <NULL>.<NULL>
+NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE [DROP - TEXT SEARCH TEMPLATE]
+NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE [DROP - TEXT SEARCH TEMPLATE]
create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$;
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE FUNCTION <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE FUNCTION [CREATE - <NULL>]
create cast (text as int4) with function cmd.testcast(text) as assignment;
-NOTICE: snitch: command_start CREATE CAST <NULL>.<NULL>
-NOTICE: snitch: command_start CREATE CAST <NULL>.<NULL>
+NOTICE: snitch: command_start CREATE CAST [CREATE - <NULL>]
+NOTICE: snitch: command_start CREATE CAST [CREATE - <NULL>]
alter schema cmd rename to cmd1;
-NOTICE: snitch: command_start ALTER SCHEMA <NULL>.<NULL>
-NOTICE: snitch: command_start ALTER SCHEMA <NULL>.<NULL>
+NOTICE: snitch: command_start ALTER SCHEMA [ALTER - SCHEMA]
+NOTICE: snitch: command_start ALTER SCHEMA [ALTER - SCHEMA]
drop schema cmd1 cascade;
-NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
-NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
+NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA]
+NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA]
NOTICE: drop cascades to 12 other objects
DETAIL: drop cascades to table cmd1.foo
drop cascades to view cmd1.v
@@ -452,15 +453,17 @@ drop cascades to function cmd1.trigfunc()
drop cascades to function cmd1.testcast(text)
drop cascades to cast from text to integer
drop schema cmd2 cascade;
-NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
-NOTICE: snitch: command_start DROP SCHEMA <NULL>.<NULL>
+NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA]
+NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA]
-- fail because owning event trigger snitch
-drop role regbob;
-ERROR: role "regbob" cannot be dropped because some objects depend on it
+drop role regression_bob;
+ERROR: role "regression_bob" cannot be dropped because some objects depend on it
DETAIL: owner of event trigger snitch
drop event trigger any_t;
-drop event trigger snitch;
-drop role regbob;
+drop event trigger if exists snitch;
+drop event trigger if exists snitch;
+NOTICE: event trigger "snitch" does not exist, skipping
+drop role regression_bob;
create table onerow(id integer);
create or replace function insert_one_row()
returns event_trigger
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 0dac40f..70eab92 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -134,11 +134,10 @@ WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
- oid | typname | oid | proname
-------+---------------+------+------------
- 1790 | refcursor | 46 | textin
- 3838 | event_trigger | 2300 | trigger_in
-(2 rows)
+ oid | typname | oid | proname
+------+-----------+-----+---------
+ 1790 | refcursor | 46 | textin
+(1 row)
-- Varlena array types will point to array_in
-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
@@ -178,11 +177,10 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1)))
ORDER BY 1;
- oid | typname | oid | proname
-------+---------------+------+-------------
- 1790 | refcursor | 47 | textout
- 3838 | event_trigger | 2301 | trigger_out
-(2 rows)
+ oid | typname | oid | proname
+------+-----------+-----+---------
+ 1790 | refcursor | 47 | textout
+(1 row)
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
diff --git a/src/test/regress/sql/event_triggers.sql b/src/test/regress/sql/event_triggers.sql
index cf25ee7..9ca1a28 100644
--- a/src/test/regress/sql/event_triggers.sql
+++ b/src/test/regress/sql/event_triggers.sql
@@ -7,14 +7,10 @@ create or replace function snitch()
as $$
begin
-- can't output tg_objectid here that would break pg_regress
- raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ raise notice 'snitch: % % [% - %]', tg_event, tg_tag, tg_operation, tg_objecttype;
end;
$$;
---
--- TODO: REASSIGN OWNED and DROP OWNED
---
-
create event trigger any_t on command_start
execute procedure snitch();
@@ -68,9 +64,11 @@ alter event trigger foo_t rename to snitch;
create schema cmd;
create schema cmd2;
-create role regbob;
-alter event trigger snitch owner to regbob;
+create role regression_bob;
+alter event trigger snitch owner to regression_bob;
+alter role regression_bob superuser;
+alter event trigger snitch owner to regression_bob;
create table cmd.foo(id bigserial primary key);
create view cmd.v as select * from cmd.foo;
@@ -103,7 +101,7 @@ alter table cmd.test set with oids;
alter table cmd.test set without oids;
create sequence test_seq_;
-alter sequence test_seq_ owner to regbob;
+alter sequence test_seq_ owner to regression_bob;
alter sequence test_seq_ rename to test_seq;
alter sequence test_seq set schema cmd;
alter sequence cmd.test_seq start with 3;
@@ -117,7 +115,7 @@ alter sequence cmd.test_seq cycle;
alter sequence cmd.test_seq no cycle;
create view view_test as select id, things from cmd.test;
-alter view view_test owner to regbob;
+alter view view_test owner to regression_bob;
alter view view_test rename to view_test2;
alter view view_test2 set schema cmd;
alter view cmd.view_test2 alter column id set default 9;
@@ -142,7 +140,7 @@ as $$ select t from cmd.foo where id = $1; $$;
alter function fun(int) strict;
alter function fun(int) rename to notfun;
alter function notfun(int) set schema cmd;
-alter function cmd.notfun(int) owner to regbob;
+alter function cmd.notfun(int) owner to regression_bob;
alter function cmd.notfun(int) cost 77;
drop function cmd.notfun(int);
@@ -170,7 +168,7 @@ create type cmd.compfoo AS (f1 int, f2 text);
alter type cmd.compfoo add attribute f3 text;
create type cmd.type_test AS (a integer, b integer, c text);
-alter type cmd.type_test owner to regbob;
+alter type cmd.type_test owner to regression_bob;
alter type cmd.type_test rename to type_test2;
alter type cmd.type_test2 set schema public;
alter type public.type_test2 rename attribute a to z;
@@ -191,7 +189,7 @@ alter domain cmd.us_postal_code drop default;
alter domain cmd.us_postal_code drop not null;
alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$');
alter domain cmd.us_postal_code drop constraint dummy_constraint;
-alter domain cmd.us_postal_code owner to regbob;
+alter domain cmd.us_postal_code owner to regression_bob;
alter domain cmd.us_postal_code set schema cmd2;
drop domain cmd2.us_postal_code;
@@ -248,11 +246,12 @@ drop schema cmd1 cascade;
drop schema cmd2 cascade;
-- fail because owning event trigger snitch
-drop role regbob;
+drop role regression_bob;
drop event trigger any_t;
-drop event trigger snitch;
-drop role regbob;
+drop event trigger if exists snitch;
+drop event trigger if exists snitch;
+drop role regression_bob;
create table onerow(id integer);
event_triggers_v1.8.patch.gzapplication/octet-streamDownload
� 6�O�]}W�F��?�b�i�7!��9v��Lm�4�'�#���SYr�q�s������V��6��J�� H��������H=}��YN`oD!��L�
��-/�D�x���l��E
����1���nln�1:����m�70���7p����g���q8?hy������Y�c7g�&���8t'���8L�����`6����jkC���@j�CP�:2�1*��L?c� 9�U��s?r��������@�i�;,��J������nc�����Rp�����K���1�B��o��7i�@ -:b���!I�b7��mItP�n���S�����K�c�Y�N-CyuE�H)g�����gr,�2�,���G,�K����s���q� �Dkl�l
$����S+��]��*��\�K!�U�|�/����E�'a���x���m%I8�-� ���]s
�U���|Vym��<�������;�igTlFq����}8��|KY�]�9��Ihj)��*�Z1L�@��_+V�E�C��4�bv�Y����dE��d6���h:=~�0H�`!����\���s#���
yt?Sn�l��2��u���s�X�3n%0�d��i�_�aS�#�q2,kL��}kd�}3Y���.I
O�]Nh���o�Q`q���&7xO��G�z+I�WVx����a�`|���]S���nF<B7���sm�M���kL�G���}���HW��~�M�r���1��w}lc��:� s<G�6T;y�` 7"��Z� ��4Qh�(X��5�&}?*[��6�����k��t�k��WY���h����������A� �\�Q#K���_� f 68c� +qA�K+�k�0�+�U�f��
�2���g���
7-����5����X�E����
?���N�U�;�5�������k=�3KB[�|-0"��� u��M=JC�%P�H*�?s;t�(��<>%�J��f������D(n��A��j�\�Z�� M��_���xQ�������5c�����Aw'�g��������Dm�������`Cx�5� 7� �f���Y��X��[~T�7��K��@2�k�����]#�b; L�cAJ��NG�b������)j�+�e[>���������-��������<��$�&e������`iu�0n005@���.����<�^2�����|��sj�t�����"�\�������o������W�����E�cm���@��C�-�����&��N+������v��n�����1�w@������� ��2IQb�N�C��sI�z�|�� (
Es4�)_�Cu����W.��5NHyC�q`Sy�����`�����D�2������� �0��X]uPn�s
��S�Lj�eX��������(�I���`�(�H<�V���O��5�I��@/ym�n�D +�������� r��TH�6�i:)b�<����d��������(>�&�xE��9��&v'��#��i&�9f�,B��Q��q�sRURp���a"S� m��4�7��'p�[����G<��i$3`�W�p` q���xo�&B��n$|Aj�R������ 0z���k�+�U3�#������������^��r�1��+�W?t\of�a1����EWA�i��4��������� nN�p������
;/��< H�� ��r���@�4/^�
1����B9@
O�0������sa.h����q�k��Y�O�=�WD�*'��!����x� #_����e�d������
��1�Y6�0��m-op�9G�YI��;���!�b�]��:I
��(W}�s��4��/�
�r��)���q>�E��`;���Q�,W<#�$+|yj!r��G�������Fa����C�
�1\EZ.X�}�>v'��p,m3��r�7@?oR��ET��30��`���MX�Y��&�[F���K�;�|�[h?(e�����Z(����r�!�i���2E�$���Z���t^����
����� ��1� ��1lE�W���b��tJX,m� �q��4�P�C�N+g��*z>���x���(��-/677 �k01S��[��a6ALKqH���05A�=��� �X�jB��)0��/�i������l���V�t{�����1�H�r�����J��.YdDP����� �l����=��J��#b���g���(��"%����8]G2HP��%D*�� ����,���|��l�p -q��9J��Xv����*�����l#���aELW��YI���� Um�\p�H� ?xB-�D�
���)���� x 4Ja%]=��5�"R�U�r"��.e ��4m����t���h�g���[k:��O,f\����g,eF�7 *���3����7d���U�����z���1��,}<�X��h�=j���)�9���o���]�<��G��i��<��;���N����5���F�����]����N{�f������{�w�~{\��������;��}zt�>������Q�=������w����I��C�Y��=i����/����n�y�Zo�^{K>GrlFl��wt>�=������������a��V�v�=9;�y�1���j.�g��9�$;��;=�3�����������pTW%<ta�=� �<lG�f������z�:$�u�uHk���\~?���5���Z�;$���N��ZsX���d��9��uN�Y�sVF�:8��!�s�H�X��Q�d}3G���{E����m~������g�,�9�e`��9.�p��\���f�$��OsIF�����guO$��M# ���D�<�;�DL�9�D�<}D<~?���5���Z'�������z'���z��r,�9a�g���"����"b���"�`�E�����������,�R������u�����w�E�[������9b��b�����~�SO� �o�0�wG����d���������-_2�X4+W5��NckC��������c���vaeu�|S������NV�
wU�������7� +W����!8�F��,�J�F3W A5�S����T��^J�^1L�^pL��p���lM&a�:ej��)��)SS���oJ�T[�B����e�/K�>��il>�I�~�!�����/���fY�_��X�P���zv�qW�e���b��N��z�*d)9�r���R����UlN���,u�o\��Ko�����b��������cQEeE�v�<���|=4�=_��X�/MS��8�J\Yi4�z;���Z
u�� }o����X������Ri�.F�d�U��\?��3�2H��i��l=�klnmn���b1���k0d���
u����N{;z%�w�w� ����[���
���������O��+�a0 �)Ur�'�oho��|K�d,��D���d������E���H����ts
f�L����Hi���z�yP�O F��V6D�rOZ�A�W5``�t�,{$�KQ���I2����:����(EZ��N����� �r^)��o��3+���)� ��������7��?���=�u�QgU�L)�
�,�{Z��~n�4������_��AIs�h*�S.�#�@��s�����\/!�w]'�Q�i(=������>���������3����EU(���Yr��QK.O���-��4�����[��t��W�
��h"����7������NK
������g�ss����� c�^����Y�E0��e#
N�e�M`�yN%f�D����� ��x���x�������+? pq���i����;�����G�D���wz���������3>�Mh ��N@���YZz� #��-`Q])-�)�d�f]+���%*l��������?P��!��l76A\%o�~��r@uB����t�a�6�j�XWl��@�!�����7*���v��
b�g$Y��.��aw��!�.��s@����lKD�@`���|+�heb�){7��HrQ2E�~q9��)��K���(�E��BdbXn��h�.��.�u����Qj�(�^)��`��c������rg+Q�7�MM*����������x@T��{�J� #*.��'B�8�5= ��K/��XGL�I���et^fG� ���Q���P+�R.�>`������t���C�� Z����
u+���R����X,������|�?�Z�H�DiQ
(�o)����{����4�����nQ,��NI*@�b[�W"H���u"�:�bg������
kE����~K��<N�'�����&!{=�1{]�f��/��p0k�����1"zQ �jT[��o���T���&������"�UA}f�H��4% Z;���7�S( ���q���K�r�j w9w�G�a�W�:�i���(.���+��/g�p8g��bAq���i���bSE�$��Bw �7NZ�X�W �������ZE�L���6���3��"