aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2013-01-21 18:00:24 -0500
committerRobert Haas <rhaas@postgresql.org>2013-01-21 18:00:24 -0500
commit841a5150c575ccd89e4b03aec66eeeefb21f3cbe (patch)
tree38799645974b15dfa7745d1d9d5d0676c766b3bc
parent765cbfdc9263bf7c90b9d1f1044c6950b8b7088c (diff)
downloadpostgresql-841a5150c575ccd89e4b03aec66eeeefb21f3cbe.tar.gz
postgresql-841a5150c575ccd89e4b03aec66eeeefb21f3cbe.zip
Add ddl_command_end support for event triggers.
Dimitri Fontaine, with slight changes by me
-rw-r--r--doc/src/sgml/event-trigger.sgml93
-rw-r--r--src/backend/commands/event_trigger.c160
-rw-r--r--src/backend/tcop/utility.c273
-rw-r--r--src/backend/utils/cache/evtcache.c2
-rw-r--r--src/include/commands/event_trigger.h1
-rw-r--r--src/include/utils/evtcache.h3
-rw-r--r--src/test/regress/expected/event_trigger.out6
-rw-r--r--src/test/regress/sql/event_trigger.sql4
8 files changed, 390 insertions, 152 deletions
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index dc4e7614990..f9e27567684 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -27,8 +27,9 @@
<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>ddl_command_start</>. Support for
- additional events may be added in future releases.
+ supported events are <literal>ddl_command_start</>
+ and <literal>ddl_command_end</>. Support for additional events may be
+ added in future releases.
</para>
<para>
@@ -44,6 +45,13 @@
</para>
<para>
+ The <literal>ddl_command_end</> event occurs just before returning
+ control from the execution of a <literal>CREATE</>, <literal>ALTER</>,
+ or <literal>DROP</> commmand. It shares the same exceptions as
+ the <literal>ddl_command_start</> event.
+ </para>
+
+ <para>
For a complete list of commands supported by the event trigger mechanism,
see <xref linkend="event-trigger-matrix">.
</para>
@@ -84,328 +92,409 @@
<row>
<entry>command tag</entry>
<entry><literal>ddl_command_start</literal></entry>
+ <entry><literal>ddl_command_end</literal></entry>
</row>
</thead>
<tbody>
<row>
<entry align="left"><literal>ALTER AGGREGATE</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER CONVERSION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER EXTENSION</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER FUNCTION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER OPERATOR</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER SCHEMA</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER SERVER</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <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>
+ <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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER TRIGGER</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>ALTER VIEW</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE CAST</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE CONVERSION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE EXTENSION</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE FUNCTION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE LANGUAGE</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE RULE</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE SEQUENCE</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE TABLE</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <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>
+ <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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE TRIGGER</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>CREATE VIEW</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP CAST</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP CONVERSION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP EXTENSION</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP FUNCTION</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP LANGUAGE</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP RULE</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP SEQUENCE</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP TABLE</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP TRIGGER</literal></entry>
<entry align="center"><literal>X</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>
+ <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>
+ <entry align="center"><literal>X</literal></entry>
</row>
<row>
<entry align="left"><literal>DROP VIEW</literal></entry>
<entry align="center"><literal>X</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>
+ <entry align="center"><literal>X</literal></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9063187f5fa..dc40de2c024 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -125,7 +125,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
errhint("Must be superuser to create an event trigger.")));
/* Validate event name. */
- if (strcmp(stmt->eventname, "ddl_command_start") != 0)
+ if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
+ strcmp(stmt->eventname, "ddl_command_end") != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized event name \"%s\"",
@@ -527,6 +528,39 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
}
/*
+ * Return true when we want to fire given Event Trigger and false otherwise,
+ * filtering on the session replication role and the event trigger registered
+ * tags matching.
+ */
+static bool
+filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
+{
+ /*
+ * Filter by session replication role, knowing that we never see disabled
+ * items down here.
+ */
+ if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
+ {
+ if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
+ return false;
+ }
+ else
+ {
+ if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
+ return false;
+ }
+
+ /* Filter by tags, if any were specified. */
+ if (item->ntags != 0 && bsearch(&tag, item->tag,
+ item->ntags, sizeof(char *),
+ pg_qsort_strcmp) == NULL)
+ return false;
+
+ /* if we reach that point, we're not filtering out this item */
+ return true;
+}
+
+/*
* Fire ddl_command_start triggers.
*/
void
@@ -601,34 +635,105 @@ EventTriggerDDLCommandStart(Node *parsetree)
{
EventTriggerCacheItem *item = lfirst(lc);
- /* Filter by session replication role. */
- if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
- {
- if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
- continue;
- }
- else
+ if (filter_event_trigger(&tag, item))
{
- if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
- continue;
+ /* We must plan to fire this trigger. */
+ runlist = lappend_oid(runlist, item->fnoid);
}
+ }
+
+ /* Construct event trigger data. */
+ trigdata.type = T_EventTriggerData;
+ trigdata.event = "ddl_command_start";
+ trigdata.parsetree = parsetree;
+ trigdata.tag = tag;
+
+ /* Run the triggers. */
+ EventTriggerInvoke(runlist, &trigdata);
+
+ /* Cleanup. */
+ list_free(runlist);
+
+ /*
+ * Make sure anything the event triggers did will be visible to
+ * the main command.
+ */
+ CommandCounterIncrement();
+}
+
+/*
+ * Fire ddl_command_end triggers.
+ */
+void
+EventTriggerDDLCommandEnd(Node *parsetree)
+{
+ List *cachelist;
+ List *runlist = NIL;
+ ListCell *lc;
+ const char *tag;
+ EventTriggerData trigdata;
+
+ /*
+ * See EventTriggerDDLCommandStart for a discussion about why event
+ * triggers are disabled in single user mode.
+ */
+ if (!IsUnderPostmaster)
+ return;
- /* Filter by tags, if any were specified. */
- if (item->ntags != 0 && bsearch(&tag, item->tag,
- item->ntags, sizeof(char *),
- pg_qsort_strcmp) == NULL)
- continue;
+ /*
+ * See EventTriggerDDLCommandStart for a discussion about why this check is
+ * important.
+ *
+ */
+#ifdef USE_ASSERT_CHECKING
+ if (assert_enabled)
+ {
+ const char *dbgtag;
- /* We must plan to fire this trigger. */
- runlist = lappend_oid(runlist, item->fnoid);
+ dbgtag = CreateCommandTag(parsetree);
+ if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
+ elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
+ }
+#endif
+
+ /* Use cache to find triggers for this event; fast exit if none. */
+ cachelist = EventCacheLookup(EVT_DDLCommandEnd);
+ if (cachelist == NULL)
+ return;
+
+ /* Get the command tag. */
+ tag = CreateCommandTag(parsetree);
+
+ /*
+ * Filter list of event triggers by command tag, and copy them into
+ * our memory context. Once we start running the command trigers, or
+ * indeed once we do anything at all that touches the catalogs, an
+ * invalidation might leave cachelist pointing at garbage, so we must
+ * do this before we can do much else.
+ */
+ foreach (lc, cachelist)
+ {
+ EventTriggerCacheItem *item = lfirst(lc);
+
+ if (filter_event_trigger(&tag, item))
+ {
+ /* We must plan to fire this trigger. */
+ runlist = lappend_oid(runlist, item->fnoid);
+ }
}
/* Construct event trigger data. */
trigdata.type = T_EventTriggerData;
- trigdata.event = "ddl_command_start";
+ trigdata.event = "ddl_command_end";
trigdata.parsetree = parsetree;
trigdata.tag = tag;
+ /*
+ * Make sure anything the main command did will be visible to the
+ * event triggers.
+ */
+ CommandCounterIncrement();
+
/* Run the triggers. */
EventTriggerInvoke(runlist, &trigdata);
@@ -645,6 +750,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
MemoryContext context;
MemoryContext oldcontext;
ListCell *lc;
+ bool first = true;
/*
* Let's evaluate event triggers in their own memory context, so
@@ -665,6 +771,17 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
FunctionCallInfoData fcinfo;
PgStat_FunctionCallUsage fcusage;
+ /*
+ * We want each event trigger to be able to see the results of
+ * the previous event trigger's action. Caller is responsible
+ * for any command-counter increment that is needed between the
+ * event trigger and anything else in the transaction.
+ */
+ if (first)
+ first = false;
+ else
+ CommandCounterIncrement();
+
/* Look up the function */
fmgr_info(fnoid, &flinfo);
@@ -677,13 +794,6 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
/* Reclaim memory. */
MemoryContextReset(context);
-
- /*
- * We want each event trigger to be able to see the results of
- * the previous event trigger's action, and we want the main
- * command to be able to see the results of all event triggers.
- */
- CommandCounterIncrement();
}
/* Restore old memory context and delete the temporary one. */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index ad5e30384d9..598e20f91c3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -340,6 +340,34 @@ ProcessUtility(Node *parsetree,
dest, completionTag, context);
}
+#define InvokeDDLCommandEventTriggers(parsetree, fncall) \
+ do { \
+ if (isCompleteQuery) \
+ { \
+ EventTriggerDDLCommandStart(parsetree); \
+ } \
+ fncall; \
+ if (isCompleteQuery) \
+ { \
+ EventTriggerDDLCommandEnd(parsetree); \
+ } \
+ } while (0)
+
+#define InvokeDDLCommandEventTriggersIfSupported(parsetree, fncall, objtype) \
+ do { \
+ bool _supported = EventTriggerSupportsObjectType(objtype); \
+ \
+ if (_supported) \
+ { \
+ EventTriggerDDLCommandStart(parsetree); \
+ } \
+ fncall; \
+ if (_supported) \
+ { \
+ EventTriggerDDLCommandEnd(parsetree); \
+ } \
+ } while (0)
+
void
standard_ProcessUtility(Node *parsetree,
const char *queryString,
@@ -507,10 +535,10 @@ standard_ProcessUtility(Node *parsetree,
* relation and attribute manipulation
*/
case T_CreateSchemaStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateSchemaCommand((CreateSchemaStmt *) parsetree,
- queryString);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateSchemaCommand((CreateSchemaStmt *) parsetree,
+ queryString));
break;
case T_CreateStmt:
@@ -583,6 +611,9 @@ standard_ProcessUtility(Node *parsetree,
if (lnext(l) != NULL)
CommandCounterIncrement();
}
+
+ if (isCompleteQuery)
+ EventTriggerDDLCommandEnd(parsetree);
}
break;
@@ -604,63 +635,63 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateExtensionStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateExtension((CreateExtensionStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateExtension((CreateExtensionStmt *) parsetree));
break;
case T_AlterExtensionStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree));
break;
case T_AlterExtensionContentsStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree));
break;
case T_CreateFdwStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateForeignDataWrapper((CreateFdwStmt *) parsetree));
break;
case T_AlterFdwStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterForeignDataWrapper((AlterFdwStmt *) parsetree));
break;
case T_CreateForeignServerStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateForeignServer((CreateForeignServerStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateForeignServer((CreateForeignServerStmt *) parsetree));
break;
case T_AlterForeignServerStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterForeignServer((AlterForeignServerStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterForeignServer((AlterForeignServerStmt *) parsetree));
break;
case T_CreateUserMappingStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateUserMapping((CreateUserMappingStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateUserMapping((CreateUserMappingStmt *) parsetree));
break;
case T_AlterUserMappingStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterUserMapping((AlterUserMappingStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterUserMapping((AlterUserMappingStmt *) parsetree));
break;
case T_DropUserMappingStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- RemoveUserMapping((DropUserMappingStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ RemoveUserMapping((DropUserMappingStmt *) parsetree));
break;
case T_DropStmt:
@@ -689,6 +720,11 @@ standard_ProcessUtility(Node *parsetree,
RemoveObjects((DropStmt *) parsetree);
break;
}
+
+ if (isCompleteQuery
+ && EventTriggerSupportsObjectType(stmt->removeType))
+ EventTriggerDDLCommandEnd(parsetree);
+
break;
}
@@ -736,37 +772,29 @@ standard_ProcessUtility(Node *parsetree,
*/
case T_RenameStmt:
{
- RenameStmt *stmt;
+ RenameStmt *stmt = (RenameStmt *) parsetree;
- stmt = (RenameStmt *) parsetree;
- if (isCompleteQuery &&
- EventTriggerSupportsObjectType(stmt->renameType))
- EventTriggerDDLCommandStart(parsetree);
- ExecRenameStmt(stmt);
+ InvokeDDLCommandEventTriggersIfSupported(parsetree,
+ ExecRenameStmt(stmt),
+ stmt->renameType);
break;
}
case T_AlterObjectSchemaStmt:
{
- AlterObjectSchemaStmt *stmt;
-
- stmt = (AlterObjectSchemaStmt *) parsetree;
- if (isCompleteQuery &&
- EventTriggerSupportsObjectType(stmt->objectType))
- EventTriggerDDLCommandStart(parsetree);
- ExecAlterObjectSchemaStmt(stmt);
+ AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
+ InvokeDDLCommandEventTriggersIfSupported(parsetree,
+ ExecAlterObjectSchemaStmt(stmt),
+ stmt->objectType);
break;
}
case T_AlterOwnerStmt:
{
- AlterOwnerStmt *stmt;
-
- stmt = (AlterOwnerStmt *) parsetree;
- if (isCompleteQuery &&
- EventTriggerSupportsObjectType(stmt->objectType))
- EventTriggerDDLCommandStart(parsetree);
- ExecAlterOwnerStmt(stmt);
+ AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
+ InvokeDDLCommandEventTriggersIfSupported(parsetree,
+ ExecAlterOwnerStmt(stmt),
+ stmt->objectType);
break;
}
@@ -889,9 +917,9 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_AlterDefaultPrivilegesStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree));
break;
/*
@@ -950,47 +978,46 @@ standard_ProcessUtility(Node *parsetree,
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
-
- DefineCompositeType(stmt->typevar, stmt->coldeflist);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineCompositeType(stmt->typevar, stmt->coldeflist));
}
break;
case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineEnum((CreateEnumStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineEnum((CreateEnumStmt *) parsetree));
break;
case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineRange((CreateRangeStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineRange((CreateRangeStmt *) parsetree));
break;
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterEnum((AlterEnumStmt *) parsetree, isTopLevel);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterEnum((AlterEnumStmt *) parsetree, isTopLevel));
break;
case T_ViewStmt: /* CREATE VIEW */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineView((ViewStmt *) parsetree, queryString);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineView((ViewStmt *) parsetree, queryString));
break;
case T_CreateFunctionStmt: /* CREATE FUNCTION */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateFunction((CreateFunctionStmt *) parsetree, queryString);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateFunction((CreateFunctionStmt *) parsetree, queryString));
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterFunction((AlterFunctionStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterFunction((AlterFunctionStmt *) parsetree));
break;
case T_IndexStmt: /* CREATE INDEX */
@@ -1019,21 +1046,21 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_RuleStmt: /* CREATE RULE */
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineRule((RuleStmt *) parsetree, queryString);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineRule((RuleStmt *) parsetree, queryString));
break;
case T_CreateSeqStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineSequence((CreateSeqStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineSequence((CreateSeqStmt *) parsetree));
break;
case T_AlterSeqStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterSequence((AlterSeqStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterSequence((AlterSeqStmt *) parsetree));
break;
case T_DoStmt:
@@ -1131,10 +1158,10 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTableAsStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- ExecCreateTableAs((CreateTableAsStmt *) parsetree,
- queryString, params, completionTag);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ ExecCreateTableAs((CreateTableAsStmt *) parsetree,
+ queryString, params, completionTag));
break;
case T_VariableSetStmt:
@@ -1156,10 +1183,10 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
- InvalidOid, InvalidOid, false);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
+ InvalidOid, InvalidOid, false));
break;
case T_CreateEventTrigStmt:
@@ -1173,18 +1200,18 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreatePLangStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateProceduralLanguage((CreatePLangStmt *) parsetree));
break;
/*
* ******************************** DOMAIN statements ****
*/
case T_CreateDomainStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineDomain((CreateDomainStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineDomain((CreateDomainStmt *) parsetree));
break;
/*
@@ -1288,45 +1315,45 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateConversionStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateConversionCommand((CreateConversionStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateConversionCommand((CreateConversionStmt *) parsetree));
break;
case T_CreateCastStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- CreateCast((CreateCastStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ CreateCast((CreateCastStmt *) parsetree));
break;
case T_CreateOpClassStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineOpClass((CreateOpClassStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineOpClass((CreateOpClassStmt *) parsetree));
break;
case T_CreateOpFamilyStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- DefineOpFamily((CreateOpFamilyStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ DefineOpFamily((CreateOpFamilyStmt *) parsetree));
break;
case T_AlterOpFamilyStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterOpFamily((AlterOpFamilyStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterOpFamily((AlterOpFamilyStmt *) parsetree));
break;
case T_AlterTSDictionaryStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterTSDictionary((AlterTSDictionaryStmt *) parsetree));
break;
case T_AlterTSConfigurationStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
+ InvokeDDLCommandEventTriggers(
+ parsetree,
+ AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree));
break;
default:
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 38d89fa6a7a..34c61280c27 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -167,6 +167,8 @@ BuildEventTriggerCache(void)
evtevent = NameStr(form->evtevent);
if (strcmp(evtevent, "ddl_command_start") == 0)
event = EVT_DDLCommandStart;
+ else if (strcmp(evtevent, "ddl_command_end") == 0)
+ event = EVT_DDLCommandEnd;
else
continue;
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 6d84b15b086..74c150bd084 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -41,5 +41,6 @@ extern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId);
extern bool EventTriggerSupportsObjectType(ObjectType obtype);
extern void EventTriggerDDLCommandStart(Node *parsetree);
+extern void EventTriggerDDLCommandEnd(Node *parsetree);
#endif /* EVENT_TRIGGER_H */
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index 25a9b6f0369..c230995212d 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -18,7 +18,8 @@
typedef enum
{
- EVT_DDLCommandStart
+ EVT_DDLCommandStart,
+ EVT_DDLCommandEnd
} EventTriggerEvent;
typedef struct
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 5c8f323ed47..843e22c0bfb 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -16,6 +16,8 @@ ERROR: unrecognized event name "elephant_bootstrap"
-- OK
create event trigger regress_event_trigger on ddl_command_start
execute procedure test_event_trigger();
+create event trigger regress_event_trigger_end on ddl_command_end
+ execute procedure test_event_trigger();
-- should fail, food is not a valid filter variable
create event trigger regress_event_trigger2 on ddl_command_start
when food in ('sandwhich')
@@ -65,9 +67,10 @@ alter event trigger regress_event_trigger enable;
alter event trigger regress_event_trigger disable;
-- regress_event_trigger2 should fire, but not regress_event_trigger
create table event_trigger_fire1 (a int);
-NOTICE: test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE: test_event_trigger: ddl_command_end CREATE TABLE
-- but nothing should fire here
drop table event_trigger_fire1;
+NOTICE: test_event_trigger: ddl_command_end DROP TABLE
-- alter owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regression_bob;
ERROR: permission denied to change owner of event trigger "regress_event_trigger"
@@ -92,5 +95,6 @@ drop event trigger if exists regress_event_trigger2;
drop event trigger if exists regress_event_trigger2;
NOTICE: event trigger "regress_event_trigger2" does not exist, skipping
drop event trigger regress_event_trigger3;
+drop event trigger regress_event_trigger_end;
drop function test_event_trigger();
drop role regression_bob;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 699e092cb10..acd003254cd 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -18,6 +18,9 @@ create event trigger regress_event_trigger on elephant_bootstrap
create event trigger regress_event_trigger on ddl_command_start
execute procedure test_event_trigger();
+create event trigger regress_event_trigger_end on ddl_command_end
+ execute procedure test_event_trigger();
+
-- should fail, food is not a valid filter variable
create event trigger regress_event_trigger2 on ddl_command_start
when food in ('sandwhich')
@@ -96,5 +99,6 @@ drop role regression_bob;
drop event trigger if exists regress_event_trigger2;
drop event trigger if exists regress_event_trigger2;
drop event trigger regress_event_trigger3;
+drop event trigger regress_event_trigger_end;
drop function test_event_trigger();
drop role regression_bob;