Move routines to manipulate WAL into PostgreSQL::Test::Cluster
[pgsql.git] / src / backend / commands / event_trigger.c
blobedc2c988e2934a651ac7b0a0b51446614c4620b3
1 /*-------------------------------------------------------------------------
3 * event_trigger.c
4 * PostgreSQL EVENT TRIGGER support code.
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * IDENTIFICATION
10 * src/backend/commands/event_trigger.c
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
16 #include "access/heapam.h"
17 #include "access/htup_details.h"
18 #include "access/table.h"
19 #include "access/xact.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/objectaccess.h"
24 #include "catalog/pg_authid.h"
25 #include "catalog/pg_auth_members.h"
26 #include "catalog/pg_database.h"
27 #include "catalog/pg_event_trigger.h"
28 #include "catalog/pg_namespace.h"
29 #include "catalog/pg_opclass.h"
30 #include "catalog/pg_opfamily.h"
31 #include "catalog/pg_parameter_acl.h"
32 #include "catalog/pg_proc.h"
33 #include "catalog/pg_tablespace.h"
34 #include "catalog/pg_trigger.h"
35 #include "catalog/pg_ts_config.h"
36 #include "catalog/pg_type.h"
37 #include "commands/event_trigger.h"
38 #include "commands/extension.h"
39 #include "commands/trigger.h"
40 #include "funcapi.h"
41 #include "lib/ilist.h"
42 #include "miscadmin.h"
43 #include "parser/parse_func.h"
44 #include "pgstat.h"
45 #include "storage/lmgr.h"
46 #include "tcop/deparse_utility.h"
47 #include "tcop/utility.h"
48 #include "utils/acl.h"
49 #include "utils/builtins.h"
50 #include "utils/evtcache.h"
51 #include "utils/fmgroids.h"
52 #include "utils/fmgrprotos.h"
53 #include "utils/lsyscache.h"
54 #include "utils/memutils.h"
55 #include "utils/rel.h"
56 #include "utils/snapmgr.h"
57 #include "utils/syscache.h"
59 typedef struct EventTriggerQueryState
61 /* memory context for this state's objects */
62 MemoryContext cxt;
64 /* sql_drop */
65 slist_head SQLDropList;
66 bool in_sql_drop;
68 /* table_rewrite */
69 Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite
70 * event */
71 int table_rewrite_reason; /* AT_REWRITE reason */
73 /* Support for command collection */
74 bool commandCollectionInhibited;
75 CollectedCommand *currentCommand;
76 List *commandList; /* list of CollectedCommand; see
77 * deparse_utility.h */
78 struct EventTriggerQueryState *previous;
79 } EventTriggerQueryState;
81 static EventTriggerQueryState *currentEventTriggerState = NULL;
83 /* GUC parameter */
84 bool event_triggers = true;
86 /* Support for dropped objects */
87 typedef struct SQLDropObject
89 ObjectAddress address;
90 const char *schemaname;
91 const char *objname;
92 const char *objidentity;
93 const char *objecttype;
94 List *addrnames;
95 List *addrargs;
96 bool original;
97 bool normal;
98 bool istemp;
99 slist_node next;
100 } SQLDropObject;
102 static void AlterEventTriggerOwner_internal(Relation rel,
103 HeapTuple tup,
104 Oid newOwnerId);
105 static void error_duplicate_filter_variable(const char *defname);
106 static Datum filter_list_to_array(List *filterlist);
107 static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
108 Oid evtOwner, Oid funcoid, List *taglist);
109 static void validate_ddl_tags(const char *filtervar, List *taglist);
110 static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
111 static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
112 static const char *stringify_grant_objtype(ObjectType objtype);
113 static const char *stringify_adefprivs_objtype(ObjectType objtype);
114 static void SetDatabaseHasLoginEventTriggers(void);
117 * Create an event trigger.
120 CreateEventTrigger(CreateEventTrigStmt *stmt)
122 HeapTuple tuple;
123 Oid funcoid;
124 Oid funcrettype;
125 Oid evtowner = GetUserId();
126 ListCell *lc;
127 List *tags = NULL;
130 * It would be nice to allow database owners or even regular users to do
131 * this, but there are obvious privilege escalation risks which would have
132 * to somehow be plugged first.
134 if (!superuser())
135 ereport(ERROR,
136 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
137 errmsg("permission denied to create event trigger \"%s\"",
138 stmt->trigname),
139 errhint("Must be superuser to create an event trigger.")));
141 /* Validate event name. */
142 if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
143 strcmp(stmt->eventname, "ddl_command_end") != 0 &&
144 strcmp(stmt->eventname, "sql_drop") != 0 &&
145 strcmp(stmt->eventname, "login") != 0 &&
146 strcmp(stmt->eventname, "table_rewrite") != 0)
147 ereport(ERROR,
148 (errcode(ERRCODE_SYNTAX_ERROR),
149 errmsg("unrecognized event name \"%s\"",
150 stmt->eventname)));
152 /* Validate filter conditions. */
153 foreach(lc, stmt->whenclause)
155 DefElem *def = (DefElem *) lfirst(lc);
157 if (strcmp(def->defname, "tag") == 0)
159 if (tags != NULL)
160 error_duplicate_filter_variable(def->defname);
161 tags = (List *) def->arg;
163 else
164 ereport(ERROR,
165 (errcode(ERRCODE_SYNTAX_ERROR),
166 errmsg("unrecognized filter variable \"%s\"", def->defname)));
169 /* Validate tag list, if any. */
170 if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
171 strcmp(stmt->eventname, "ddl_command_end") == 0 ||
172 strcmp(stmt->eventname, "sql_drop") == 0)
173 && tags != NULL)
174 validate_ddl_tags("tag", tags);
175 else if (strcmp(stmt->eventname, "table_rewrite") == 0
176 && tags != NULL)
177 validate_table_rewrite_tags("tag", tags);
178 else if (strcmp(stmt->eventname, "login") == 0 && tags != NULL)
179 ereport(ERROR,
180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
181 errmsg("tag filtering is not supported for login event triggers")));
184 * Give user a nice error message if an event trigger of the same name
185 * already exists.
187 tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
188 if (HeapTupleIsValid(tuple))
189 ereport(ERROR,
190 (errcode(ERRCODE_DUPLICATE_OBJECT),
191 errmsg("event trigger \"%s\" already exists",
192 stmt->trigname)));
194 /* Find and validate the trigger function. */
195 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
196 funcrettype = get_func_rettype(funcoid);
197 if (funcrettype != EVENT_TRIGGEROID)
198 ereport(ERROR,
199 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
200 errmsg("function %s must return type %s",
201 NameListToString(stmt->funcname), "event_trigger")));
203 /* Insert catalog entries. */
204 return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
205 evtowner, funcoid, tags);
209 * Validate DDL command tags.
211 static void
212 validate_ddl_tags(const char *filtervar, List *taglist)
214 ListCell *lc;
216 foreach(lc, taglist)
218 const char *tagstr = strVal(lfirst(lc));
219 CommandTag commandTag = GetCommandTagEnum(tagstr);
221 if (commandTag == CMDTAG_UNKNOWN)
222 ereport(ERROR,
223 (errcode(ERRCODE_SYNTAX_ERROR),
224 errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
225 tagstr, filtervar)));
226 if (!command_tag_event_trigger_ok(commandTag))
227 ereport(ERROR,
228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
229 /* translator: %s represents an SQL statement name */
230 errmsg("event triggers are not supported for %s",
231 tagstr)));
236 * Validate DDL command tags for event table_rewrite.
238 static void
239 validate_table_rewrite_tags(const char *filtervar, List *taglist)
241 ListCell *lc;
243 foreach(lc, taglist)
245 const char *tagstr = strVal(lfirst(lc));
246 CommandTag commandTag = GetCommandTagEnum(tagstr);
248 if (!command_tag_table_rewrite_ok(commandTag))
249 ereport(ERROR,
250 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
251 /* translator: %s represents an SQL statement name */
252 errmsg("event triggers are not supported for %s",
253 tagstr)));
258 * Complain about a duplicate filter variable.
260 static void
261 error_duplicate_filter_variable(const char *defname)
263 ereport(ERROR,
264 (errcode(ERRCODE_SYNTAX_ERROR),
265 errmsg("filter variable \"%s\" specified more than once",
266 defname)));
270 * Insert the new pg_event_trigger row and record dependencies.
272 static Oid
273 insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtOwner,
274 Oid funcoid, List *taglist)
276 Relation tgrel;
277 Oid trigoid;
278 HeapTuple tuple;
279 Datum values[Natts_pg_event_trigger];
280 bool nulls[Natts_pg_event_trigger];
281 NameData evtnamedata,
282 evteventdata;
283 ObjectAddress myself,
284 referenced;
286 /* Open pg_event_trigger. */
287 tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
289 /* Build the new pg_trigger tuple. */
290 trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
291 Anum_pg_event_trigger_oid);
292 values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
293 memset(nulls, false, sizeof(nulls));
294 namestrcpy(&evtnamedata, trigname);
295 values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
296 namestrcpy(&evteventdata, eventname);
297 values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
298 values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
299 values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
300 values[Anum_pg_event_trigger_evtenabled - 1] =
301 CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
302 if (taglist == NIL)
303 nulls[Anum_pg_event_trigger_evttags - 1] = true;
304 else
305 values[Anum_pg_event_trigger_evttags - 1] =
306 filter_list_to_array(taglist);
308 /* Insert heap tuple. */
309 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
310 CatalogTupleInsert(tgrel, tuple);
311 heap_freetuple(tuple);
314 * Login event triggers have an additional flag in pg_database to enable
315 * faster lookups in hot codepaths. Set the flag unless already True.
317 if (strcmp(eventname, "login") == 0)
318 SetDatabaseHasLoginEventTriggers();
320 /* Depend on owner. */
321 recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
323 /* Depend on event trigger function. */
324 myself.classId = EventTriggerRelationId;
325 myself.objectId = trigoid;
326 myself.objectSubId = 0;
327 referenced.classId = ProcedureRelationId;
328 referenced.objectId = funcoid;
329 referenced.objectSubId = 0;
330 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
332 /* Depend on extension, if any. */
333 recordDependencyOnCurrentExtension(&myself, false);
335 /* Post creation hook for new event trigger */
336 InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
338 /* Close pg_event_trigger. */
339 table_close(tgrel, RowExclusiveLock);
341 return trigoid;
345 * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented
346 * by a DefElem whose value is a List of String nodes; in the catalog, we
347 * store the list of strings as a text array. This function transforms the
348 * former representation into the latter one.
350 * For cleanliness, we store command tags in the catalog as text. It's
351 * possible (although not currently anticipated) that we might have
352 * a case-sensitive filter variable in the future, in which case this would
353 * need some further adjustment.
355 static Datum
356 filter_list_to_array(List *filterlist)
358 ListCell *lc;
359 Datum *data;
360 int i = 0,
361 l = list_length(filterlist);
363 data = (Datum *) palloc(l * sizeof(Datum));
365 foreach(lc, filterlist)
367 const char *value = strVal(lfirst(lc));
368 char *result,
371 result = pstrdup(value);
372 for (p = result; *p; p++)
373 *p = pg_ascii_toupper((unsigned char) *p);
374 data[i++] = PointerGetDatum(cstring_to_text(result));
375 pfree(result);
378 return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
382 * Set pg_database.dathasloginevt flag for current database indicating that
383 * current database has on login event triggers.
385 void
386 SetDatabaseHasLoginEventTriggers(void)
388 /* Set dathasloginevt flag in pg_database */
389 Form_pg_database db;
390 Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
391 ItemPointerData otid;
392 HeapTuple tuple;
395 * Use shared lock to prevent a conflict with EventTriggerOnLogin() trying
396 * to reset pg_database.dathasloginevt flag. Note, this lock doesn't
397 * effectively blocks database or other objection. It's just custom lock
398 * tag used to prevent multiple backends changing
399 * pg_database.dathasloginevt flag.
401 LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
403 tuple = SearchSysCacheLockedCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
404 if (!HeapTupleIsValid(tuple))
405 elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
406 otid = tuple->t_self;
407 db = (Form_pg_database) GETSTRUCT(tuple);
408 if (!db->dathasloginevt)
410 db->dathasloginevt = true;
411 CatalogTupleUpdate(pg_db, &otid, tuple);
412 CommandCounterIncrement();
414 UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
415 table_close(pg_db, RowExclusiveLock);
416 heap_freetuple(tuple);
420 * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
423 AlterEventTrigger(AlterEventTrigStmt *stmt)
425 Relation tgrel;
426 HeapTuple tup;
427 Oid trigoid;
428 Form_pg_event_trigger evtForm;
429 char tgenabled = stmt->tgenabled;
431 tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
433 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
434 CStringGetDatum(stmt->trigname));
435 if (!HeapTupleIsValid(tup))
436 ereport(ERROR,
437 (errcode(ERRCODE_UNDEFINED_OBJECT),
438 errmsg("event trigger \"%s\" does not exist",
439 stmt->trigname)));
441 evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
442 trigoid = evtForm->oid;
444 if (!object_ownercheck(EventTriggerRelationId, trigoid, GetUserId()))
445 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
446 stmt->trigname);
448 /* tuple is a copy, so we can modify it below */
449 evtForm->evtenabled = tgenabled;
451 CatalogTupleUpdate(tgrel, &tup->t_self, tup);
454 * Login event triggers have an additional flag in pg_database to enable
455 * faster lookups in hot codepaths. Set the flag unless already True.
457 if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
458 tgenabled != TRIGGER_DISABLED)
459 SetDatabaseHasLoginEventTriggers();
461 InvokeObjectPostAlterHook(EventTriggerRelationId,
462 trigoid, 0);
464 /* clean up */
465 heap_freetuple(tup);
466 table_close(tgrel, RowExclusiveLock);
468 return trigoid;
472 * Change event trigger's owner -- by name
474 ObjectAddress
475 AlterEventTriggerOwner(const char *name, Oid newOwnerId)
477 Oid evtOid;
478 HeapTuple tup;
479 Form_pg_event_trigger evtForm;
480 Relation rel;
481 ObjectAddress address;
483 rel = table_open(EventTriggerRelationId, RowExclusiveLock);
485 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
487 if (!HeapTupleIsValid(tup))
488 ereport(ERROR,
489 (errcode(ERRCODE_UNDEFINED_OBJECT),
490 errmsg("event trigger \"%s\" does not exist", name)));
492 evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
493 evtOid = evtForm->oid;
495 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
497 ObjectAddressSet(address, EventTriggerRelationId, evtOid);
499 heap_freetuple(tup);
501 table_close(rel, RowExclusiveLock);
503 return address;
507 * Change event trigger owner, by OID
509 void
510 AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId)
512 HeapTuple tup;
513 Relation rel;
515 rel = table_open(EventTriggerRelationId, RowExclusiveLock);
517 tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
519 if (!HeapTupleIsValid(tup))
520 ereport(ERROR,
521 (errcode(ERRCODE_UNDEFINED_OBJECT),
522 errmsg("event trigger with OID %u does not exist", trigOid)));
524 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
526 heap_freetuple(tup);
528 table_close(rel, RowExclusiveLock);
532 * Internal workhorse for changing an event trigger's owner
534 static void
535 AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
537 Form_pg_event_trigger form;
539 form = (Form_pg_event_trigger) GETSTRUCT(tup);
541 if (form->evtowner == newOwnerId)
542 return;
544 if (!object_ownercheck(EventTriggerRelationId, form->oid, GetUserId()))
545 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
546 NameStr(form->evtname));
548 /* New owner must be a superuser */
549 if (!superuser_arg(newOwnerId))
550 ereport(ERROR,
551 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
552 errmsg("permission denied to change owner of event trigger \"%s\"",
553 NameStr(form->evtname)),
554 errhint("The owner of an event trigger must be a superuser.")));
556 form->evtowner = newOwnerId;
557 CatalogTupleUpdate(rel, &tup->t_self, tup);
559 /* Update owner dependency reference */
560 changeDependencyOnOwner(EventTriggerRelationId,
561 form->oid,
562 newOwnerId);
564 InvokeObjectPostAlterHook(EventTriggerRelationId,
565 form->oid, 0);
569 * get_event_trigger_oid - Look up an event trigger by name to find its OID.
571 * If missing_ok is false, throw an error if trigger not found. If
572 * true, just return InvalidOid.
575 get_event_trigger_oid(const char *trigname, bool missing_ok)
577 Oid oid;
579 oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
580 CStringGetDatum(trigname));
581 if (!OidIsValid(oid) && !missing_ok)
582 ereport(ERROR,
583 (errcode(ERRCODE_UNDEFINED_OBJECT),
584 errmsg("event trigger \"%s\" does not exist", trigname)));
585 return oid;
589 * Return true when we want to fire given Event Trigger and false otherwise,
590 * filtering on the session replication role and the event trigger registered
591 * tags matching.
593 static bool
594 filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
597 * Filter by session replication role, knowing that we never see disabled
598 * items down here.
600 if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
602 if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
603 return false;
605 else
607 if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
608 return false;
611 /* Filter by tags, if any were specified. */
612 if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
613 return false;
615 /* if we reach that point, we're not filtering out this item */
616 return true;
619 static CommandTag
620 EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
622 if (event == EVT_Login)
623 return CMDTAG_LOGIN;
624 else
625 return CreateCommandTag(parsetree);
629 * Setup for running triggers for the given event. Return value is an OID list
630 * of functions to run; if there are any, trigdata is filled with an
631 * appropriate EventTriggerData for them to receive.
633 static List *
634 EventTriggerCommonSetup(Node *parsetree,
635 EventTriggerEvent event, const char *eventstr,
636 EventTriggerData *trigdata, bool unfiltered)
638 CommandTag tag;
639 List *cachelist;
640 ListCell *lc;
641 List *runlist = NIL;
644 * We want the list of command tags for which this procedure is actually
645 * invoked to match up exactly with the list that CREATE EVENT TRIGGER
646 * accepts. This debugging cross-check will throw an error if this
647 * function is invoked for a command tag that CREATE EVENT TRIGGER won't
648 * accept. (Unfortunately, there doesn't seem to be any simple, automated
649 * way to verify that CREATE EVENT TRIGGER doesn't accept extra stuff that
650 * never reaches this control point.)
652 * If this cross-check fails for you, you probably need to either adjust
653 * standard_ProcessUtility() not to invoke event triggers for the command
654 * type in question, or you need to adjust event_trigger_ok to accept the
655 * relevant command tag.
657 #ifdef USE_ASSERT_CHECKING
659 CommandTag dbgtag;
661 dbgtag = EventTriggerGetTag(parsetree, event);
663 if (event == EVT_DDLCommandStart ||
664 event == EVT_DDLCommandEnd ||
665 event == EVT_SQLDrop ||
666 event == EVT_Login)
668 if (!command_tag_event_trigger_ok(dbgtag))
669 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
671 else if (event == EVT_TableRewrite)
673 if (!command_tag_table_rewrite_ok(dbgtag))
674 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
677 #endif
679 /* Use cache to find triggers for this event; fast exit if none. */
680 cachelist = EventCacheLookup(event);
681 if (cachelist == NIL)
682 return NIL;
684 /* Get the command tag. */
685 tag = EventTriggerGetTag(parsetree, event);
688 * Filter list of event triggers by command tag, and copy them into our
689 * memory context. Once we start running the command triggers, or indeed
690 * once we do anything at all that touches the catalogs, an invalidation
691 * might leave cachelist pointing at garbage, so we must do this before we
692 * can do much else.
694 foreach(lc, cachelist)
696 EventTriggerCacheItem *item = lfirst(lc);
698 if (unfiltered || filter_event_trigger(tag, item))
700 /* We must plan to fire this trigger. */
701 runlist = lappend_oid(runlist, item->fnoid);
705 /* Don't spend any more time on this if no functions to run */
706 if (runlist == NIL)
707 return NIL;
709 trigdata->type = T_EventTriggerData;
710 trigdata->event = eventstr;
711 trigdata->parsetree = parsetree;
712 trigdata->tag = tag;
714 return runlist;
718 * Fire ddl_command_start triggers.
720 void
721 EventTriggerDDLCommandStart(Node *parsetree)
723 List *runlist;
724 EventTriggerData trigdata;
727 * Event Triggers are completely disabled in standalone mode. There are
728 * (at least) two reasons for this:
730 * 1. A sufficiently broken event trigger might not only render the
731 * database unusable, but prevent disabling itself to fix the situation.
732 * In this scenario, restarting in standalone mode provides an escape
733 * hatch.
735 * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
736 * therefore will malfunction if pg_event_trigger's indexes are damaged.
737 * To allow recovery from a damaged index, we need some operating mode
738 * wherein event triggers are disabled. (Or we could implement
739 * heapscan-and-sort logic for that case, but having disaster recovery
740 * scenarios depend on code that's otherwise untested isn't appetizing.)
742 * Additionally, event triggers can be disabled with a superuser-only GUC
743 * to make fixing database easier as per 1 above.
745 if (!IsUnderPostmaster || !event_triggers)
746 return;
748 runlist = EventTriggerCommonSetup(parsetree,
749 EVT_DDLCommandStart,
750 "ddl_command_start",
751 &trigdata, false);
752 if (runlist == NIL)
753 return;
755 /* Run the triggers. */
756 EventTriggerInvoke(runlist, &trigdata);
758 /* Cleanup. */
759 list_free(runlist);
762 * Make sure anything the event triggers did will be visible to the main
763 * command.
765 CommandCounterIncrement();
769 * Fire ddl_command_end triggers.
771 void
772 EventTriggerDDLCommandEnd(Node *parsetree)
774 List *runlist;
775 EventTriggerData trigdata;
778 * See EventTriggerDDLCommandStart for a discussion about why event
779 * triggers are disabled in single user mode or via GUC.
781 if (!IsUnderPostmaster || !event_triggers)
782 return;
785 * Also do nothing if our state isn't set up, which it won't be if there
786 * weren't any relevant event triggers at the start of the current DDL
787 * command. This test might therefore seem optional, but it's important
788 * because EventTriggerCommonSetup might find triggers that didn't exist
789 * at the time the command started. Although this function itself
790 * wouldn't crash, the event trigger functions would presumably call
791 * pg_event_trigger_ddl_commands which would fail. Better to do nothing
792 * until the next command.
794 if (!currentEventTriggerState)
795 return;
797 runlist = EventTriggerCommonSetup(parsetree,
798 EVT_DDLCommandEnd, "ddl_command_end",
799 &trigdata, false);
800 if (runlist == NIL)
801 return;
804 * Make sure anything the main command did will be visible to the event
805 * triggers.
807 CommandCounterIncrement();
809 /* Run the triggers. */
810 EventTriggerInvoke(runlist, &trigdata);
812 /* Cleanup. */
813 list_free(runlist);
817 * Fire sql_drop triggers.
819 void
820 EventTriggerSQLDrop(Node *parsetree)
822 List *runlist;
823 EventTriggerData trigdata;
826 * See EventTriggerDDLCommandStart for a discussion about why event
827 * triggers are disabled in single user mode or via a GUC.
829 if (!IsUnderPostmaster || !event_triggers)
830 return;
833 * Use current state to determine whether this event fires at all. If
834 * there are no triggers for the sql_drop event, then we don't have
835 * anything to do here. Note that dropped object collection is disabled
836 * if this is the case, so even if we were to try to run, the list would
837 * be empty.
839 if (!currentEventTriggerState ||
840 slist_is_empty(&currentEventTriggerState->SQLDropList))
841 return;
843 runlist = EventTriggerCommonSetup(parsetree,
844 EVT_SQLDrop, "sql_drop",
845 &trigdata, false);
848 * Nothing to do if run list is empty. Note this typically can't happen,
849 * because if there are no sql_drop events, then objects-to-drop wouldn't
850 * have been collected in the first place and we would have quit above.
851 * But it could occur if event triggers were dropped partway through.
853 if (runlist == NIL)
854 return;
857 * Make sure anything the main command did will be visible to the event
858 * triggers.
860 CommandCounterIncrement();
863 * Make sure pg_event_trigger_dropped_objects only works when running
864 * these triggers. Use PG_TRY to ensure in_sql_drop is reset even when
865 * one trigger fails. (This is perhaps not necessary, as the currentState
866 * variable will be removed shortly by our caller, but it seems better to
867 * play safe.)
869 currentEventTriggerState->in_sql_drop = true;
871 /* Run the triggers. */
872 PG_TRY();
874 EventTriggerInvoke(runlist, &trigdata);
876 PG_FINALLY();
878 currentEventTriggerState->in_sql_drop = false;
880 PG_END_TRY();
882 /* Cleanup. */
883 list_free(runlist);
887 * Fire login event triggers if any are present. The dathasloginevt
888 * pg_database flag is left unchanged when an event trigger is dropped to avoid
889 * complicating the codepath in the case of multiple event triggers. This
890 * function will instead unset the flag if no trigger is defined.
892 void
893 EventTriggerOnLogin(void)
895 List *runlist;
896 EventTriggerData trigdata;
899 * See EventTriggerDDLCommandStart for a discussion about why event
900 * triggers are disabled in single user mode or via a GUC. We also need a
901 * database connection (some background workers don't have it).
903 if (!IsUnderPostmaster || !event_triggers ||
904 !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
905 return;
907 StartTransactionCommand();
908 runlist = EventTriggerCommonSetup(NULL,
909 EVT_Login, "login",
910 &trigdata, false);
912 if (runlist != NIL)
915 * Event trigger execution may require an active snapshot.
917 PushActiveSnapshot(GetTransactionSnapshot());
919 /* Run the triggers. */
920 EventTriggerInvoke(runlist, &trigdata);
922 /* Cleanup. */
923 list_free(runlist);
925 PopActiveSnapshot();
929 * There is no active login event trigger, but our
930 * pg_database.dathasloginevt is set. Try to unset this flag. We use the
931 * lock to prevent concurrent SetDatabaseHasLoginEventTriggers(), but we
932 * don't want to hang the connection waiting on the lock. Thus, we are
933 * just trying to acquire the lock conditionally.
935 else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
936 0, AccessExclusiveLock))
939 * The lock is held. Now we need to recheck that login event triggers
940 * list is still empty. Once the list is empty, we know that even if
941 * there is a backend which concurrently inserts/enables a login event
942 * trigger, it will update pg_database.dathasloginevt *afterwards*.
944 runlist = EventTriggerCommonSetup(NULL,
945 EVT_Login, "login",
946 &trigdata, true);
948 if (runlist == NIL)
950 Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
951 HeapTuple tuple;
952 void *state;
953 Form_pg_database db;
954 ScanKeyData key[1];
956 /* Fetch a copy of the tuple to scribble on */
957 ScanKeyInit(&key[0],
958 Anum_pg_database_oid,
959 BTEqualStrategyNumber, F_OIDEQ,
960 ObjectIdGetDatum(MyDatabaseId));
962 systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
963 NULL, 1, key, &tuple, &state);
965 if (!HeapTupleIsValid(tuple))
966 elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
968 db = (Form_pg_database) GETSTRUCT(tuple);
969 if (db->dathasloginevt)
971 db->dathasloginevt = false;
974 * Do an "in place" update of the pg_database tuple. Doing
975 * this instead of regular updates serves two purposes. First,
976 * that avoids possible waiting on the row-level lock. Second,
977 * that avoids dealing with TOAST.
979 systable_inplace_update_finish(state, tuple);
981 else
982 systable_inplace_update_cancel(state);
983 table_close(pg_db, RowExclusiveLock);
984 heap_freetuple(tuple);
986 else
988 list_free(runlist);
991 CommitTransactionCommand();
996 * Fire table_rewrite triggers.
998 void
999 EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
1001 List *runlist;
1002 EventTriggerData trigdata;
1005 * See EventTriggerDDLCommandStart for a discussion about why event
1006 * triggers are disabled in single user mode or via a GUC.
1008 if (!IsUnderPostmaster || !event_triggers)
1009 return;
1012 * Also do nothing if our state isn't set up, which it won't be if there
1013 * weren't any relevant event triggers at the start of the current DDL
1014 * command. This test might therefore seem optional, but it's
1015 * *necessary*, because EventTriggerCommonSetup might find triggers that
1016 * didn't exist at the time the command started.
1018 if (!currentEventTriggerState)
1019 return;
1021 runlist = EventTriggerCommonSetup(parsetree,
1022 EVT_TableRewrite,
1023 "table_rewrite",
1024 &trigdata, false);
1025 if (runlist == NIL)
1026 return;
1029 * Make sure pg_event_trigger_table_rewrite_oid only works when running
1030 * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
1031 * when one trigger fails. (This is perhaps not necessary, as the
1032 * currentState variable will be removed shortly by our caller, but it
1033 * seems better to play safe.)
1035 currentEventTriggerState->table_rewrite_oid = tableOid;
1036 currentEventTriggerState->table_rewrite_reason = reason;
1038 /* Run the triggers. */
1039 PG_TRY();
1041 EventTriggerInvoke(runlist, &trigdata);
1043 PG_FINALLY();
1045 currentEventTriggerState->table_rewrite_oid = InvalidOid;
1046 currentEventTriggerState->table_rewrite_reason = 0;
1048 PG_END_TRY();
1050 /* Cleanup. */
1051 list_free(runlist);
1054 * Make sure anything the event triggers did will be visible to the main
1055 * command.
1057 CommandCounterIncrement();
1061 * Invoke each event trigger in a list of event triggers.
1063 static void
1064 EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
1066 MemoryContext context;
1067 MemoryContext oldcontext;
1068 ListCell *lc;
1069 bool first = true;
1071 /* Guard against stack overflow due to recursive event trigger */
1072 check_stack_depth();
1075 * Let's evaluate event triggers in their own memory context, so that any
1076 * leaks get cleaned up promptly.
1078 context = AllocSetContextCreate(CurrentMemoryContext,
1079 "event trigger context",
1080 ALLOCSET_DEFAULT_SIZES);
1081 oldcontext = MemoryContextSwitchTo(context);
1083 /* Call each event trigger. */
1084 foreach(lc, fn_oid_list)
1086 LOCAL_FCINFO(fcinfo, 0);
1087 Oid fnoid = lfirst_oid(lc);
1088 FmgrInfo flinfo;
1089 PgStat_FunctionCallUsage fcusage;
1091 elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
1094 * We want each event trigger to be able to see the results of the
1095 * previous event trigger's action. Caller is responsible for any
1096 * command-counter increment that is needed between the event trigger
1097 * and anything else in the transaction.
1099 if (first)
1100 first = false;
1101 else
1102 CommandCounterIncrement();
1104 /* Look up the function */
1105 fmgr_info(fnoid, &flinfo);
1107 /* Call the function, passing no arguments but setting a context. */
1108 InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
1109 InvalidOid, (Node *) trigdata, NULL);
1110 pgstat_init_function_usage(fcinfo, &fcusage);
1111 FunctionCallInvoke(fcinfo);
1112 pgstat_end_function_usage(&fcusage, true);
1114 /* Reclaim memory. */
1115 MemoryContextReset(context);
1118 /* Restore old memory context and delete the temporary one. */
1119 MemoryContextSwitchTo(oldcontext);
1120 MemoryContextDelete(context);
1124 * Do event triggers support this object type?
1126 * See also event trigger documentation in event-trigger.sgml.
1128 bool
1129 EventTriggerSupportsObjectType(ObjectType obtype)
1131 switch (obtype)
1133 case OBJECT_DATABASE:
1134 case OBJECT_TABLESPACE:
1135 case OBJECT_ROLE:
1136 case OBJECT_PARAMETER_ACL:
1137 /* no support for global objects (except subscriptions) */
1138 return false;
1139 case OBJECT_EVENT_TRIGGER:
1140 /* no support for event triggers on event triggers */
1141 return false;
1142 default:
1143 return true;
1148 * Do event triggers support this object class?
1150 * See also event trigger documentation in event-trigger.sgml.
1152 bool
1153 EventTriggerSupportsObject(const ObjectAddress *object)
1155 switch (object->classId)
1157 case DatabaseRelationId:
1158 case TableSpaceRelationId:
1159 case AuthIdRelationId:
1160 case AuthMemRelationId:
1161 case ParameterAclRelationId:
1162 /* no support for global objects (except subscriptions) */
1163 return false;
1164 case EventTriggerRelationId:
1165 /* no support for event triggers on event triggers */
1166 return false;
1167 default:
1168 return true;
1173 * Prepare event trigger state for a new complete query to run, if necessary;
1174 * returns whether this was done. If it was, EventTriggerEndCompleteQuery must
1175 * be called when the query is done, regardless of whether it succeeds or fails
1176 * -- so use of a PG_TRY block is mandatory.
1178 bool
1179 EventTriggerBeginCompleteQuery(void)
1181 EventTriggerQueryState *state;
1182 MemoryContext cxt;
1185 * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
1186 * reason to have event trigger state at all; so if there are none, don't
1187 * install one.
1189 if (!trackDroppedObjectsNeeded())
1190 return false;
1192 cxt = AllocSetContextCreate(TopMemoryContext,
1193 "event trigger state",
1194 ALLOCSET_DEFAULT_SIZES);
1195 state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
1196 state->cxt = cxt;
1197 slist_init(&(state->SQLDropList));
1198 state->in_sql_drop = false;
1199 state->table_rewrite_oid = InvalidOid;
1201 state->commandCollectionInhibited = currentEventTriggerState ?
1202 currentEventTriggerState->commandCollectionInhibited : false;
1203 state->currentCommand = NULL;
1204 state->commandList = NIL;
1205 state->previous = currentEventTriggerState;
1206 currentEventTriggerState = state;
1208 return true;
1212 * Query completed (or errored out) -- clean up local state, return to previous
1213 * one.
1215 * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
1216 * returned false previously.
1218 * Note: this might be called in the PG_CATCH block of a failing transaction,
1219 * so be wary of running anything unnecessary. (In particular, it's probably
1220 * unwise to try to allocate memory.)
1222 void
1223 EventTriggerEndCompleteQuery(void)
1225 EventTriggerQueryState *prevstate;
1227 prevstate = currentEventTriggerState->previous;
1229 /* this avoids the need for retail pfree of SQLDropList items: */
1230 MemoryContextDelete(currentEventTriggerState->cxt);
1232 currentEventTriggerState = prevstate;
1236 * Do we need to keep close track of objects being dropped?
1238 * This is useful because there is a cost to running with them enabled.
1240 bool
1241 trackDroppedObjectsNeeded(void)
1244 * true if any sql_drop, table_rewrite, ddl_command_end event trigger
1245 * exists
1247 return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
1248 (EventCacheLookup(EVT_TableRewrite) != NIL) ||
1249 (EventCacheLookup(EVT_DDLCommandEnd) != NIL);
1253 * Support for dropped objects information on event trigger functions.
1255 * We keep the list of objects dropped by the current command in current
1256 * state's SQLDropList (comprising SQLDropObject items). Each time a new
1257 * command is to start, a clean EventTriggerQueryState is created; commands
1258 * that drop objects do the dependency.c dance to drop objects, which
1259 * populates the current state's SQLDropList; when the event triggers are
1260 * invoked they can consume the list via pg_event_trigger_dropped_objects().
1261 * When the command finishes, the EventTriggerQueryState is cleared, and
1262 * the one from the previous command is restored (when no command is in
1263 * execution, the current state is NULL).
1265 * All this lets us support the case that an event trigger function drops
1266 * objects "reentrantly".
1270 * Register one object as being dropped by the current command.
1272 void
1273 EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
1275 SQLDropObject *obj;
1276 MemoryContext oldcxt;
1278 if (!currentEventTriggerState)
1279 return;
1281 Assert(EventTriggerSupportsObject(object));
1283 /* don't report temp schemas except my own */
1284 if (object->classId == NamespaceRelationId &&
1285 (isAnyTempNamespace(object->objectId) &&
1286 !isTempNamespace(object->objectId)))
1287 return;
1289 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1291 obj = palloc0(sizeof(SQLDropObject));
1292 obj->address = *object;
1293 obj->original = original;
1294 obj->normal = normal;
1297 * Obtain schema names from the object's catalog tuple, if one exists;
1298 * this lets us skip objects in temp schemas. We trust that
1299 * ObjectProperty contains all object classes that can be
1300 * schema-qualified.
1302 if (is_objectclass_supported(object->classId))
1304 Relation catalog;
1305 HeapTuple tuple;
1307 catalog = table_open(obj->address.classId, AccessShareLock);
1308 tuple = get_catalog_object_by_oid(catalog,
1309 get_object_attnum_oid(object->classId),
1310 obj->address.objectId);
1312 if (tuple)
1314 AttrNumber attnum;
1315 Datum datum;
1316 bool isnull;
1318 attnum = get_object_attnum_namespace(obj->address.classId);
1319 if (attnum != InvalidAttrNumber)
1321 datum = heap_getattr(tuple, attnum,
1322 RelationGetDescr(catalog), &isnull);
1323 if (!isnull)
1325 Oid namespaceId;
1327 namespaceId = DatumGetObjectId(datum);
1328 /* temp objects are only reported if they are my own */
1329 if (isTempNamespace(namespaceId))
1331 obj->schemaname = "pg_temp";
1332 obj->istemp = true;
1334 else if (isAnyTempNamespace(namespaceId))
1336 pfree(obj);
1337 table_close(catalog, AccessShareLock);
1338 MemoryContextSwitchTo(oldcxt);
1339 return;
1341 else
1343 obj->schemaname = get_namespace_name(namespaceId);
1344 obj->istemp = false;
1349 if (get_object_namensp_unique(obj->address.classId) &&
1350 obj->address.objectSubId == 0)
1352 attnum = get_object_attnum_name(obj->address.classId);
1353 if (attnum != InvalidAttrNumber)
1355 datum = heap_getattr(tuple, attnum,
1356 RelationGetDescr(catalog), &isnull);
1357 if (!isnull)
1358 obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
1363 table_close(catalog, AccessShareLock);
1365 else
1367 if (object->classId == NamespaceRelationId &&
1368 isTempNamespace(object->objectId))
1369 obj->istemp = true;
1372 /* object identity, objname and objargs */
1373 obj->objidentity =
1374 getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
1375 false);
1377 /* object type */
1378 obj->objecttype = getObjectTypeDescription(&obj->address, false);
1380 slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
1382 MemoryContextSwitchTo(oldcxt);
1386 * pg_event_trigger_dropped_objects
1388 * Make the list of dropped objects available to the user function run by the
1389 * Event Trigger.
1391 Datum
1392 pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
1394 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1395 slist_iter iter;
1398 * Protect this function from being called out of context
1400 if (!currentEventTriggerState ||
1401 !currentEventTriggerState->in_sql_drop)
1402 ereport(ERROR,
1403 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1404 errmsg("%s can only be called in a sql_drop event trigger function",
1405 "pg_event_trigger_dropped_objects()")));
1407 /* Build tuplestore to hold the result rows */
1408 InitMaterializedSRF(fcinfo, 0);
1410 slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
1412 SQLDropObject *obj;
1413 int i = 0;
1414 Datum values[12] = {0};
1415 bool nulls[12] = {0};
1417 obj = slist_container(SQLDropObject, next, iter.cur);
1419 /* classid */
1420 values[i++] = ObjectIdGetDatum(obj->address.classId);
1422 /* objid */
1423 values[i++] = ObjectIdGetDatum(obj->address.objectId);
1425 /* objsubid */
1426 values[i++] = Int32GetDatum(obj->address.objectSubId);
1428 /* original */
1429 values[i++] = BoolGetDatum(obj->original);
1431 /* normal */
1432 values[i++] = BoolGetDatum(obj->normal);
1434 /* is_temporary */
1435 values[i++] = BoolGetDatum(obj->istemp);
1437 /* object_type */
1438 values[i++] = CStringGetTextDatum(obj->objecttype);
1440 /* schema_name */
1441 if (obj->schemaname)
1442 values[i++] = CStringGetTextDatum(obj->schemaname);
1443 else
1444 nulls[i++] = true;
1446 /* object_name */
1447 if (obj->objname)
1448 values[i++] = CStringGetTextDatum(obj->objname);
1449 else
1450 nulls[i++] = true;
1452 /* object_identity */
1453 if (obj->objidentity)
1454 values[i++] = CStringGetTextDatum(obj->objidentity);
1455 else
1456 nulls[i++] = true;
1458 /* address_names and address_args */
1459 if (obj->addrnames)
1461 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
1463 if (obj->addrargs)
1464 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
1465 else
1466 values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
1468 else
1470 nulls[i++] = true;
1471 nulls[i++] = true;
1474 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1475 values, nulls);
1478 return (Datum) 0;
1482 * pg_event_trigger_table_rewrite_oid
1484 * Make the Oid of the table going to be rewritten available to the user
1485 * function run by the Event Trigger.
1487 Datum
1488 pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
1491 * Protect this function from being called out of context
1493 if (!currentEventTriggerState ||
1494 currentEventTriggerState->table_rewrite_oid == InvalidOid)
1495 ereport(ERROR,
1496 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1497 errmsg("%s can only be called in a table_rewrite event trigger function",
1498 "pg_event_trigger_table_rewrite_oid()")));
1500 PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
1504 * pg_event_trigger_table_rewrite_reason
1506 * Make the rewrite reason available to the user.
1508 Datum
1509 pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
1512 * Protect this function from being called out of context
1514 if (!currentEventTriggerState ||
1515 currentEventTriggerState->table_rewrite_reason == 0)
1516 ereport(ERROR,
1517 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1518 errmsg("%s can only be called in a table_rewrite event trigger function",
1519 "pg_event_trigger_table_rewrite_reason()")));
1521 PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
1524 /*-------------------------------------------------------------------------
1525 * Support for DDL command deparsing
1527 * The routines below enable an event trigger function to obtain a list of
1528 * DDL commands as they are executed. There are three main pieces to this
1529 * feature:
1531 * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
1532 * adds a struct CollectedCommand representation of itself to the command list,
1533 * using the routines below.
1535 * 2) Some time after that, ddl_command_end fires and the command list is made
1536 * available to the event trigger function via pg_event_trigger_ddl_commands();
1537 * the complete command details are exposed as a column of type pg_ddl_command.
1539 * 3) An extension can install a function capable of taking a value of type
1540 * pg_ddl_command and transform it into some external, user-visible and/or
1541 * -modifiable representation.
1542 *-------------------------------------------------------------------------
1546 * Inhibit DDL command collection.
1548 void
1549 EventTriggerInhibitCommandCollection(void)
1551 if (!currentEventTriggerState)
1552 return;
1554 currentEventTriggerState->commandCollectionInhibited = true;
1558 * Re-establish DDL command collection.
1560 void
1561 EventTriggerUndoInhibitCommandCollection(void)
1563 if (!currentEventTriggerState)
1564 return;
1566 currentEventTriggerState->commandCollectionInhibited = false;
1570 * EventTriggerCollectSimpleCommand
1571 * Save data about a simple DDL command that was just executed
1573 * address identifies the object being operated on. secondaryObject is an
1574 * object address that was related in some way to the executed command; its
1575 * meaning is command-specific.
1577 * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
1578 * object being moved, objectId is its OID, and secondaryOid is the OID of the
1579 * old schema. (The destination schema OID can be obtained by catalog lookup
1580 * of the object.)
1582 void
1583 EventTriggerCollectSimpleCommand(ObjectAddress address,
1584 ObjectAddress secondaryObject,
1585 Node *parsetree)
1587 MemoryContext oldcxt;
1588 CollectedCommand *command;
1590 /* ignore if event trigger context not set, or collection disabled */
1591 if (!currentEventTriggerState ||
1592 currentEventTriggerState->commandCollectionInhibited)
1593 return;
1595 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1597 command = palloc(sizeof(CollectedCommand));
1599 command->type = SCT_Simple;
1600 command->in_extension = creating_extension;
1602 command->d.simple.address = address;
1603 command->d.simple.secondaryObject = secondaryObject;
1604 command->parsetree = copyObject(parsetree);
1606 currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
1607 command);
1609 MemoryContextSwitchTo(oldcxt);
1613 * EventTriggerAlterTableStart
1614 * Prepare to receive data on an ALTER TABLE command about to be executed
1616 * Note we don't collect the command immediately; instead we keep it in
1617 * currentCommand, and only when we're done processing the subcommands we will
1618 * add it to the command list.
1620 void
1621 EventTriggerAlterTableStart(Node *parsetree)
1623 MemoryContext oldcxt;
1624 CollectedCommand *command;
1626 /* ignore if event trigger context not set, or collection disabled */
1627 if (!currentEventTriggerState ||
1628 currentEventTriggerState->commandCollectionInhibited)
1629 return;
1631 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1633 command = palloc(sizeof(CollectedCommand));
1635 command->type = SCT_AlterTable;
1636 command->in_extension = creating_extension;
1638 command->d.alterTable.classId = RelationRelationId;
1639 command->d.alterTable.objectId = InvalidOid;
1640 command->d.alterTable.subcmds = NIL;
1641 command->parsetree = copyObject(parsetree);
1643 command->parent = currentEventTriggerState->currentCommand;
1644 currentEventTriggerState->currentCommand = command;
1646 MemoryContextSwitchTo(oldcxt);
1650 * Remember the OID of the object being affected by an ALTER TABLE.
1652 * This is needed because in some cases we don't know the OID until later.
1654 void
1655 EventTriggerAlterTableRelid(Oid objectId)
1657 if (!currentEventTriggerState ||
1658 currentEventTriggerState->commandCollectionInhibited)
1659 return;
1661 currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
1665 * EventTriggerCollectAlterTableSubcmd
1666 * Save data about a single part of an ALTER TABLE.
1668 * Several different commands go through this path, but apart from ALTER TABLE
1669 * itself, they are all concerned with AlterTableCmd nodes that are generated
1670 * internally, so that's all that this code needs to handle at the moment.
1672 void
1673 EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
1675 MemoryContext oldcxt;
1676 CollectedATSubcmd *newsub;
1678 /* ignore if event trigger context not set, or collection disabled */
1679 if (!currentEventTriggerState ||
1680 currentEventTriggerState->commandCollectionInhibited)
1681 return;
1683 Assert(IsA(subcmd, AlterTableCmd));
1684 Assert(currentEventTriggerState->currentCommand != NULL);
1685 Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
1687 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1689 newsub = palloc(sizeof(CollectedATSubcmd));
1690 newsub->address = address;
1691 newsub->parsetree = copyObject(subcmd);
1693 currentEventTriggerState->currentCommand->d.alterTable.subcmds =
1694 lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
1696 MemoryContextSwitchTo(oldcxt);
1700 * EventTriggerAlterTableEnd
1701 * Finish up saving an ALTER TABLE command, and add it to command list.
1703 * FIXME this API isn't considering the possibility that an xact/subxact is
1704 * aborted partway through. Probably it's best to add an
1705 * AtEOSubXact_EventTriggers() to fix this.
1707 void
1708 EventTriggerAlterTableEnd(void)
1710 CollectedCommand *parent;
1712 /* ignore if event trigger context not set, or collection disabled */
1713 if (!currentEventTriggerState ||
1714 currentEventTriggerState->commandCollectionInhibited)
1715 return;
1717 parent = currentEventTriggerState->currentCommand->parent;
1719 /* If no subcommands, don't collect */
1720 if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
1722 MemoryContext oldcxt;
1724 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1726 currentEventTriggerState->commandList =
1727 lappend(currentEventTriggerState->commandList,
1728 currentEventTriggerState->currentCommand);
1730 MemoryContextSwitchTo(oldcxt);
1732 else
1733 pfree(currentEventTriggerState->currentCommand);
1735 currentEventTriggerState->currentCommand = parent;
1739 * EventTriggerCollectGrant
1740 * Save data about a GRANT/REVOKE command being executed
1742 * This function creates a copy of the InternalGrant, as the original might
1743 * not have the right lifetime.
1745 void
1746 EventTriggerCollectGrant(InternalGrant *istmt)
1748 MemoryContext oldcxt;
1749 CollectedCommand *command;
1750 InternalGrant *icopy;
1751 ListCell *cell;
1753 /* ignore if event trigger context not set, or collection disabled */
1754 if (!currentEventTriggerState ||
1755 currentEventTriggerState->commandCollectionInhibited)
1756 return;
1758 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1761 * This is tedious, but necessary.
1763 icopy = palloc(sizeof(InternalGrant));
1764 memcpy(icopy, istmt, sizeof(InternalGrant));
1765 icopy->objects = list_copy(istmt->objects);
1766 icopy->grantees = list_copy(istmt->grantees);
1767 icopy->col_privs = NIL;
1768 foreach(cell, istmt->col_privs)
1769 icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
1771 /* Now collect it, using the copied InternalGrant */
1772 command = palloc(sizeof(CollectedCommand));
1773 command->type = SCT_Grant;
1774 command->in_extension = creating_extension;
1775 command->d.grant.istmt = icopy;
1776 command->parsetree = NULL;
1778 currentEventTriggerState->commandList =
1779 lappend(currentEventTriggerState->commandList, command);
1781 MemoryContextSwitchTo(oldcxt);
1785 * EventTriggerCollectAlterOpFam
1786 * Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
1787 * executed
1789 void
1790 EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
1791 List *operators, List *procedures)
1793 MemoryContext oldcxt;
1794 CollectedCommand *command;
1796 /* ignore if event trigger context not set, or collection disabled */
1797 if (!currentEventTriggerState ||
1798 currentEventTriggerState->commandCollectionInhibited)
1799 return;
1801 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1803 command = palloc(sizeof(CollectedCommand));
1804 command->type = SCT_AlterOpFamily;
1805 command->in_extension = creating_extension;
1806 ObjectAddressSet(command->d.opfam.address,
1807 OperatorFamilyRelationId, opfamoid);
1808 command->d.opfam.operators = operators;
1809 command->d.opfam.procedures = procedures;
1810 command->parsetree = (Node *) copyObject(stmt);
1812 currentEventTriggerState->commandList =
1813 lappend(currentEventTriggerState->commandList, command);
1815 MemoryContextSwitchTo(oldcxt);
1819 * EventTriggerCollectCreateOpClass
1820 * Save data about a CREATE OPERATOR CLASS command being executed
1822 void
1823 EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
1824 List *operators, List *procedures)
1826 MemoryContext oldcxt;
1827 CollectedCommand *command;
1829 /* ignore if event trigger context not set, or collection disabled */
1830 if (!currentEventTriggerState ||
1831 currentEventTriggerState->commandCollectionInhibited)
1832 return;
1834 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1836 command = palloc0(sizeof(CollectedCommand));
1837 command->type = SCT_CreateOpClass;
1838 command->in_extension = creating_extension;
1839 ObjectAddressSet(command->d.createopc.address,
1840 OperatorClassRelationId, opcoid);
1841 command->d.createopc.operators = operators;
1842 command->d.createopc.procedures = procedures;
1843 command->parsetree = (Node *) copyObject(stmt);
1845 currentEventTriggerState->commandList =
1846 lappend(currentEventTriggerState->commandList, command);
1848 MemoryContextSwitchTo(oldcxt);
1852 * EventTriggerCollectAlterTSConfig
1853 * Save data about an ALTER TEXT SEARCH CONFIGURATION command being
1854 * executed
1856 void
1857 EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
1858 Oid *dictIds, int ndicts)
1860 MemoryContext oldcxt;
1861 CollectedCommand *command;
1863 /* ignore if event trigger context not set, or collection disabled */
1864 if (!currentEventTriggerState ||
1865 currentEventTriggerState->commandCollectionInhibited)
1866 return;
1868 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1870 command = palloc0(sizeof(CollectedCommand));
1871 command->type = SCT_AlterTSConfig;
1872 command->in_extension = creating_extension;
1873 ObjectAddressSet(command->d.atscfg.address,
1874 TSConfigRelationId, cfgId);
1875 command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
1876 memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
1877 command->d.atscfg.ndicts = ndicts;
1878 command->parsetree = (Node *) copyObject(stmt);
1880 currentEventTriggerState->commandList =
1881 lappend(currentEventTriggerState->commandList, command);
1883 MemoryContextSwitchTo(oldcxt);
1887 * EventTriggerCollectAlterDefPrivs
1888 * Save data about an ALTER DEFAULT PRIVILEGES command being
1889 * executed
1891 void
1892 EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
1894 MemoryContext oldcxt;
1895 CollectedCommand *command;
1897 /* ignore if event trigger context not set, or collection disabled */
1898 if (!currentEventTriggerState ||
1899 currentEventTriggerState->commandCollectionInhibited)
1900 return;
1902 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1904 command = palloc0(sizeof(CollectedCommand));
1905 command->type = SCT_AlterDefaultPrivileges;
1906 command->d.defprivs.objtype = stmt->action->objtype;
1907 command->in_extension = creating_extension;
1908 command->parsetree = (Node *) copyObject(stmt);
1910 currentEventTriggerState->commandList =
1911 lappend(currentEventTriggerState->commandList, command);
1912 MemoryContextSwitchTo(oldcxt);
1916 * In a ddl_command_end event trigger, this function reports the DDL commands
1917 * being run.
1919 Datum
1920 pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
1922 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1923 ListCell *lc;
1926 * Protect this function from being called out of context
1928 if (!currentEventTriggerState)
1929 ereport(ERROR,
1930 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1931 errmsg("%s can only be called in an event trigger function",
1932 "pg_event_trigger_ddl_commands()")));
1934 /* Build tuplestore to hold the result rows */
1935 InitMaterializedSRF(fcinfo, 0);
1937 foreach(lc, currentEventTriggerState->commandList)
1939 CollectedCommand *cmd = lfirst(lc);
1940 Datum values[9];
1941 bool nulls[9] = {0};
1942 ObjectAddress addr;
1943 int i = 0;
1946 * For IF NOT EXISTS commands that attempt to create an existing
1947 * object, the returned OID is Invalid. Don't return anything.
1949 * One might think that a viable alternative would be to look up the
1950 * Oid of the existing object and run the deparse with that. But
1951 * since the parse tree might be different from the one that created
1952 * the object in the first place, we might not end up in a consistent
1953 * state anyway.
1955 if (cmd->type == SCT_Simple &&
1956 !OidIsValid(cmd->d.simple.address.objectId))
1957 continue;
1959 switch (cmd->type)
1961 case SCT_Simple:
1962 case SCT_AlterTable:
1963 case SCT_AlterOpFamily:
1964 case SCT_CreateOpClass:
1965 case SCT_AlterTSConfig:
1967 char *identity;
1968 char *type;
1969 char *schema = NULL;
1971 if (cmd->type == SCT_Simple)
1972 addr = cmd->d.simple.address;
1973 else if (cmd->type == SCT_AlterTable)
1974 ObjectAddressSet(addr,
1975 cmd->d.alterTable.classId,
1976 cmd->d.alterTable.objectId);
1977 else if (cmd->type == SCT_AlterOpFamily)
1978 addr = cmd->d.opfam.address;
1979 else if (cmd->type == SCT_CreateOpClass)
1980 addr = cmd->d.createopc.address;
1981 else if (cmd->type == SCT_AlterTSConfig)
1982 addr = cmd->d.atscfg.address;
1985 * If an object was dropped in the same command we may end
1986 * up in a situation where we generated a message but can
1987 * no longer look for the object information, so skip it
1988 * rather than failing. This can happen for example with
1989 * some subcommand combinations of ALTER TABLE.
1991 identity = getObjectIdentity(&addr, true);
1992 if (identity == NULL)
1993 continue;
1995 /* The type can never be NULL. */
1996 type = getObjectTypeDescription(&addr, true);
1999 * Obtain schema name, if any ("pg_temp" if a temp
2000 * object). If the object class is not in the supported
2001 * list here, we assume it's a schema-less object type,
2002 * and thus "schema" remains set to NULL.
2004 if (is_objectclass_supported(addr.classId))
2006 AttrNumber nspAttnum;
2008 nspAttnum = get_object_attnum_namespace(addr.classId);
2009 if (nspAttnum != InvalidAttrNumber)
2011 Relation catalog;
2012 HeapTuple objtup;
2013 Oid schema_oid;
2014 bool isnull;
2016 catalog = table_open(addr.classId, AccessShareLock);
2017 objtup = get_catalog_object_by_oid(catalog,
2018 get_object_attnum_oid(addr.classId),
2019 addr.objectId);
2020 if (!HeapTupleIsValid(objtup))
2021 elog(ERROR, "cache lookup failed for object %u/%u",
2022 addr.classId, addr.objectId);
2023 schema_oid =
2024 heap_getattr(objtup, nspAttnum,
2025 RelationGetDescr(catalog), &isnull);
2026 if (isnull)
2027 elog(ERROR,
2028 "invalid null namespace in object %u/%u/%d",
2029 addr.classId, addr.objectId, addr.objectSubId);
2030 schema = get_namespace_name_or_temp(schema_oid);
2032 table_close(catalog, AccessShareLock);
2036 /* classid */
2037 values[i++] = ObjectIdGetDatum(addr.classId);
2038 /* objid */
2039 values[i++] = ObjectIdGetDatum(addr.objectId);
2040 /* objsubid */
2041 values[i++] = Int32GetDatum(addr.objectSubId);
2042 /* command tag */
2043 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2044 /* object_type */
2045 values[i++] = CStringGetTextDatum(type);
2046 /* schema */
2047 if (schema == NULL)
2048 nulls[i++] = true;
2049 else
2050 values[i++] = CStringGetTextDatum(schema);
2051 /* identity */
2052 values[i++] = CStringGetTextDatum(identity);
2053 /* in_extension */
2054 values[i++] = BoolGetDatum(cmd->in_extension);
2055 /* command */
2056 values[i++] = PointerGetDatum(cmd);
2058 break;
2060 case SCT_AlterDefaultPrivileges:
2061 /* classid */
2062 nulls[i++] = true;
2063 /* objid */
2064 nulls[i++] = true;
2065 /* objsubid */
2066 nulls[i++] = true;
2067 /* command tag */
2068 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2069 /* object_type */
2070 values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
2071 /* schema */
2072 nulls[i++] = true;
2073 /* identity */
2074 nulls[i++] = true;
2075 /* in_extension */
2076 values[i++] = BoolGetDatum(cmd->in_extension);
2077 /* command */
2078 values[i++] = PointerGetDatum(cmd);
2079 break;
2081 case SCT_Grant:
2082 /* classid */
2083 nulls[i++] = true;
2084 /* objid */
2085 nulls[i++] = true;
2086 /* objsubid */
2087 nulls[i++] = true;
2088 /* command tag */
2089 values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
2090 "GRANT" : "REVOKE");
2091 /* object_type */
2092 values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
2093 /* schema */
2094 nulls[i++] = true;
2095 /* identity */
2096 nulls[i++] = true;
2097 /* in_extension */
2098 values[i++] = BoolGetDatum(cmd->in_extension);
2099 /* command */
2100 values[i++] = PointerGetDatum(cmd);
2101 break;
2104 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2105 values, nulls);
2108 PG_RETURN_VOID();
2112 * Return the ObjectType as a string, as it would appear in GRANT and
2113 * REVOKE commands.
2115 static const char *
2116 stringify_grant_objtype(ObjectType objtype)
2118 switch (objtype)
2120 case OBJECT_COLUMN:
2121 return "COLUMN";
2122 case OBJECT_TABLE:
2123 return "TABLE";
2124 case OBJECT_SEQUENCE:
2125 return "SEQUENCE";
2126 case OBJECT_DATABASE:
2127 return "DATABASE";
2128 case OBJECT_DOMAIN:
2129 return "DOMAIN";
2130 case OBJECT_FDW:
2131 return "FOREIGN DATA WRAPPER";
2132 case OBJECT_FOREIGN_SERVER:
2133 return "FOREIGN SERVER";
2134 case OBJECT_FUNCTION:
2135 return "FUNCTION";
2136 case OBJECT_LANGUAGE:
2137 return "LANGUAGE";
2138 case OBJECT_LARGEOBJECT:
2139 return "LARGE OBJECT";
2140 case OBJECT_SCHEMA:
2141 return "SCHEMA";
2142 case OBJECT_PARAMETER_ACL:
2143 return "PARAMETER";
2144 case OBJECT_PROCEDURE:
2145 return "PROCEDURE";
2146 case OBJECT_ROUTINE:
2147 return "ROUTINE";
2148 case OBJECT_TABLESPACE:
2149 return "TABLESPACE";
2150 case OBJECT_TYPE:
2151 return "TYPE";
2152 /* these currently aren't used */
2153 case OBJECT_ACCESS_METHOD:
2154 case OBJECT_AGGREGATE:
2155 case OBJECT_AMOP:
2156 case OBJECT_AMPROC:
2157 case OBJECT_ATTRIBUTE:
2158 case OBJECT_CAST:
2159 case OBJECT_COLLATION:
2160 case OBJECT_CONVERSION:
2161 case OBJECT_DEFAULT:
2162 case OBJECT_DEFACL:
2163 case OBJECT_DOMCONSTRAINT:
2164 case OBJECT_EVENT_TRIGGER:
2165 case OBJECT_EXTENSION:
2166 case OBJECT_FOREIGN_TABLE:
2167 case OBJECT_INDEX:
2168 case OBJECT_MATVIEW:
2169 case OBJECT_OPCLASS:
2170 case OBJECT_OPERATOR:
2171 case OBJECT_OPFAMILY:
2172 case OBJECT_POLICY:
2173 case OBJECT_PUBLICATION:
2174 case OBJECT_PUBLICATION_NAMESPACE:
2175 case OBJECT_PUBLICATION_REL:
2176 case OBJECT_ROLE:
2177 case OBJECT_RULE:
2178 case OBJECT_STATISTIC_EXT:
2179 case OBJECT_SUBSCRIPTION:
2180 case OBJECT_TABCONSTRAINT:
2181 case OBJECT_TRANSFORM:
2182 case OBJECT_TRIGGER:
2183 case OBJECT_TSCONFIGURATION:
2184 case OBJECT_TSDICTIONARY:
2185 case OBJECT_TSPARSER:
2186 case OBJECT_TSTEMPLATE:
2187 case OBJECT_USER_MAPPING:
2188 case OBJECT_VIEW:
2189 elog(ERROR, "unsupported object type: %d", (int) objtype);
2192 return "???"; /* keep compiler quiet */
2196 * Return the ObjectType as a string; as above, but use the spelling
2197 * in ALTER DEFAULT PRIVILEGES commands instead. Generally this is just
2198 * the plural.
2200 static const char *
2201 stringify_adefprivs_objtype(ObjectType objtype)
2203 switch (objtype)
2205 case OBJECT_COLUMN:
2206 return "COLUMNS";
2207 case OBJECT_TABLE:
2208 return "TABLES";
2209 case OBJECT_SEQUENCE:
2210 return "SEQUENCES";
2211 case OBJECT_DATABASE:
2212 return "DATABASES";
2213 case OBJECT_DOMAIN:
2214 return "DOMAINS";
2215 case OBJECT_FDW:
2216 return "FOREIGN DATA WRAPPERS";
2217 case OBJECT_FOREIGN_SERVER:
2218 return "FOREIGN SERVERS";
2219 case OBJECT_FUNCTION:
2220 return "FUNCTIONS";
2221 case OBJECT_LANGUAGE:
2222 return "LANGUAGES";
2223 case OBJECT_LARGEOBJECT:
2224 return "LARGE OBJECTS";
2225 case OBJECT_SCHEMA:
2226 return "SCHEMAS";
2227 case OBJECT_PROCEDURE:
2228 return "PROCEDURES";
2229 case OBJECT_ROUTINE:
2230 return "ROUTINES";
2231 case OBJECT_TABLESPACE:
2232 return "TABLESPACES";
2233 case OBJECT_TYPE:
2234 return "TYPES";
2235 /* these currently aren't used */
2236 case OBJECT_ACCESS_METHOD:
2237 case OBJECT_AGGREGATE:
2238 case OBJECT_AMOP:
2239 case OBJECT_AMPROC:
2240 case OBJECT_ATTRIBUTE:
2241 case OBJECT_CAST:
2242 case OBJECT_COLLATION:
2243 case OBJECT_CONVERSION:
2244 case OBJECT_DEFAULT:
2245 case OBJECT_DEFACL:
2246 case OBJECT_DOMCONSTRAINT:
2247 case OBJECT_EVENT_TRIGGER:
2248 case OBJECT_EXTENSION:
2249 case OBJECT_FOREIGN_TABLE:
2250 case OBJECT_INDEX:
2251 case OBJECT_MATVIEW:
2252 case OBJECT_OPCLASS:
2253 case OBJECT_OPERATOR:
2254 case OBJECT_OPFAMILY:
2255 case OBJECT_PARAMETER_ACL:
2256 case OBJECT_POLICY:
2257 case OBJECT_PUBLICATION:
2258 case OBJECT_PUBLICATION_NAMESPACE:
2259 case OBJECT_PUBLICATION_REL:
2260 case OBJECT_ROLE:
2261 case OBJECT_RULE:
2262 case OBJECT_STATISTIC_EXT:
2263 case OBJECT_SUBSCRIPTION:
2264 case OBJECT_TABCONSTRAINT:
2265 case OBJECT_TRANSFORM:
2266 case OBJECT_TRIGGER:
2267 case OBJECT_TSCONFIGURATION:
2268 case OBJECT_TSDICTIONARY:
2269 case OBJECT_TSPARSER:
2270 case OBJECT_TSTEMPLATE:
2271 case OBJECT_USER_MAPPING:
2272 case OBJECT_VIEW:
2273 elog(ERROR, "unsupported object type: %d", (int) objtype);
2276 return "???"; /* keep compiler quiet */