aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
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 /src/backend/commands
parent765cbfdc9263bf7c90b9d1f1044c6950b8b7088c (diff)
downloadpostgresql-841a5150c575ccd89e4b03aec66eeeefb21f3cbe.tar.gz
postgresql-841a5150c575ccd89e4b03aec66eeeefb21f3cbe.zip
Add ddl_command_end support for event triggers.
Dimitri Fontaine, with slight changes by me
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/event_trigger.c160
1 files changed, 135 insertions, 25 deletions
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. */