]> granicus.if.org Git - postgresql/commitdiff
Add ddl_command_end support for event triggers.
authorRobert Haas <rhaas@postgresql.org>
Mon, 21 Jan 2013 23:00:24 +0000 (18:00 -0500)
committerRobert Haas <rhaas@postgresql.org>
Mon, 21 Jan 2013 23:00:24 +0000 (18:00 -0500)
Dimitri Fontaine, with slight changes by me

doc/src/sgml/event-trigger.sgml
src/backend/commands/event_trigger.c
src/backend/tcop/utility.c
src/backend/utils/cache/evtcache.c
src/include/commands/event_trigger.h
src/include/utils/evtcache.h
src/test/regress/expected/event_trigger.out
src/test/regress/sql/event_trigger.sql

index dc4e76149900184e00247667fb67763856812436..f9e2756768490b622142017b3a1dd1ae313f26a9 100644 (file)
@@ -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>
      <literal>CREATE TABLE AS</literal>.
    </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">.
        <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>
index 9063187f5faf598792e093c56707f42c46036297..dc40de2c02483f92b9c131636973b7eb077109dd 100644 (file)
@@ -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\"",
@@ -526,6 +527,39 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
        return oid;
 }
 
+/*
+ * 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.
  */
@@ -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. */
index ad5e30384d99844818d6678976e805d29c90cd28..598e20f91c3685db73e16eaaf8e9ef4f16069182 100644 (file)
@@ -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:
index 38d89fa6a7adb584ecd493e7cd70a67f94e45e3b..34c61280c27c08e0f82a0395d9f71d10aab4623b 100644 (file)
@@ -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;
 
index 6d84b15b086d213e72d34a3d4960f1d49b6a7dd3..74c150bd0846f30f7755529b8fb55522569cd656 100644 (file)
@@ -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 */
index 25a9b6f03699a568664a1960e78e0abb2c59cc22..c230995212dc1ff0b40f8c769048734d8b5ca31d 100644 (file)
@@ -18,7 +18,8 @@
 
 typedef enum
 {
-       EVT_DDLCommandStart
+       EVT_DDLCommandStart,
+       EVT_DDLCommandEnd
 } EventTriggerEvent;
 
 typedef struct
index 5c8f323ed473f8d12287beacf73ba621b209a35e..843e22c0bfb2767563288ac81316d5f5c6c4eb29 100644 (file)
@@ -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;
index 699e092cb10d7e6df62876b3b54434c922ccccd9..acd003254cdf015c23c8a93b79d7db78e0c7e7e7 100644 (file)
@@ -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;