From 841a5150c575ccd89e4b03aec66eeeefb21f3cbe Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Mon, 21 Jan 2013 18:00:24 -0500 Subject: [PATCH] Add ddl_command_end support for event triggers. Dimitri Fontaine, with slight changes by me --- doc/src/sgml/event-trigger.sgml | 93 ++++++- src/backend/commands/event_trigger.c | 162 ++++++++++-- src/backend/tcop/utility.c | 273 +++++++++++--------- src/backend/utils/cache/evtcache.c | 2 + src/include/commands/event_trigger.h | 1 + src/include/utils/evtcache.h | 3 +- src/test/regress/expected/event_trigger.out | 6 +- src/test/regress/sql/event_trigger.sql | 4 + 8 files changed, 391 insertions(+), 153 deletions(-) diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml index dc4e761499..f9e2756768 100644 --- a/doc/src/sgml/event-trigger.sgml +++ b/doc/src/sgml/event-trigger.sgml @@ -27,8 +27,9 @@ 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 ddl_command_start. Support for - additional events may be added in future releases. + supported events are ddl_command_start + and ddl_command_end. Support for additional events may be + added in future releases. @@ -43,6 +44,13 @@ CREATE TABLE AS. + + The ddl_command_end event occurs just before returning + control from the execution of a CREATE, ALTER, + or DROP commmand. It shares the same exceptions as + the ddl_command_start event. + + For a complete list of commands supported by the event trigger mechanism, see . @@ -84,328 +92,409 @@ command tag ddl_command_start + ddl_command_end ALTER AGGREGATE X + X ALTER COLLATION X + X ALTER CONVERSION X + X ALTER DOMAIN X + X ALTER EXTENSION X + X ALTER FOREIGN DATA WRAPPER X + X ALTER FOREIGN TABLE X + X ALTER FUNCTION X + X ALTER LANGUAGE X + X ALTER OPERATOR X + X ALTER OPERATOR CLASS X + X ALTER OPERATOR FAMILY X + X ALTER SCHEMA X + X ALTER SEQUENCE X + X ALTER SERVER X + X ALTER TABLE X + X ALTER TEXT SEARCH CONFIGURATION X + X ALTER TEXT SEARCH DICTIONARY X + X ALTER TEXT SEARCH PARSER X + X ALTER TEXT SEARCH TEMPLATE X + X ALTER TRIGGER X + X ALTER TYPE X + X ALTER USER MAPPING X + X ALTER VIEW X + X CREATE AGGREGATE X + X CREATE CAST X + X CREATE COLLATION X + X CREATE CONVERSION X + X CREATE DOMAIN X + X CREATE EXTENSION X + X CREATE FOREIGN DATA WRAPPER X + X CREATE FOREIGN TABLE X + X CREATE FUNCTION X + X CREATE INDEX X + X CREATE LANGUAGE X + X CREATE OPERATOR X + X CREATE OPERATOR CLASS X + X CREATE OPERATOR FAMILY X + X CREATE RULE X + X CREATE SCHEMA X + X CREATE SEQUENCE X + X CREATE SERVER X + X CREATE TABLE X + X CREATE TABLE AS X + X CREATE TEXT SEARCH CONFIGURATION X + X CREATE TEXT SEARCH DICTIONARY X + X CREATE TEXT SEARCH PARSER X + X CREATE TEXT SEARCH TEMPLATE X + X CREATE TRIGGER X + X CREATE TYPE X + X CREATE USER MAPPING X + X CREATE VIEW X + X DROP AGGREGATE X + X DROP CAST X + X DROP COLLATION X + X DROP CONVERSION X + X DROP DOMAIN X + X DROP EXTENSION X + X DROP FOREIGN DATA WRAPPER X + X DROP FOREIGN TABLE X + X DROP FUNCTION X + X DROP INDEX X + X DROP LANGUAGE X + X DROP OPERATOR X + X DROP OPERATOR CLASS X + X DROP OPERATOR FAMILY X + X DROP RULE X + X DROP SCHEMA X + X DROP SEQUENCE X + X DROP SERVER X + X DROP TABLE X + X DROP TEXT SEARCH CONFIGURATION X + X DROP TEXT SEARCH DICTIONARY X + X DROP TEXT SEARCH PARSER X + X DROP TEXT SEARCH TEMPLATE X + X DROP TRIGGER X + X DROP TYPE X + X DROP USER MAPPING X + X DROP VIEW X + X SELECT INTO X + X diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 9063187f5f..dc40de2c02 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\"", @@ -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,26 +635,11 @@ EventTriggerDDLCommandStart(Node *parsetree) { EventTriggerCacheItem *item = lfirst(lc); - /* Filter by session replication role. */ - if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + if (filter_event_trigger(&tag, item)) { - if (item->enabled == TRIGGER_FIRES_ON_ORIGIN) - continue; + /* We must plan to fire this trigger. */ + runlist = lappend_oid(runlist, item->fnoid); } - else - { - if (item->enabled == TRIGGER_FIRES_ON_REPLICA) - continue; - } - - /* Filter by tags, if any were specified. */ - if (item->ntags != 0 && bsearch(&tag, item->tag, - item->ntags, sizeof(char *), - pg_qsort_strcmp) == NULL) - continue; - - /* We must plan to fire this trigger. */ - runlist = lappend_oid(runlist, item->fnoid); } /* Construct event trigger data. */ @@ -634,6 +653,92 @@ EventTriggerDDLCommandStart(Node *parsetree) /* 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; + + /* + * See EventTriggerDDLCommandStart for a discussion about why this check is + * important. + * + */ +#ifdef USE_ASSERT_CHECKING + if (assert_enabled) + { + const char *dbgtag; + + 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_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); + + /* Cleanup. */ + list_free(runlist); } /* @@ -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 ad5e30384d..598e20f91c 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 38d89fa6a7..34c61280c2 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 6d84b15b08..74c150bd08 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 25a9b6f036..c230995212 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 5c8f323ed4..843e22c0bf 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 699e092cb1..acd003254c 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;