4242#include "utils/syscache.h"
4343#include "tcop/utility.h"
4444
45-
4645typedef struct EventTriggerQueryState
4746{
47+ /* sql_drop */
4848 slist_head SQLDropList ;
4949 bool in_sql_drop ;
50+
51+ /* table_rewrite */
52+ Oid table_rewrite_oid ; /* InvalidOid, or set for table_rewrite event */
53+ int table_rewrite_reason ; /* AT_REWRITE reason */
54+
5055 MemoryContext cxt ;
5156 struct EventTriggerQueryState * previous ;
5257} EventTriggerQueryState ;
@@ -119,11 +124,14 @@ static void AlterEventTriggerOwner_internal(Relation rel,
119124 HeapTuple tup ,
120125 Oid newOwnerId );
121126static event_trigger_command_tag_check_result check_ddl_tag (const char * tag );
127+ static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag (
128+ const char * tag );
122129static void error_duplicate_filter_variable (const char * defname );
123130static Datum filter_list_to_array (List * filterlist );
124131static Oid insert_event_trigger_tuple (char * trigname , char * eventname ,
125132 Oid evtOwner , Oid funcoid , List * tags );
126133static void validate_ddl_tags (const char * filtervar , List * taglist );
134+ static void validate_table_rewrite_tags (const char * filtervar , List * taglist );
127135static void EventTriggerInvoke (List * fn_oid_list , EventTriggerData * trigdata );
128136
129137/*
@@ -154,7 +162,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
154162 /* Validate event name. */
155163 if (strcmp (stmt -> eventname , "ddl_command_start" ) != 0 &&
156164 strcmp (stmt -> eventname , "ddl_command_end" ) != 0 &&
157- strcmp (stmt -> eventname , "sql_drop" ) != 0 )
165+ strcmp (stmt -> eventname , "sql_drop" ) != 0 &&
166+ strcmp (stmt -> eventname , "table_rewrite" ) != 0 )
158167 ereport (ERROR ,
159168 (errcode (ERRCODE_SYNTAX_ERROR ),
160169 errmsg ("unrecognized event name \"%s\"" ,
@@ -183,6 +192,9 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
183192 strcmp (stmt -> eventname , "sql_drop" ) == 0 )
184193 && tags != NULL )
185194 validate_ddl_tags ("tag" , tags );
195+ else if (strcmp (stmt -> eventname , "table_rewrite" ) == 0
196+ && tags != NULL )
197+ validate_table_rewrite_tags ("tag" , tags );
186198
187199 /*
188200 * Give user a nice error message if an event trigger of the same name
@@ -280,6 +292,38 @@ check_ddl_tag(const char *tag)
280292 return EVENT_TRIGGER_COMMAND_TAG_OK ;
281293}
282294
295+ /*
296+ * Validate DDL command tags for event table_rewrite.
297+ */
298+ static void
299+ validate_table_rewrite_tags (const char * filtervar , List * taglist )
300+ {
301+ ListCell * lc ;
302+
303+ foreach (lc , taglist )
304+ {
305+ const char * tag = strVal (lfirst (lc ));
306+ event_trigger_command_tag_check_result result ;
307+
308+ result = check_table_rewrite_ddl_tag (tag );
309+ if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED )
310+ ereport (ERROR ,
311+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
312+ /* translator: %s represents an SQL statement name */
313+ errmsg ("event triggers are not supported for %s" ,
314+ tag )));
315+ }
316+ }
317+
318+ static event_trigger_command_tag_check_result
319+ check_table_rewrite_ddl_tag (const char * tag )
320+ {
321+ if (pg_strcasecmp (tag , "ALTER TABLE" ) == 0 )
322+ return EVENT_TRIGGER_COMMAND_TAG_OK ;
323+
324+ return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED ;
325+ }
326+
283327/*
284328 * Complain about a duplicate filter variable.
285329 */
@@ -641,8 +685,18 @@ EventTriggerCommonSetup(Node *parsetree,
641685 const char * dbgtag ;
642686
643687 dbgtag = CreateCommandTag (parsetree );
644- if (check_ddl_tag (dbgtag ) != EVENT_TRIGGER_COMMAND_TAG_OK )
645- elog (ERROR , "unexpected command tag \"%s\"" , dbgtag );
688+ if (event == EVT_DDLCommandStart ||
689+ event == EVT_DDLCommandEnd ||
690+ event == EVT_SQLDrop )
691+ {
692+ if (check_ddl_tag (dbgtag ) != EVENT_TRIGGER_COMMAND_TAG_OK )
693+ elog (ERROR , "unexpected command tag \"%s\"" , dbgtag );
694+ }
695+ else if (event == EVT_TableRewrite )
696+ {
697+ if (check_table_rewrite_ddl_tag (dbgtag ) != EVENT_TRIGGER_COMMAND_TAG_OK )
698+ elog (ERROR , "unexpected command tag \"%s\"" , dbgtag );
699+ }
646700 }
647701#endif
648702
@@ -838,6 +892,80 @@ EventTriggerSQLDrop(Node *parsetree)
838892 list_free (runlist );
839893}
840894
895+
896+ /*
897+ * Fire table_rewrite triggers.
898+ */
899+ void
900+ EventTriggerTableRewrite (Node * parsetree , Oid tableOid , int reason )
901+ {
902+ List * runlist ;
903+ EventTriggerData trigdata ;
904+
905+ elog (DEBUG1 , "EventTriggerTableRewrite(%u)" , tableOid );
906+
907+ /*
908+ * Event Triggers are completely disabled in standalone mode. There are
909+ * (at least) two reasons for this:
910+ *
911+ * 1. A sufficiently broken event trigger might not only render the
912+ * database unusable, but prevent disabling itself to fix the situation.
913+ * In this scenario, restarting in standalone mode provides an escape
914+ * hatch.
915+ *
916+ * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
917+ * therefore will malfunction if pg_event_trigger's indexes are damaged.
918+ * To allow recovery from a damaged index, we need some operating mode
919+ * wherein event triggers are disabled. (Or we could implement
920+ * heapscan-and-sort logic for that case, but having disaster recovery
921+ * scenarios depend on code that's otherwise untested isn't appetizing.)
922+ */
923+ if (!IsUnderPostmaster )
924+ return ;
925+
926+ runlist = EventTriggerCommonSetup (parsetree ,
927+ EVT_TableRewrite ,
928+ "table_rewrite" ,
929+ & trigdata );
930+ if (runlist == NIL )
931+ return ;
932+
933+ /*
934+ * Make sure pg_event_trigger_table_rewrite_oid only works when running
935+ * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
936+ * when one trigger fails. (This is perhaps not necessary, as the
937+ * currentState variable will be removed shortly by our caller, but it
938+ * seems better to play safe.)
939+ */
940+ currentEventTriggerState -> table_rewrite_oid = tableOid ;
941+ currentEventTriggerState -> table_rewrite_reason = reason ;
942+
943+ /* Run the triggers. */
944+ PG_TRY ();
945+ {
946+ EventTriggerInvoke (runlist , & trigdata );
947+ }
948+ PG_CATCH ();
949+ {
950+ currentEventTriggerState -> table_rewrite_oid = InvalidOid ;
951+ currentEventTriggerState -> table_rewrite_reason = 0 ;
952+ PG_RE_THROW ();
953+ }
954+ PG_END_TRY ();
955+
956+ currentEventTriggerState -> table_rewrite_oid = InvalidOid ;
957+ currentEventTriggerState -> table_rewrite_reason = 0 ;
958+
959+ /* Cleanup. */
960+ list_free (runlist );
961+
962+ /*
963+ * Make sure anything the event triggers did will be visible to the main
964+ * command.
965+ */
966+ CommandCounterIncrement ();
967+ }
968+
841969/*
842970 * Invoke each event trigger in a list of event triggers.
843971 */
@@ -871,6 +999,8 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
871999 FunctionCallInfoData fcinfo ;
8721000 PgStat_FunctionCallUsage fcusage ;
8731001
1002+ elog (DEBUG1 , "EventTriggerInvoke %u" , fnoid );
1003+
8741004 /*
8751005 * We want each event trigger to be able to see the results of the
8761006 * previous event trigger's action. Caller is responsible for any
@@ -1026,8 +1156,9 @@ EventTriggerBeginCompleteQuery(void)
10261156 MemoryContext cxt ;
10271157
10281158 /*
1029- * Currently, sql_drop events are the only reason to have event trigger
1030- * state at all; so if there are none, don't install one.
1159+ * Currently, sql_drop and table_rewrite events are the only reason to
1160+ * have event trigger state at all; so if there are none, don't install
1161+ * one.
10311162 */
10321163 if (!trackDroppedObjectsNeeded ())
10331164 return false;
@@ -1041,6 +1172,7 @@ EventTriggerBeginCompleteQuery(void)
10411172 state -> cxt = cxt ;
10421173 slist_init (& (state -> SQLDropList ));
10431174 state -> in_sql_drop = false;
1175+ state -> table_rewrite_oid = InvalidOid ;
10441176
10451177 state -> previous = currentEventTriggerState ;
10461178 currentEventTriggerState = state ;
@@ -1080,8 +1212,9 @@ EventTriggerEndCompleteQuery(void)
10801212bool
10811213trackDroppedObjectsNeeded (void )
10821214{
1083- /* true if any sql_drop event trigger exists */
1084- return list_length (EventCacheLookup (EVT_SQLDrop )) > 0 ;
1215+ /* true if any sql_drop or table_rewrite event trigger exists */
1216+ return list_length (EventCacheLookup (EVT_SQLDrop )) > 0 ||
1217+ list_length (EventCacheLookup (EVT_TableRewrite )) > 0 ;
10851218}
10861219
10871220/*
@@ -1297,3 +1430,46 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
12971430
12981431 return (Datum ) 0 ;
12991432}
1433+
1434+ /*
1435+ * pg_event_trigger_table_rewrite_oid
1436+ *
1437+ * Make the Oid of the table going to be rewritten available to the user
1438+ * function run by the Event Trigger.
1439+ */
1440+ Datum
1441+ pg_event_trigger_table_rewrite_oid (PG_FUNCTION_ARGS )
1442+ {
1443+ /*
1444+ * Protect this function from being called out of context
1445+ */
1446+ if (!currentEventTriggerState ||
1447+ currentEventTriggerState -> table_rewrite_oid == InvalidOid )
1448+ ereport (ERROR ,
1449+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
1450+ errmsg ("%s can only be called in a table_rewrite event trigger function" ,
1451+ "pg_event_trigger_table_rewrite_oid()" )));
1452+
1453+ PG_RETURN_OID (currentEventTriggerState -> table_rewrite_oid );
1454+ }
1455+
1456+ /*
1457+ * pg_event_trigger_table_rewrite_reason
1458+ *
1459+ * Make the rewrite reason available to the user.
1460+ */
1461+ Datum
1462+ pg_event_trigger_table_rewrite_reason (PG_FUNCTION_ARGS )
1463+ {
1464+ /*
1465+ * Protect this function from being called out of context
1466+ */
1467+ if (!currentEventTriggerState ||
1468+ currentEventTriggerState -> table_rewrite_reason == 0 )
1469+ ereport (ERROR ,
1470+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
1471+ errmsg ("%s can only be called in a table_rewrite event trigger function" ,
1472+ "pg_event_trigger_table_rewrite_reason()" )));
1473+
1474+ PG_RETURN_INT32 (currentEventTriggerState -> table_rewrite_reason );
1475+ }
0 commit comments