diff options
author | Robert Haas <rhaas@postgresql.org> | 2013-01-21 18:00:24 -0500 |
---|---|---|
committer | Robert Haas <rhaas@postgresql.org> | 2013-01-21 18:00:24 -0500 |
commit | 841a5150c575ccd89e4b03aec66eeeefb21f3cbe (patch) | |
tree | 38799645974b15dfa7745d1d9d5d0676c766b3bc /src/backend/commands | |
parent | 765cbfdc9263bf7c90b9d1f1044c6950b8b7088c (diff) | |
download | postgresql-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.c | 160 |
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. */ |