@@ -125,7 +125,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
125125 errhint ("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 )
129130 ereport (ERROR ,
130131 (errcode (ERRCODE_SYNTAX_ERROR ),
131132 errmsg ("unrecognized event name \"%s\"" ,
@@ -526,6 +527,39 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
526527 return 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 {
602636 EventTriggerCacheItem * 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. */
627726 trigdata .type = T_EventTriggerData ;
628- trigdata .event = "ddl_command_start " ;
727+ trigdata .event = "ddl_command_end " ;
629728 trigdata .parsetree = parsetree ;
630729 trigdata .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. */
633738 EventTriggerInvoke (runlist , & trigdata );
634739
@@ -645,6 +750,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
645750 MemoryContext context ;
646751 MemoryContext oldcontext ;
647752 ListCell * 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)
665771 FunctionCallInfoData fcinfo ;
666772 PgStat_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 */
669786 fmgr_info (fnoid , & flinfo );
670787
@@ -677,13 +794,6 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
677794
678795 /* Reclaim memory. */
679796 MemoryContextReset (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. */
0 commit comments