diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml index ac31332..3bbf1a4 100644 --- a/doc/src/sgml/event-trigger.sgml +++ b/doc/src/sgml/event-trigger.sgml @@ -12,7 +12,7 @@ PostgreSQL also provides event triggers. Unlike regular triggers, which are attached to a single table and capture only DML events, event triggers are global to a particular database and are capable of - capturing DDL events. + capturing DDL events or transaction commits. @@ -29,8 +29,9 @@ occurs in the database in which it is defined. Currently, the only supported events are ddl_command_start, - ddl_command_end - and sql_drop. + ddl_command_end, + sql_drop, and + transaction_commit. Support for additional events may be added in future releases. @@ -65,6 +66,15 @@ + A transaction_commit trigger is called at the end of a + transaction, just before any deferred triggers are fired, unless + no data changes have been made by the transaction, or + PostgreSQL is running in Single-User mode. This is so + that you can recover from a badly specified transaction_commit + trigger. + + + Event triggers (like other functions) cannot be executed in an aborted transaction. Thus, if a DDL command fails with an error, any associated ddl_command_end triggers will not be executed. Conversely, @@ -77,8 +87,13 @@ - For a complete list of commands supported by the event trigger mechanism, - see . + A transaction_commit trigger is also not called in an + aborted transaction. + + + + For a complete list of commands supported by the event trigger + mechanism, see . @@ -101,6 +116,11 @@ to intercept. A common use of such triggers is to restrict the range of DDL operations which users may perform. + + + transaction_commit triggers do not currently support + WHEN clauses. + diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 0591f3f..74fc04c 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1825,6 +1825,16 @@ CommitTransaction(void) Assert(s->parent == NULL); /* + * First fire any pre-commit triggers, so if they in turn cause any + * deferred triggers etc to fire this will be picked up below. + * Only fire them, though, if we have a real transaction ID and + * we're not running standalone. Not firing when standalone provides + * a way to recover from setting up a bad transaction trigger. + */ + if (s->transactionId != InvalidTransactionId && IsUnderPostmaster) + PreCommitTriggersFire(); + + /* * Do pre-commit processing that involves calling user-defined code, such * as triggers. Since closing cursors could queue trigger actions, * triggers could open cursors, etc, we have to keep looping until there's @@ -2030,6 +2040,16 @@ PrepareTransaction(void) Assert(s->parent == NULL); /* + * First fire any pre-commit triggers, so if they in turn cause any + * deferred triggers etc to fire this will be picked up below. + * Only fire them, though, if we have a real transaction ID and + * we're not running standalone. Not firing when standalone provides + * a way to recover from setting up a bad transaction trigger. + */ + if (s->transactionId != InvalidTransactionId && IsUnderPostmaster) + PreCommitTriggersFire(); + + /* * Do pre-commit processing that involves calling user-defined code, such * as triggers. Since closing cursors could queue trigger actions, * triggers could open cursors, etc, we have to keep looping until there's diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 328e2a8..f93441f 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -153,7 +153,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) /* Validate event name. */ if (strcmp(stmt->eventname, "ddl_command_start") != 0 && strcmp(stmt->eventname, "ddl_command_end") != 0 && - strcmp(stmt->eventname, "sql_drop") != 0) + strcmp(stmt->eventname, "sql_drop") != 0 && + strcmp(stmt->eventname, "transaction_commit") != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized event name \"%s\"", @@ -1291,3 +1292,42 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) return (Datum) 0; } + +/* + * PreCommitTriggersFire + * + * fire triggers set for the commit event. + * + * This will be called just before deferred RI and Constraint triggers are + * fired. + */ +void +PreCommitTriggersFire(void) +{ + List * trigger_list; + EventTriggerData trigdata; + List *runlist = NIL; + ListCell *lc; + + trigger_list = EventCacheLookup(EVT_Commit); + + foreach(lc, trigger_list) + { + EventTriggerCacheItem *item = lfirst(lc); + + runlist = lappend_oid(runlist, item->fnoid); + } + + /* don't spend any more time on this if no functions to run */ + if (runlist == NIL) + return; + + trigdata.type = T_EventTriggerData; + trigdata.event = "transaction_commit"; + trigdata.parsetree = NULL; + trigdata.tag = "COMMIT"; + + EventTriggerInvoke(runlist, &trigdata); + + list_free(runlist); +} diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c index c2242c4..c8bb1e6 100644 --- a/src/backend/utils/cache/evtcache.c +++ b/src/backend/utils/cache/evtcache.c @@ -169,6 +169,8 @@ BuildEventTriggerCache(void) event = EVT_DDLCommandEnd; else if (strcmp(evtevent, "sql_drop") == 0) event = EVT_SQLDrop; + else if (strcmp(evtevent, "transaction_commit") == 0) + event = EVT_Commit; else continue; diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index cb0e5d5..67bbb35 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -52,4 +52,6 @@ extern void EventTriggerEndCompleteQuery(void); extern bool trackDroppedObjectsNeeded(void); extern void EventTriggerSQLDropAddObject(ObjectAddress *object); +extern void PreCommitTriggersFire(void); + #endif /* EVENT_TRIGGER_H */ diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h index 43c6f61..60178ed 100644 --- a/src/include/utils/evtcache.h +++ b/src/include/utils/evtcache.h @@ -20,7 +20,8 @@ typedef enum { EVT_DDLCommandStart, EVT_DDLCommandEnd, - EVT_SQLDrop + EVT_SQLDrop, + EVT_Commit } EventTriggerEvent; typedef struct