@@ -125,7 +125,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
125125errhint ("Must be superuser to create an event trigger." )));
126126
127127/* Validate event name. */
128- if (strcmp (stmt -> eventname ,"ddl_command_start" )!= 0 )
128+ if (strcmp (stmt -> eventname ,"ddl_command_start" )!= 0 &&
129+ strcmp (stmt -> eventname ,"ddl_command_end" )!= 0 )
129130ereport (ERROR ,
130131(errcode (ERRCODE_SYNTAX_ERROR ),
131132errmsg ("unrecognized event name \"%s\"" ,
@@ -526,6 +527,39 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
526527return oid ;
527528}
528529
530+ /*
531+ * Return true when we want to fire given Event Trigger and false otherwise,
532+ * filtering on the session replication role and the event trigger registered
533+ * tags matching.
534+ */
535+ static bool
536+ filter_event_trigger (const char * * tag ,EventTriggerCacheItem * item )
537+ {
538+ /*
539+ * Filter by session replication role, knowing that we never see disabled
540+ * items down here.
541+ */
542+ if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA )
543+ {
544+ if (item -> enabled == TRIGGER_FIRES_ON_ORIGIN )
545+ return false;
546+ }
547+ else
548+ {
549+ if (item -> enabled == TRIGGER_FIRES_ON_REPLICA )
550+ return false;
551+ }
552+
553+ /* Filter by tags, if any were specified. */
554+ if (item -> ntags != 0 && bsearch (& tag ,item -> tag ,
555+ item -> ntags ,sizeof (char * ),
556+ pg_qsort_strcmp )== NULL )
557+ return false;
558+
559+ /* if we reach that point, we're not filtering out this item */
560+ return true;
561+ }
562+
529563/*
530564 * Fire ddl_command_start triggers.
531565 */
@@ -601,34 +635,105 @@ EventTriggerDDLCommandStart(Node *parsetree)
601635{
602636EventTriggerCacheItem * item = lfirst (lc );
603637
604- /* Filter by session replication role. */
605- if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA )
606- {
607- if (item -> enabled == TRIGGER_FIRES_ON_ORIGIN )
608- continue ;
609- }
610- else
638+ if (filter_event_trigger (& tag ,item ))
611639{
612- if ( item -> enabled == TRIGGER_FIRES_ON_REPLICA )
613- continue ;
640+ /* We must plan to fire this trigger. */
641+ runlist = lappend_oid ( runlist , item -> fnoid ) ;
614642}
643+ }
644+
645+ /* Construct event trigger data. */
646+ trigdata .type = T_EventTriggerData ;
647+ trigdata .event = "ddl_command_start" ;
648+ trigdata .parsetree = parsetree ;
649+ trigdata .tag = tag ;
650+
651+ /* Run the triggers. */
652+ EventTriggerInvoke (runlist ,& trigdata );
653+
654+ /* Cleanup. */
655+ list_free (runlist );
656+
657+ /*
658+ * Make sure anything the event triggers did will be visible to
659+ * the main command.
660+ */
661+ CommandCounterIncrement ();
662+ }
663+
664+ /*
665+ * Fire ddl_command_end triggers.
666+ */
667+ void
668+ EventTriggerDDLCommandEnd (Node * parsetree )
669+ {
670+ List * cachelist ;
671+ List * runlist = NIL ;
672+ ListCell * lc ;
673+ const char * tag ;
674+ EventTriggerData trigdata ;
675+
676+ /*
677+ * See EventTriggerDDLCommandStart for a discussion about why event
678+ * triggers are disabled in single user mode.
679+ */
680+ if (!IsUnderPostmaster )
681+ return ;
615682
616- /* Filter by tags, if any were specified. */
617- if (item -> ntags != 0 && bsearch (& tag ,item -> tag ,
618- item -> ntags ,sizeof (char * ),
619- pg_qsort_strcmp )== NULL )
620- continue ;
683+ /*
684+ * See EventTriggerDDLCommandStart for a discussion about why this check is
685+ * important.
686+ *
687+ */
688+ #ifdef USE_ASSERT_CHECKING
689+ if (assert_enabled )
690+ {
691+ const char * dbgtag ;
621692
622- /* We must plan to fire this trigger. */
623- runlist = lappend_oid (runlist ,item -> fnoid );
693+ dbgtag = CreateCommandTag (parsetree );
694+ if (check_ddl_tag (dbgtag )!= EVENT_TRIGGER_COMMAND_TAG_OK )
695+ elog (ERROR ,"unexpected command tag \"%s\"" ,dbgtag );
696+ }
697+ #endif
698+
699+ /* Use cache to find triggers for this event; fast exit if none. */
700+ cachelist = EventCacheLookup (EVT_DDLCommandEnd );
701+ if (cachelist == NULL )
702+ return ;
703+
704+ /* Get the command tag. */
705+ tag = CreateCommandTag (parsetree );
706+
707+ /*
708+ * Filter list of event triggers by command tag, and copy them into
709+ * our memory context. Once we start running the command trigers, or
710+ * indeed once we do anything at all that touches the catalogs, an
711+ * invalidation might leave cachelist pointing at garbage, so we must
712+ * do this before we can do much else.
713+ */
714+ foreach (lc ,cachelist )
715+ {
716+ EventTriggerCacheItem * item = lfirst (lc );
717+
718+ if (filter_event_trigger (& tag ,item ))
719+ {
720+ /* We must plan to fire this trigger. */
721+ runlist = lappend_oid (runlist ,item -> fnoid );
722+ }
624723}
625724
626725/* Construct event trigger data. */
627726trigdata .type = T_EventTriggerData ;
628- trigdata .event = "ddl_command_start " ;
727+ trigdata .event = "ddl_command_end " ;
629728trigdata .parsetree = parsetree ;
630729trigdata .tag = tag ;
631730
731+ /*
732+ * Make sure anything the main command did will be visible to the
733+ * event triggers.
734+ */
735+ CommandCounterIncrement ();
736+
632737/* Run the triggers. */
633738EventTriggerInvoke (runlist ,& trigdata );
634739
@@ -645,6 +750,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
645750MemoryContext context ;
646751MemoryContext oldcontext ;
647752ListCell * lc ;
753+ bool first = true;
648754
649755/*
650756 * Let's evaluate event triggers in their own memory context, so
@@ -665,6 +771,17 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
665771FunctionCallInfoData fcinfo ;
666772PgStat_FunctionCallUsage fcusage ;
667773
774+ /*
775+ * We want each event trigger to be able to see the results of
776+ * the previous event trigger's action. Caller is responsible
777+ * for any command-counter increment that is needed between the
778+ * event trigger and anything else in the transaction.
779+ */
780+ if (first )
781+ first = false;
782+ else
783+ CommandCounterIncrement ();
784+
668785/* Look up the function */
669786fmgr_info (fnoid ,& flinfo );
670787
@@ -677,13 +794,6 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
677794
678795/* Reclaim memory. */
679796MemoryContextReset (context );
680-
681- /*
682- * We want each event trigger to be able to see the results of
683- * the previous event trigger's action, and we want the main
684- * command to be able to see the results of all event triggers.
685- */
686- CommandCounterIncrement ();
687797}
688798
689799/* Restore old memory context and delete the temporary one. */