Login event trigger documentation wordsmithing
[pgsql.git] / src / backend / commands / event_trigger.c
blobc95e9cf6f0efaee220932a192b0aa9880e2ac2a8
1 /*-------------------------------------------------------------------------
3 * event_trigger.c
4 * PostgreSQL EVENT TRIGGER support code.
6 * Portions Copyright (c) 1996-2024, 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_database.h"
25 #include "catalog/pg_event_trigger.h"
26 #include "catalog/pg_namespace.h"
27 #include "catalog/pg_opclass.h"
28 #include "catalog/pg_opfamily.h"
29 #include "catalog/pg_proc.h"
30 #include "catalog/pg_trigger.h"
31 #include "catalog/pg_ts_config.h"
32 #include "catalog/pg_type.h"
33 #include "commands/event_trigger.h"
34 #include "commands/extension.h"
35 #include "commands/trigger.h"
36 #include "funcapi.h"
37 #include "lib/ilist.h"
38 #include "miscadmin.h"
39 #include "parser/parse_func.h"
40 #include "pgstat.h"
41 #include "storage/lmgr.h"
42 #include "tcop/deparse_utility.h"
43 #include "tcop/utility.h"
44 #include "utils/acl.h"
45 #include "utils/builtins.h"
46 #include "utils/evtcache.h"
47 #include "utils/fmgroids.h"
48 #include "utils/fmgrprotos.h"
49 #include "utils/lsyscache.h"
50 #include "utils/memutils.h"
51 #include "utils/rel.h"
52 #include "utils/snapmgr.h"
53 #include "utils/syscache.h"
55 typedef struct EventTriggerQueryState
57 /* memory context for this state's objects */
58 MemoryContext cxt;
60 /* sql_drop */
61 slist_head SQLDropList;
62 bool in_sql_drop;
64 /* table_rewrite */
65 Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite
66 * event */
67 int table_rewrite_reason; /* AT_REWRITE reason */
69 /* Support for command collection */
70 bool commandCollectionInhibited;
71 CollectedCommand *currentCommand;
72 List *commandList; /* list of CollectedCommand; see
73 * deparse_utility.h */
74 struct EventTriggerQueryState *previous;
75 } EventTriggerQueryState;
77 static EventTriggerQueryState *currentEventTriggerState = NULL;
79 /* GUC parameter */
80 bool event_triggers = true;
82 /* Support for dropped objects */
83 typedef struct SQLDropObject
85 ObjectAddress address;
86 const char *schemaname;
87 const char *objname;
88 const char *objidentity;
89 const char *objecttype;
90 List *addrnames;
91 List *addrargs;
92 bool original;
93 bool normal;
94 bool istemp;
95 slist_node next;
96 } SQLDropObject;
98 static void AlterEventTriggerOwner_internal(Relation rel,
99 HeapTuple tup,
100 Oid newOwnerId);
101 static void error_duplicate_filter_variable(const char *defname);
102 static Datum filter_list_to_array(List *filterlist);
103 static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
104 Oid evtOwner, Oid funcoid, List *taglist);
105 static void validate_ddl_tags(const char *filtervar, List *taglist);
106 static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
107 static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
108 static const char *stringify_grant_objtype(ObjectType objtype);
109 static const char *stringify_adefprivs_objtype(ObjectType objtype);
110 static void SetDatatabaseHasLoginEventTriggers(void);
113 * Create an event trigger.
116 CreateEventTrigger(CreateEventTrigStmt *stmt)
118 HeapTuple tuple;
119 Oid funcoid;
120 Oid funcrettype;
121 Oid evtowner = GetUserId();
122 ListCell *lc;
123 List *tags = NULL;
126 * It would be nice to allow database owners or even regular users to do
127 * this, but there are obvious privilege escalation risks which would have
128 * to somehow be plugged first.
130 if (!superuser())
131 ereport(ERROR,
132 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
133 errmsg("permission denied to create event trigger \"%s\"",
134 stmt->trigname),
135 errhint("Must be superuser to create an event trigger.")));
137 /* Validate event name. */
138 if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
139 strcmp(stmt->eventname, "ddl_command_end") != 0 &&
140 strcmp(stmt->eventname, "sql_drop") != 0 &&
141 strcmp(stmt->eventname, "login") != 0 &&
142 strcmp(stmt->eventname, "table_rewrite") != 0)
143 ereport(ERROR,
144 (errcode(ERRCODE_SYNTAX_ERROR),
145 errmsg("unrecognized event name \"%s\"",
146 stmt->eventname)));
148 /* Validate filter conditions. */
149 foreach(lc, stmt->whenclause)
151 DefElem *def = (DefElem *) lfirst(lc);
153 if (strcmp(def->defname, "tag") == 0)
155 if (tags != NULL)
156 error_duplicate_filter_variable(def->defname);
157 tags = (List *) def->arg;
159 else
160 ereport(ERROR,
161 (errcode(ERRCODE_SYNTAX_ERROR),
162 errmsg("unrecognized filter variable \"%s\"", def->defname)));
165 /* Validate tag list, if any. */
166 if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
167 strcmp(stmt->eventname, "ddl_command_end") == 0 ||
168 strcmp(stmt->eventname, "sql_drop") == 0)
169 && tags != NULL)
170 validate_ddl_tags("tag", tags);
171 else if (strcmp(stmt->eventname, "table_rewrite") == 0
172 && tags != NULL)
173 validate_table_rewrite_tags("tag", tags);
174 else if (strcmp(stmt->eventname, "login") == 0 && tags != NULL)
175 ereport(ERROR,
176 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
177 errmsg("tag filtering is not supported for login event triggers")));
180 * Give user a nice error message if an event trigger of the same name
181 * already exists.
183 tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
184 if (HeapTupleIsValid(tuple))
185 ereport(ERROR,
186 (errcode(ERRCODE_DUPLICATE_OBJECT),
187 errmsg("event trigger \"%s\" already exists",
188 stmt->trigname)));
190 /* Find and validate the trigger function. */
191 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
192 funcrettype = get_func_rettype(funcoid);
193 if (funcrettype != EVENT_TRIGGEROID)
194 ereport(ERROR,
195 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
196 errmsg("function %s must return type %s",
197 NameListToString(stmt->funcname), "event_trigger")));
199 /* Insert catalog entries. */
200 return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
201 evtowner, funcoid, tags);
205 * Validate DDL command tags.
207 static void
208 validate_ddl_tags(const char *filtervar, List *taglist)
210 ListCell *lc;
212 foreach(lc, taglist)
214 const char *tagstr = strVal(lfirst(lc));
215 CommandTag commandTag = GetCommandTagEnum(tagstr);
217 if (commandTag == CMDTAG_UNKNOWN)
218 ereport(ERROR,
219 (errcode(ERRCODE_SYNTAX_ERROR),
220 errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
221 tagstr, filtervar)));
222 if (!command_tag_event_trigger_ok(commandTag))
223 ereport(ERROR,
224 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
225 /* translator: %s represents an SQL statement name */
226 errmsg("event triggers are not supported for %s",
227 tagstr)));
232 * Validate DDL command tags for event table_rewrite.
234 static void
235 validate_table_rewrite_tags(const char *filtervar, List *taglist)
237 ListCell *lc;
239 foreach(lc, taglist)
241 const char *tagstr = strVal(lfirst(lc));
242 CommandTag commandTag = GetCommandTagEnum(tagstr);
244 if (!command_tag_table_rewrite_ok(commandTag))
245 ereport(ERROR,
246 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
247 /* translator: %s represents an SQL statement name */
248 errmsg("event triggers are not supported for %s",
249 tagstr)));
254 * Complain about a duplicate filter variable.
256 static void
257 error_duplicate_filter_variable(const char *defname)
259 ereport(ERROR,
260 (errcode(ERRCODE_SYNTAX_ERROR),
261 errmsg("filter variable \"%s\" specified more than once",
262 defname)));
266 * Insert the new pg_event_trigger row and record dependencies.
268 static Oid
269 insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtOwner,
270 Oid funcoid, List *taglist)
272 Relation tgrel;
273 Oid trigoid;
274 HeapTuple tuple;
275 Datum values[Natts_pg_trigger];
276 bool nulls[Natts_pg_trigger];
277 NameData evtnamedata,
278 evteventdata;
279 ObjectAddress myself,
280 referenced;
282 /* Open pg_event_trigger. */
283 tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
285 /* Build the new pg_trigger tuple. */
286 trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
287 Anum_pg_event_trigger_oid);
288 values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
289 memset(nulls, false, sizeof(nulls));
290 namestrcpy(&evtnamedata, trigname);
291 values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
292 namestrcpy(&evteventdata, eventname);
293 values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
294 values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
295 values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
296 values[Anum_pg_event_trigger_evtenabled - 1] =
297 CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
298 if (taglist == NIL)
299 nulls[Anum_pg_event_trigger_evttags - 1] = true;
300 else
301 values[Anum_pg_event_trigger_evttags - 1] =
302 filter_list_to_array(taglist);
304 /* Insert heap tuple. */
305 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
306 CatalogTupleInsert(tgrel, tuple);
307 heap_freetuple(tuple);
310 * Login event triggers have an additional flag in pg_database to enable
311 * faster lookups in hot codepaths. Set the flag unless already True.
313 if (strcmp(eventname, "login") == 0)
314 SetDatatabaseHasLoginEventTriggers();
316 /* Depend on owner. */
317 recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
319 /* Depend on event trigger function. */
320 myself.classId = EventTriggerRelationId;
321 myself.objectId = trigoid;
322 myself.objectSubId = 0;
323 referenced.classId = ProcedureRelationId;
324 referenced.objectId = funcoid;
325 referenced.objectSubId = 0;
326 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
328 /* Depend on extension, if any. */
329 recordDependencyOnCurrentExtension(&myself, false);
331 /* Post creation hook for new event trigger */
332 InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
334 /* Close pg_event_trigger. */
335 table_close(tgrel, RowExclusiveLock);
337 return trigoid;
341 * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented
342 * by a DefElem whose value is a List of String nodes; in the catalog, we
343 * store the list of strings as a text array. This function transforms the
344 * former representation into the latter one.
346 * For cleanliness, we store command tags in the catalog as text. It's
347 * possible (although not currently anticipated) that we might have
348 * a case-sensitive filter variable in the future, in which case this would
349 * need some further adjustment.
351 static Datum
352 filter_list_to_array(List *filterlist)
354 ListCell *lc;
355 Datum *data;
356 int i = 0,
357 l = list_length(filterlist);
359 data = (Datum *) palloc(l * sizeof(Datum));
361 foreach(lc, filterlist)
363 const char *value = strVal(lfirst(lc));
364 char *result,
367 result = pstrdup(value);
368 for (p = result; *p; p++)
369 *p = pg_ascii_toupper((unsigned char) *p);
370 data[i++] = PointerGetDatum(cstring_to_text(result));
371 pfree(result);
374 return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
378 * Set pg_database.dathasloginevt flag for current database indicating that
379 * current database has on login event triggers.
381 void
382 SetDatatabaseHasLoginEventTriggers(void)
384 /* Set dathasloginevt flag in pg_database */
385 Form_pg_database db;
386 Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
387 HeapTuple tuple;
390 * Use shared lock to prevent a conflict with EventTriggerOnLogin() trying
391 * to reset pg_database.dathasloginevt flag. Note, this lock doesn't
392 * effectively blocks database or other objection. It's just custom lock
393 * tag used to prevent multiple backends changing
394 * pg_database.dathasloginevt flag.
396 LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
398 tuple = SearchSysCacheCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
399 if (!HeapTupleIsValid(tuple))
400 elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
401 db = (Form_pg_database) GETSTRUCT(tuple);
402 if (!db->dathasloginevt)
404 db->dathasloginevt = true;
405 CatalogTupleUpdate(pg_db, &tuple->t_self, tuple);
406 CommandCounterIncrement();
408 table_close(pg_db, RowExclusiveLock);
409 heap_freetuple(tuple);
413 * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
416 AlterEventTrigger(AlterEventTrigStmt *stmt)
418 Relation tgrel;
419 HeapTuple tup;
420 Oid trigoid;
421 Form_pg_event_trigger evtForm;
422 char tgenabled = stmt->tgenabled;
424 tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
426 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
427 CStringGetDatum(stmt->trigname));
428 if (!HeapTupleIsValid(tup))
429 ereport(ERROR,
430 (errcode(ERRCODE_UNDEFINED_OBJECT),
431 errmsg("event trigger \"%s\" does not exist",
432 stmt->trigname)));
434 evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
435 trigoid = evtForm->oid;
437 if (!object_ownercheck(EventTriggerRelationId, trigoid, GetUserId()))
438 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
439 stmt->trigname);
441 /* tuple is a copy, so we can modify it below */
442 evtForm->evtenabled = tgenabled;
444 CatalogTupleUpdate(tgrel, &tup->t_self, tup);
447 * Login event triggers have an additional flag in pg_database to enable
448 * faster lookups in hot codepaths. Set the flag unless already True.
450 if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
451 tgenabled != TRIGGER_DISABLED)
452 SetDatatabaseHasLoginEventTriggers();
454 InvokeObjectPostAlterHook(EventTriggerRelationId,
455 trigoid, 0);
457 /* clean up */
458 heap_freetuple(tup);
459 table_close(tgrel, RowExclusiveLock);
461 return trigoid;
465 * Change event trigger's owner -- by name
467 ObjectAddress
468 AlterEventTriggerOwner(const char *name, Oid newOwnerId)
470 Oid evtOid;
471 HeapTuple tup;
472 Form_pg_event_trigger evtForm;
473 Relation rel;
474 ObjectAddress address;
476 rel = table_open(EventTriggerRelationId, RowExclusiveLock);
478 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
480 if (!HeapTupleIsValid(tup))
481 ereport(ERROR,
482 (errcode(ERRCODE_UNDEFINED_OBJECT),
483 errmsg("event trigger \"%s\" does not exist", name)));
485 evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
486 evtOid = evtForm->oid;
488 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
490 ObjectAddressSet(address, EventTriggerRelationId, evtOid);
492 heap_freetuple(tup);
494 table_close(rel, RowExclusiveLock);
496 return address;
500 * Change event trigger owner, by OID
502 void
503 AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId)
505 HeapTuple tup;
506 Relation rel;
508 rel = table_open(EventTriggerRelationId, RowExclusiveLock);
510 tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
512 if (!HeapTupleIsValid(tup))
513 ereport(ERROR,
514 (errcode(ERRCODE_UNDEFINED_OBJECT),
515 errmsg("event trigger with OID %u does not exist", trigOid)));
517 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
519 heap_freetuple(tup);
521 table_close(rel, RowExclusiveLock);
525 * Internal workhorse for changing an event trigger's owner
527 static void
528 AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
530 Form_pg_event_trigger form;
532 form = (Form_pg_event_trigger) GETSTRUCT(tup);
534 if (form->evtowner == newOwnerId)
535 return;
537 if (!object_ownercheck(EventTriggerRelationId, form->oid, GetUserId()))
538 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
539 NameStr(form->evtname));
541 /* New owner must be a superuser */
542 if (!superuser_arg(newOwnerId))
543 ereport(ERROR,
544 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
545 errmsg("permission denied to change owner of event trigger \"%s\"",
546 NameStr(form->evtname)),
547 errhint("The owner of an event trigger must be a superuser.")));
549 form->evtowner = newOwnerId;
550 CatalogTupleUpdate(rel, &tup->t_self, tup);
552 /* Update owner dependency reference */
553 changeDependencyOnOwner(EventTriggerRelationId,
554 form->oid,
555 newOwnerId);
557 InvokeObjectPostAlterHook(EventTriggerRelationId,
558 form->oid, 0);
562 * get_event_trigger_oid - Look up an event trigger by name to find its OID.
564 * If missing_ok is false, throw an error if trigger not found. If
565 * true, just return InvalidOid.
568 get_event_trigger_oid(const char *trigname, bool missing_ok)
570 Oid oid;
572 oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
573 CStringGetDatum(trigname));
574 if (!OidIsValid(oid) && !missing_ok)
575 ereport(ERROR,
576 (errcode(ERRCODE_UNDEFINED_OBJECT),
577 errmsg("event trigger \"%s\" does not exist", trigname)));
578 return oid;
582 * Return true when we want to fire given Event Trigger and false otherwise,
583 * filtering on the session replication role and the event trigger registered
584 * tags matching.
586 static bool
587 filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
590 * Filter by session replication role, knowing that we never see disabled
591 * items down here.
593 if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
595 if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
596 return false;
598 else
600 if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
601 return false;
604 /* Filter by tags, if any were specified. */
605 if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
606 return false;
608 /* if we reach that point, we're not filtering out this item */
609 return true;
612 static CommandTag
613 EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
615 if (event == EVT_Login)
616 return CMDTAG_LOGIN;
617 else
618 return CreateCommandTag(parsetree);
622 * Setup for running triggers for the given event. Return value is an OID list
623 * of functions to run; if there are any, trigdata is filled with an
624 * appropriate EventTriggerData for them to receive.
626 static List *
627 EventTriggerCommonSetup(Node *parsetree,
628 EventTriggerEvent event, const char *eventstr,
629 EventTriggerData *trigdata, bool unfiltered)
631 CommandTag tag;
632 List *cachelist;
633 ListCell *lc;
634 List *runlist = NIL;
637 * We want the list of command tags for which this procedure is actually
638 * invoked to match up exactly with the list that CREATE EVENT TRIGGER
639 * accepts. This debugging cross-check will throw an error if this
640 * function is invoked for a command tag that CREATE EVENT TRIGGER won't
641 * accept. (Unfortunately, there doesn't seem to be any simple, automated
642 * way to verify that CREATE EVENT TRIGGER doesn't accept extra stuff that
643 * never reaches this control point.)
645 * If this cross-check fails for you, you probably need to either adjust
646 * standard_ProcessUtility() not to invoke event triggers for the command
647 * type in question, or you need to adjust event_trigger_ok to accept the
648 * relevant command tag.
650 #ifdef USE_ASSERT_CHECKING
652 CommandTag dbgtag;
654 dbgtag = EventTriggerGetTag(parsetree, event);
656 if (event == EVT_DDLCommandStart ||
657 event == EVT_DDLCommandEnd ||
658 event == EVT_SQLDrop ||
659 event == EVT_Login)
661 if (!command_tag_event_trigger_ok(dbgtag))
662 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
664 else if (event == EVT_TableRewrite)
666 if (!command_tag_table_rewrite_ok(dbgtag))
667 elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
670 #endif
672 /* Use cache to find triggers for this event; fast exit if none. */
673 cachelist = EventCacheLookup(event);
674 if (cachelist == NIL)
675 return NIL;
677 /* Get the command tag. */
678 tag = EventTriggerGetTag(parsetree, event);
681 * Filter list of event triggers by command tag, and copy them into our
682 * memory context. Once we start running the command triggers, or indeed
683 * once we do anything at all that touches the catalogs, an invalidation
684 * might leave cachelist pointing at garbage, so we must do this before we
685 * can do much else.
687 foreach(lc, cachelist)
689 EventTriggerCacheItem *item = lfirst(lc);
691 if (unfiltered || filter_event_trigger(tag, item))
693 /* We must plan to fire this trigger. */
694 runlist = lappend_oid(runlist, item->fnoid);
698 /* Don't spend any more time on this if no functions to run */
699 if (runlist == NIL)
700 return NIL;
702 trigdata->type = T_EventTriggerData;
703 trigdata->event = eventstr;
704 trigdata->parsetree = parsetree;
705 trigdata->tag = tag;
707 return runlist;
711 * Fire ddl_command_start triggers.
713 void
714 EventTriggerDDLCommandStart(Node *parsetree)
716 List *runlist;
717 EventTriggerData trigdata;
720 * Event Triggers are completely disabled in standalone mode. There are
721 * (at least) two reasons for this:
723 * 1. A sufficiently broken event trigger might not only render the
724 * database unusable, but prevent disabling itself to fix the situation.
725 * In this scenario, restarting in standalone mode provides an escape
726 * hatch.
728 * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
729 * therefore will malfunction if pg_event_trigger's indexes are damaged.
730 * To allow recovery from a damaged index, we need some operating mode
731 * wherein event triggers are disabled. (Or we could implement
732 * heapscan-and-sort logic for that case, but having disaster recovery
733 * scenarios depend on code that's otherwise untested isn't appetizing.)
735 * Additionally, event triggers can be disabled with a superuser-only GUC
736 * to make fixing database easier as per 1 above.
738 if (!IsUnderPostmaster || !event_triggers)
739 return;
741 runlist = EventTriggerCommonSetup(parsetree,
742 EVT_DDLCommandStart,
743 "ddl_command_start",
744 &trigdata, false);
745 if (runlist == NIL)
746 return;
748 /* Run the triggers. */
749 EventTriggerInvoke(runlist, &trigdata);
751 /* Cleanup. */
752 list_free(runlist);
755 * Make sure anything the event triggers did will be visible to the main
756 * command.
758 CommandCounterIncrement();
762 * Fire ddl_command_end triggers.
764 void
765 EventTriggerDDLCommandEnd(Node *parsetree)
767 List *runlist;
768 EventTriggerData trigdata;
771 * See EventTriggerDDLCommandStart for a discussion about why event
772 * triggers are disabled in single user mode or via GUC.
774 if (!IsUnderPostmaster || !event_triggers)
775 return;
778 * Also do nothing if our state isn't set up, which it won't be if there
779 * weren't any relevant event triggers at the start of the current DDL
780 * command. This test might therefore seem optional, but it's important
781 * because EventTriggerCommonSetup might find triggers that didn't exist
782 * at the time the command started. Although this function itself
783 * wouldn't crash, the event trigger functions would presumably call
784 * pg_event_trigger_ddl_commands which would fail. Better to do nothing
785 * until the next command.
787 if (!currentEventTriggerState)
788 return;
790 runlist = EventTriggerCommonSetup(parsetree,
791 EVT_DDLCommandEnd, "ddl_command_end",
792 &trigdata, false);
793 if (runlist == NIL)
794 return;
797 * Make sure anything the main command did will be visible to the event
798 * triggers.
800 CommandCounterIncrement();
802 /* Run the triggers. */
803 EventTriggerInvoke(runlist, &trigdata);
805 /* Cleanup. */
806 list_free(runlist);
810 * Fire sql_drop triggers.
812 void
813 EventTriggerSQLDrop(Node *parsetree)
815 List *runlist;
816 EventTriggerData trigdata;
819 * See EventTriggerDDLCommandStart for a discussion about why event
820 * triggers are disabled in single user mode or via a GUC.
822 if (!IsUnderPostmaster || !event_triggers)
823 return;
826 * Use current state to determine whether this event fires at all. If
827 * there are no triggers for the sql_drop event, then we don't have
828 * anything to do here. Note that dropped object collection is disabled
829 * if this is the case, so even if we were to try to run, the list would
830 * be empty.
832 if (!currentEventTriggerState ||
833 slist_is_empty(&currentEventTriggerState->SQLDropList))
834 return;
836 runlist = EventTriggerCommonSetup(parsetree,
837 EVT_SQLDrop, "sql_drop",
838 &trigdata, false);
841 * Nothing to do if run list is empty. Note this typically can't happen,
842 * because if there are no sql_drop events, then objects-to-drop wouldn't
843 * have been collected in the first place and we would have quit above.
844 * But it could occur if event triggers were dropped partway through.
846 if (runlist == NIL)
847 return;
850 * Make sure anything the main command did will be visible to the event
851 * triggers.
853 CommandCounterIncrement();
856 * Make sure pg_event_trigger_dropped_objects only works when running
857 * these triggers. Use PG_TRY to ensure in_sql_drop is reset even when
858 * one trigger fails. (This is perhaps not necessary, as the currentState
859 * variable will be removed shortly by our caller, but it seems better to
860 * play safe.)
862 currentEventTriggerState->in_sql_drop = true;
864 /* Run the triggers. */
865 PG_TRY();
867 EventTriggerInvoke(runlist, &trigdata);
869 PG_FINALLY();
871 currentEventTriggerState->in_sql_drop = false;
873 PG_END_TRY();
875 /* Cleanup. */
876 list_free(runlist);
880 * Fire login event triggers if any are present. The dathasloginevt
881 * pg_database flag is left unchanged when an event trigger is dropped to avoid
882 * complicating the codepath in the case of multiple event triggers. This
883 * function will instead unset the flag if no trigger is defined.
885 void
886 EventTriggerOnLogin(void)
888 List *runlist;
889 EventTriggerData trigdata;
892 * See EventTriggerDDLCommandStart for a discussion about why event
893 * triggers are disabled in single user mode or via a GUC. We also need a
894 * database connection (some background workers don't have it).
896 if (!IsUnderPostmaster || !event_triggers ||
897 !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
898 return;
900 StartTransactionCommand();
901 runlist = EventTriggerCommonSetup(NULL,
902 EVT_Login, "login",
903 &trigdata, false);
905 if (runlist != NIL)
908 * Event trigger execution may require an active snapshot.
910 PushActiveSnapshot(GetTransactionSnapshot());
912 /* Run the triggers. */
913 EventTriggerInvoke(runlist, &trigdata);
915 /* Cleanup. */
916 list_free(runlist);
918 PopActiveSnapshot();
922 * There is no active login event trigger, but our
923 * pg_database.dathasloginevt is set. Try to unset this flag. We use the
924 * lock to prevent concurrent SetDatatabaseHasLoginEventTriggers(), but we
925 * don't want to hang the connection waiting on the lock. Thus, we are
926 * just trying to acquire the lock conditionally.
928 else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
929 0, AccessExclusiveLock))
932 * The lock is held. Now we need to recheck that login event triggers
933 * list is still empty. Once the list is empty, we know that even if
934 * there is a backend which concurrently inserts/enables a login event
935 * trigger, it will update pg_database.dathasloginevt *afterwards*.
937 runlist = EventTriggerCommonSetup(NULL,
938 EVT_Login, "login",
939 &trigdata, true);
941 if (runlist == NIL)
943 Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
944 HeapTuple tuple;
945 Form_pg_database db;
946 ScanKeyData key[1];
947 SysScanDesc scan;
950 * Get the pg_database tuple to scribble on. Note that this does
951 * not directly rely on the syscache to avoid issues with
952 * flattened toast values for the in-place update.
954 ScanKeyInit(&key[0],
955 Anum_pg_database_oid,
956 BTEqualStrategyNumber, F_OIDEQ,
957 ObjectIdGetDatum(MyDatabaseId));
959 scan = systable_beginscan(pg_db, DatabaseOidIndexId, true,
960 NULL, 1, key);
961 tuple = systable_getnext(scan);
962 tuple = heap_copytuple(tuple);
963 systable_endscan(scan);
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 * It's known that changes made by heap_inplace_update() may
980 * be lost due to concurrent normal updates. However, we are
981 * OK with that. The subsequent connections will still have a
982 * chance to set "dathasloginevt" to false.
984 heap_inplace_update(pg_db, tuple);
986 table_close(pg_db, RowExclusiveLock);
987 heap_freetuple(tuple);
989 else
991 list_free(runlist);
994 CommitTransactionCommand();
999 * Fire table_rewrite triggers.
1001 void
1002 EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
1004 List *runlist;
1005 EventTriggerData trigdata;
1008 * See EventTriggerDDLCommandStart for a discussion about why event
1009 * triggers are disabled in single user mode or via a GUC.
1011 if (!IsUnderPostmaster || !event_triggers)
1012 return;
1015 * Also do nothing if our state isn't set up, which it won't be if there
1016 * weren't any relevant event triggers at the start of the current DDL
1017 * command. This test might therefore seem optional, but it's
1018 * *necessary*, because EventTriggerCommonSetup might find triggers that
1019 * didn't exist at the time the command started.
1021 if (!currentEventTriggerState)
1022 return;
1024 runlist = EventTriggerCommonSetup(parsetree,
1025 EVT_TableRewrite,
1026 "table_rewrite",
1027 &trigdata, false);
1028 if (runlist == NIL)
1029 return;
1032 * Make sure pg_event_trigger_table_rewrite_oid only works when running
1033 * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
1034 * when one trigger fails. (This is perhaps not necessary, as the
1035 * currentState variable will be removed shortly by our caller, but it
1036 * seems better to play safe.)
1038 currentEventTriggerState->table_rewrite_oid = tableOid;
1039 currentEventTriggerState->table_rewrite_reason = reason;
1041 /* Run the triggers. */
1042 PG_TRY();
1044 EventTriggerInvoke(runlist, &trigdata);
1046 PG_FINALLY();
1048 currentEventTriggerState->table_rewrite_oid = InvalidOid;
1049 currentEventTriggerState->table_rewrite_reason = 0;
1051 PG_END_TRY();
1053 /* Cleanup. */
1054 list_free(runlist);
1057 * Make sure anything the event triggers did will be visible to the main
1058 * command.
1060 CommandCounterIncrement();
1064 * Invoke each event trigger in a list of event triggers.
1066 static void
1067 EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
1069 MemoryContext context;
1070 MemoryContext oldcontext;
1071 ListCell *lc;
1072 bool first = true;
1074 /* Guard against stack overflow due to recursive event trigger */
1075 check_stack_depth();
1078 * Let's evaluate event triggers in their own memory context, so that any
1079 * leaks get cleaned up promptly.
1081 context = AllocSetContextCreate(CurrentMemoryContext,
1082 "event trigger context",
1083 ALLOCSET_DEFAULT_SIZES);
1084 oldcontext = MemoryContextSwitchTo(context);
1086 /* Call each event trigger. */
1087 foreach(lc, fn_oid_list)
1089 LOCAL_FCINFO(fcinfo, 0);
1090 Oid fnoid = lfirst_oid(lc);
1091 FmgrInfo flinfo;
1092 PgStat_FunctionCallUsage fcusage;
1094 elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
1097 * We want each event trigger to be able to see the results of the
1098 * previous event trigger's action. Caller is responsible for any
1099 * command-counter increment that is needed between the event trigger
1100 * and anything else in the transaction.
1102 if (first)
1103 first = false;
1104 else
1105 CommandCounterIncrement();
1107 /* Look up the function */
1108 fmgr_info(fnoid, &flinfo);
1110 /* Call the function, passing no arguments but setting a context. */
1111 InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
1112 InvalidOid, (Node *) trigdata, NULL);
1113 pgstat_init_function_usage(fcinfo, &fcusage);
1114 FunctionCallInvoke(fcinfo);
1115 pgstat_end_function_usage(&fcusage, true);
1117 /* Reclaim memory. */
1118 MemoryContextReset(context);
1121 /* Restore old memory context and delete the temporary one. */
1122 MemoryContextSwitchTo(oldcontext);
1123 MemoryContextDelete(context);
1127 * Do event triggers support this object type?
1129 bool
1130 EventTriggerSupportsObjectType(ObjectType obtype)
1132 switch (obtype)
1134 case OBJECT_DATABASE:
1135 case OBJECT_TABLESPACE:
1136 case OBJECT_ROLE:
1137 case OBJECT_PARAMETER_ACL:
1138 /* no support for global objects */
1139 return false;
1140 case OBJECT_EVENT_TRIGGER:
1141 /* no support for event triggers on event triggers */
1142 return false;
1143 case OBJECT_ACCESS_METHOD:
1144 case OBJECT_AGGREGATE:
1145 case OBJECT_AMOP:
1146 case OBJECT_AMPROC:
1147 case OBJECT_ATTRIBUTE:
1148 case OBJECT_CAST:
1149 case OBJECT_COLUMN:
1150 case OBJECT_COLLATION:
1151 case OBJECT_CONVERSION:
1152 case OBJECT_DEFACL:
1153 case OBJECT_DEFAULT:
1154 case OBJECT_DOMAIN:
1155 case OBJECT_DOMCONSTRAINT:
1156 case OBJECT_EXTENSION:
1157 case OBJECT_FDW:
1158 case OBJECT_FOREIGN_SERVER:
1159 case OBJECT_FOREIGN_TABLE:
1160 case OBJECT_FUNCTION:
1161 case OBJECT_INDEX:
1162 case OBJECT_LANGUAGE:
1163 case OBJECT_LARGEOBJECT:
1164 case OBJECT_MATVIEW:
1165 case OBJECT_OPCLASS:
1166 case OBJECT_OPERATOR:
1167 case OBJECT_OPFAMILY:
1168 case OBJECT_POLICY:
1169 case OBJECT_PROCEDURE:
1170 case OBJECT_PUBLICATION:
1171 case OBJECT_PUBLICATION_NAMESPACE:
1172 case OBJECT_PUBLICATION_REL:
1173 case OBJECT_ROUTINE:
1174 case OBJECT_RULE:
1175 case OBJECT_SCHEMA:
1176 case OBJECT_SEQUENCE:
1177 case OBJECT_SUBSCRIPTION:
1178 case OBJECT_STATISTIC_EXT:
1179 case OBJECT_TABCONSTRAINT:
1180 case OBJECT_TABLE:
1181 case OBJECT_TRANSFORM:
1182 case OBJECT_TRIGGER:
1183 case OBJECT_TSCONFIGURATION:
1184 case OBJECT_TSDICTIONARY:
1185 case OBJECT_TSPARSER:
1186 case OBJECT_TSTEMPLATE:
1187 case OBJECT_TYPE:
1188 case OBJECT_USER_MAPPING:
1189 case OBJECT_VIEW:
1190 return true;
1193 * There's intentionally no default: case here; we want the
1194 * compiler to warn if a new ObjectType hasn't been handled above.
1198 /* Shouldn't get here, but if we do, say "no support" */
1199 return false;
1203 * Do event triggers support this object class?
1205 bool
1206 EventTriggerSupportsObjectClass(ObjectClass objclass)
1208 switch (objclass)
1210 case OCLASS_DATABASE:
1211 case OCLASS_TBLSPACE:
1212 case OCLASS_ROLE:
1213 case OCLASS_ROLE_MEMBERSHIP:
1214 case OCLASS_PARAMETER_ACL:
1215 /* no support for global objects */
1216 return false;
1217 case OCLASS_EVENT_TRIGGER:
1218 /* no support for event triggers on event triggers */
1219 return false;
1220 case OCLASS_CLASS:
1221 case OCLASS_PROC:
1222 case OCLASS_TYPE:
1223 case OCLASS_CAST:
1224 case OCLASS_COLLATION:
1225 case OCLASS_CONSTRAINT:
1226 case OCLASS_CONVERSION:
1227 case OCLASS_DEFAULT:
1228 case OCLASS_LANGUAGE:
1229 case OCLASS_LARGEOBJECT:
1230 case OCLASS_OPERATOR:
1231 case OCLASS_OPCLASS:
1232 case OCLASS_OPFAMILY:
1233 case OCLASS_AM:
1234 case OCLASS_AMOP:
1235 case OCLASS_AMPROC:
1236 case OCLASS_REWRITE:
1237 case OCLASS_TRIGGER:
1238 case OCLASS_SCHEMA:
1239 case OCLASS_STATISTIC_EXT:
1240 case OCLASS_TSPARSER:
1241 case OCLASS_TSDICT:
1242 case OCLASS_TSTEMPLATE:
1243 case OCLASS_TSCONFIG:
1244 case OCLASS_FDW:
1245 case OCLASS_FOREIGN_SERVER:
1246 case OCLASS_USER_MAPPING:
1247 case OCLASS_DEFACL:
1248 case OCLASS_EXTENSION:
1249 case OCLASS_POLICY:
1250 case OCLASS_PUBLICATION:
1251 case OCLASS_PUBLICATION_NAMESPACE:
1252 case OCLASS_PUBLICATION_REL:
1253 case OCLASS_SUBSCRIPTION:
1254 case OCLASS_TRANSFORM:
1255 return true;
1258 * There's intentionally no default: case here; we want the
1259 * compiler to warn if a new OCLASS hasn't been handled above.
1263 /* Shouldn't get here, but if we do, say "no support" */
1264 return false;
1268 * Prepare event trigger state for a new complete query to run, if necessary;
1269 * returns whether this was done. If it was, EventTriggerEndCompleteQuery must
1270 * be called when the query is done, regardless of whether it succeeds or fails
1271 * -- so use of a PG_TRY block is mandatory.
1273 bool
1274 EventTriggerBeginCompleteQuery(void)
1276 EventTriggerQueryState *state;
1277 MemoryContext cxt;
1280 * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
1281 * reason to have event trigger state at all; so if there are none, don't
1282 * install one.
1284 if (!trackDroppedObjectsNeeded())
1285 return false;
1287 cxt = AllocSetContextCreate(TopMemoryContext,
1288 "event trigger state",
1289 ALLOCSET_DEFAULT_SIZES);
1290 state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
1291 state->cxt = cxt;
1292 slist_init(&(state->SQLDropList));
1293 state->in_sql_drop = false;
1294 state->table_rewrite_oid = InvalidOid;
1296 state->commandCollectionInhibited = currentEventTriggerState ?
1297 currentEventTriggerState->commandCollectionInhibited : false;
1298 state->currentCommand = NULL;
1299 state->commandList = NIL;
1300 state->previous = currentEventTriggerState;
1301 currentEventTriggerState = state;
1303 return true;
1307 * Query completed (or errored out) -- clean up local state, return to previous
1308 * one.
1310 * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
1311 * returned false previously.
1313 * Note: this might be called in the PG_CATCH block of a failing transaction,
1314 * so be wary of running anything unnecessary. (In particular, it's probably
1315 * unwise to try to allocate memory.)
1317 void
1318 EventTriggerEndCompleteQuery(void)
1320 EventTriggerQueryState *prevstate;
1322 prevstate = currentEventTriggerState->previous;
1324 /* this avoids the need for retail pfree of SQLDropList items: */
1325 MemoryContextDelete(currentEventTriggerState->cxt);
1327 currentEventTriggerState = prevstate;
1331 * Do we need to keep close track of objects being dropped?
1333 * This is useful because there is a cost to running with them enabled.
1335 bool
1336 trackDroppedObjectsNeeded(void)
1339 * true if any sql_drop, table_rewrite, ddl_command_end event trigger
1340 * exists
1342 return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
1343 (EventCacheLookup(EVT_TableRewrite) != NIL) ||
1344 (EventCacheLookup(EVT_DDLCommandEnd) != NIL);
1348 * Support for dropped objects information on event trigger functions.
1350 * We keep the list of objects dropped by the current command in current
1351 * state's SQLDropList (comprising SQLDropObject items). Each time a new
1352 * command is to start, a clean EventTriggerQueryState is created; commands
1353 * that drop objects do the dependency.c dance to drop objects, which
1354 * populates the current state's SQLDropList; when the event triggers are
1355 * invoked they can consume the list via pg_event_trigger_dropped_objects().
1356 * When the command finishes, the EventTriggerQueryState is cleared, and
1357 * the one from the previous command is restored (when no command is in
1358 * execution, the current state is NULL).
1360 * All this lets us support the case that an event trigger function drops
1361 * objects "reentrantly".
1365 * Register one object as being dropped by the current command.
1367 void
1368 EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
1370 SQLDropObject *obj;
1371 MemoryContext oldcxt;
1373 if (!currentEventTriggerState)
1374 return;
1376 Assert(EventTriggerSupportsObjectClass(getObjectClass(object)));
1378 /* don't report temp schemas except my own */
1379 if (object->classId == NamespaceRelationId &&
1380 (isAnyTempNamespace(object->objectId) &&
1381 !isTempNamespace(object->objectId)))
1382 return;
1384 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1386 obj = palloc0(sizeof(SQLDropObject));
1387 obj->address = *object;
1388 obj->original = original;
1389 obj->normal = normal;
1392 * Obtain schema names from the object's catalog tuple, if one exists;
1393 * this lets us skip objects in temp schemas. We trust that
1394 * ObjectProperty contains all object classes that can be
1395 * schema-qualified.
1397 if (is_objectclass_supported(object->classId))
1399 Relation catalog;
1400 HeapTuple tuple;
1402 catalog = table_open(obj->address.classId, AccessShareLock);
1403 tuple = get_catalog_object_by_oid(catalog,
1404 get_object_attnum_oid(object->classId),
1405 obj->address.objectId);
1407 if (tuple)
1409 AttrNumber attnum;
1410 Datum datum;
1411 bool isnull;
1413 attnum = get_object_attnum_namespace(obj->address.classId);
1414 if (attnum != InvalidAttrNumber)
1416 datum = heap_getattr(tuple, attnum,
1417 RelationGetDescr(catalog), &isnull);
1418 if (!isnull)
1420 Oid namespaceId;
1422 namespaceId = DatumGetObjectId(datum);
1423 /* temp objects are only reported if they are my own */
1424 if (isTempNamespace(namespaceId))
1426 obj->schemaname = "pg_temp";
1427 obj->istemp = true;
1429 else if (isAnyTempNamespace(namespaceId))
1431 pfree(obj);
1432 table_close(catalog, AccessShareLock);
1433 MemoryContextSwitchTo(oldcxt);
1434 return;
1436 else
1438 obj->schemaname = get_namespace_name(namespaceId);
1439 obj->istemp = false;
1444 if (get_object_namensp_unique(obj->address.classId) &&
1445 obj->address.objectSubId == 0)
1447 attnum = get_object_attnum_name(obj->address.classId);
1448 if (attnum != InvalidAttrNumber)
1450 datum = heap_getattr(tuple, attnum,
1451 RelationGetDescr(catalog), &isnull);
1452 if (!isnull)
1453 obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
1458 table_close(catalog, AccessShareLock);
1460 else
1462 if (object->classId == NamespaceRelationId &&
1463 isTempNamespace(object->objectId))
1464 obj->istemp = true;
1467 /* object identity, objname and objargs */
1468 obj->objidentity =
1469 getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
1470 false);
1472 /* object type */
1473 obj->objecttype = getObjectTypeDescription(&obj->address, false);
1475 slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
1477 MemoryContextSwitchTo(oldcxt);
1481 * pg_event_trigger_dropped_objects
1483 * Make the list of dropped objects available to the user function run by the
1484 * Event Trigger.
1486 Datum
1487 pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
1489 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1490 slist_iter iter;
1493 * Protect this function from being called out of context
1495 if (!currentEventTriggerState ||
1496 !currentEventTriggerState->in_sql_drop)
1497 ereport(ERROR,
1498 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1499 errmsg("%s can only be called in a sql_drop event trigger function",
1500 "pg_event_trigger_dropped_objects()")));
1502 /* Build tuplestore to hold the result rows */
1503 InitMaterializedSRF(fcinfo, 0);
1505 slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
1507 SQLDropObject *obj;
1508 int i = 0;
1509 Datum values[12] = {0};
1510 bool nulls[12] = {0};
1512 obj = slist_container(SQLDropObject, next, iter.cur);
1514 /* classid */
1515 values[i++] = ObjectIdGetDatum(obj->address.classId);
1517 /* objid */
1518 values[i++] = ObjectIdGetDatum(obj->address.objectId);
1520 /* objsubid */
1521 values[i++] = Int32GetDatum(obj->address.objectSubId);
1523 /* original */
1524 values[i++] = BoolGetDatum(obj->original);
1526 /* normal */
1527 values[i++] = BoolGetDatum(obj->normal);
1529 /* is_temporary */
1530 values[i++] = BoolGetDatum(obj->istemp);
1532 /* object_type */
1533 values[i++] = CStringGetTextDatum(obj->objecttype);
1535 /* schema_name */
1536 if (obj->schemaname)
1537 values[i++] = CStringGetTextDatum(obj->schemaname);
1538 else
1539 nulls[i++] = true;
1541 /* object_name */
1542 if (obj->objname)
1543 values[i++] = CStringGetTextDatum(obj->objname);
1544 else
1545 nulls[i++] = true;
1547 /* object_identity */
1548 if (obj->objidentity)
1549 values[i++] = CStringGetTextDatum(obj->objidentity);
1550 else
1551 nulls[i++] = true;
1553 /* address_names and address_args */
1554 if (obj->addrnames)
1556 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
1558 if (obj->addrargs)
1559 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
1560 else
1561 values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
1563 else
1565 nulls[i++] = true;
1566 nulls[i++] = true;
1569 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1570 values, nulls);
1573 return (Datum) 0;
1577 * pg_event_trigger_table_rewrite_oid
1579 * Make the Oid of the table going to be rewritten available to the user
1580 * function run by the Event Trigger.
1582 Datum
1583 pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
1586 * Protect this function from being called out of context
1588 if (!currentEventTriggerState ||
1589 currentEventTriggerState->table_rewrite_oid == InvalidOid)
1590 ereport(ERROR,
1591 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1592 errmsg("%s can only be called in a table_rewrite event trigger function",
1593 "pg_event_trigger_table_rewrite_oid()")));
1595 PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
1599 * pg_event_trigger_table_rewrite_reason
1601 * Make the rewrite reason available to the user.
1603 Datum
1604 pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
1607 * Protect this function from being called out of context
1609 if (!currentEventTriggerState ||
1610 currentEventTriggerState->table_rewrite_reason == 0)
1611 ereport(ERROR,
1612 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1613 errmsg("%s can only be called in a table_rewrite event trigger function",
1614 "pg_event_trigger_table_rewrite_reason()")));
1616 PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
1619 /*-------------------------------------------------------------------------
1620 * Support for DDL command deparsing
1622 * The routines below enable an event trigger function to obtain a list of
1623 * DDL commands as they are executed. There are three main pieces to this
1624 * feature:
1626 * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
1627 * adds a struct CollectedCommand representation of itself to the command list,
1628 * using the routines below.
1630 * 2) Some time after that, ddl_command_end fires and the command list is made
1631 * available to the event trigger function via pg_event_trigger_ddl_commands();
1632 * the complete command details are exposed as a column of type pg_ddl_command.
1634 * 3) An extension can install a function capable of taking a value of type
1635 * pg_ddl_command and transform it into some external, user-visible and/or
1636 * -modifiable representation.
1637 *-------------------------------------------------------------------------
1641 * Inhibit DDL command collection.
1643 void
1644 EventTriggerInhibitCommandCollection(void)
1646 if (!currentEventTriggerState)
1647 return;
1649 currentEventTriggerState->commandCollectionInhibited = true;
1653 * Re-establish DDL command collection.
1655 void
1656 EventTriggerUndoInhibitCommandCollection(void)
1658 if (!currentEventTriggerState)
1659 return;
1661 currentEventTriggerState->commandCollectionInhibited = false;
1665 * EventTriggerCollectSimpleCommand
1666 * Save data about a simple DDL command that was just executed
1668 * address identifies the object being operated on. secondaryObject is an
1669 * object address that was related in some way to the executed command; its
1670 * meaning is command-specific.
1672 * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
1673 * object being moved, objectId is its OID, and secondaryOid is the OID of the
1674 * old schema. (The destination schema OID can be obtained by catalog lookup
1675 * of the object.)
1677 void
1678 EventTriggerCollectSimpleCommand(ObjectAddress address,
1679 ObjectAddress secondaryObject,
1680 Node *parsetree)
1682 MemoryContext oldcxt;
1683 CollectedCommand *command;
1685 /* ignore if event trigger context not set, or collection disabled */
1686 if (!currentEventTriggerState ||
1687 currentEventTriggerState->commandCollectionInhibited)
1688 return;
1690 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1692 command = palloc(sizeof(CollectedCommand));
1694 command->type = SCT_Simple;
1695 command->in_extension = creating_extension;
1697 command->d.simple.address = address;
1698 command->d.simple.secondaryObject = secondaryObject;
1699 command->parsetree = copyObject(parsetree);
1701 currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
1702 command);
1704 MemoryContextSwitchTo(oldcxt);
1708 * EventTriggerAlterTableStart
1709 * Prepare to receive data on an ALTER TABLE command about to be executed
1711 * Note we don't collect the command immediately; instead we keep it in
1712 * currentCommand, and only when we're done processing the subcommands we will
1713 * add it to the command list.
1715 void
1716 EventTriggerAlterTableStart(Node *parsetree)
1718 MemoryContext oldcxt;
1719 CollectedCommand *command;
1721 /* ignore if event trigger context not set, or collection disabled */
1722 if (!currentEventTriggerState ||
1723 currentEventTriggerState->commandCollectionInhibited)
1724 return;
1726 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1728 command = palloc(sizeof(CollectedCommand));
1730 command->type = SCT_AlterTable;
1731 command->in_extension = creating_extension;
1733 command->d.alterTable.classId = RelationRelationId;
1734 command->d.alterTable.objectId = InvalidOid;
1735 command->d.alterTable.subcmds = NIL;
1736 command->parsetree = copyObject(parsetree);
1738 command->parent = currentEventTriggerState->currentCommand;
1739 currentEventTriggerState->currentCommand = command;
1741 MemoryContextSwitchTo(oldcxt);
1745 * Remember the OID of the object being affected by an ALTER TABLE.
1747 * This is needed because in some cases we don't know the OID until later.
1749 void
1750 EventTriggerAlterTableRelid(Oid objectId)
1752 if (!currentEventTriggerState ||
1753 currentEventTriggerState->commandCollectionInhibited)
1754 return;
1756 currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
1760 * EventTriggerCollectAlterTableSubcmd
1761 * Save data about a single part of an ALTER TABLE.
1763 * Several different commands go through this path, but apart from ALTER TABLE
1764 * itself, they are all concerned with AlterTableCmd nodes that are generated
1765 * internally, so that's all that this code needs to handle at the moment.
1767 void
1768 EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
1770 MemoryContext oldcxt;
1771 CollectedATSubcmd *newsub;
1773 /* ignore if event trigger context not set, or collection disabled */
1774 if (!currentEventTriggerState ||
1775 currentEventTriggerState->commandCollectionInhibited)
1776 return;
1778 Assert(IsA(subcmd, AlterTableCmd));
1779 Assert(currentEventTriggerState->currentCommand != NULL);
1780 Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
1782 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1784 newsub = palloc(sizeof(CollectedATSubcmd));
1785 newsub->address = address;
1786 newsub->parsetree = copyObject(subcmd);
1788 currentEventTriggerState->currentCommand->d.alterTable.subcmds =
1789 lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
1791 MemoryContextSwitchTo(oldcxt);
1795 * EventTriggerAlterTableEnd
1796 * Finish up saving an ALTER TABLE command, and add it to command list.
1798 * FIXME this API isn't considering the possibility that an xact/subxact is
1799 * aborted partway through. Probably it's best to add an
1800 * AtEOSubXact_EventTriggers() to fix this.
1802 void
1803 EventTriggerAlterTableEnd(void)
1805 CollectedCommand *parent;
1807 /* ignore if event trigger context not set, or collection disabled */
1808 if (!currentEventTriggerState ||
1809 currentEventTriggerState->commandCollectionInhibited)
1810 return;
1812 parent = currentEventTriggerState->currentCommand->parent;
1814 /* If no subcommands, don't collect */
1815 if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
1817 MemoryContext oldcxt;
1819 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1821 currentEventTriggerState->commandList =
1822 lappend(currentEventTriggerState->commandList,
1823 currentEventTriggerState->currentCommand);
1825 MemoryContextSwitchTo(oldcxt);
1827 else
1828 pfree(currentEventTriggerState->currentCommand);
1830 currentEventTriggerState->currentCommand = parent;
1834 * EventTriggerCollectGrant
1835 * Save data about a GRANT/REVOKE command being executed
1837 * This function creates a copy of the InternalGrant, as the original might
1838 * not have the right lifetime.
1840 void
1841 EventTriggerCollectGrant(InternalGrant *istmt)
1843 MemoryContext oldcxt;
1844 CollectedCommand *command;
1845 InternalGrant *icopy;
1846 ListCell *cell;
1848 /* ignore if event trigger context not set, or collection disabled */
1849 if (!currentEventTriggerState ||
1850 currentEventTriggerState->commandCollectionInhibited)
1851 return;
1853 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1856 * This is tedious, but necessary.
1858 icopy = palloc(sizeof(InternalGrant));
1859 memcpy(icopy, istmt, sizeof(InternalGrant));
1860 icopy->objects = list_copy(istmt->objects);
1861 icopy->grantees = list_copy(istmt->grantees);
1862 icopy->col_privs = NIL;
1863 foreach(cell, istmt->col_privs)
1864 icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
1866 /* Now collect it, using the copied InternalGrant */
1867 command = palloc(sizeof(CollectedCommand));
1868 command->type = SCT_Grant;
1869 command->in_extension = creating_extension;
1870 command->d.grant.istmt = icopy;
1871 command->parsetree = NULL;
1873 currentEventTriggerState->commandList =
1874 lappend(currentEventTriggerState->commandList, command);
1876 MemoryContextSwitchTo(oldcxt);
1880 * EventTriggerCollectAlterOpFam
1881 * Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
1882 * executed
1884 void
1885 EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
1886 List *operators, List *procedures)
1888 MemoryContext oldcxt;
1889 CollectedCommand *command;
1891 /* ignore if event trigger context not set, or collection disabled */
1892 if (!currentEventTriggerState ||
1893 currentEventTriggerState->commandCollectionInhibited)
1894 return;
1896 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1898 command = palloc(sizeof(CollectedCommand));
1899 command->type = SCT_AlterOpFamily;
1900 command->in_extension = creating_extension;
1901 ObjectAddressSet(command->d.opfam.address,
1902 OperatorFamilyRelationId, opfamoid);
1903 command->d.opfam.operators = operators;
1904 command->d.opfam.procedures = procedures;
1905 command->parsetree = (Node *) copyObject(stmt);
1907 currentEventTriggerState->commandList =
1908 lappend(currentEventTriggerState->commandList, command);
1910 MemoryContextSwitchTo(oldcxt);
1914 * EventTriggerCollectCreateOpClass
1915 * Save data about a CREATE OPERATOR CLASS command being executed
1917 void
1918 EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
1919 List *operators, List *procedures)
1921 MemoryContext oldcxt;
1922 CollectedCommand *command;
1924 /* ignore if event trigger context not set, or collection disabled */
1925 if (!currentEventTriggerState ||
1926 currentEventTriggerState->commandCollectionInhibited)
1927 return;
1929 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1931 command = palloc0(sizeof(CollectedCommand));
1932 command->type = SCT_CreateOpClass;
1933 command->in_extension = creating_extension;
1934 ObjectAddressSet(command->d.createopc.address,
1935 OperatorClassRelationId, opcoid);
1936 command->d.createopc.operators = operators;
1937 command->d.createopc.procedures = procedures;
1938 command->parsetree = (Node *) copyObject(stmt);
1940 currentEventTriggerState->commandList =
1941 lappend(currentEventTriggerState->commandList, command);
1943 MemoryContextSwitchTo(oldcxt);
1947 * EventTriggerCollectAlterTSConfig
1948 * Save data about an ALTER TEXT SEARCH CONFIGURATION command being
1949 * executed
1951 void
1952 EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
1953 Oid *dictIds, int ndicts)
1955 MemoryContext oldcxt;
1956 CollectedCommand *command;
1958 /* ignore if event trigger context not set, or collection disabled */
1959 if (!currentEventTriggerState ||
1960 currentEventTriggerState->commandCollectionInhibited)
1961 return;
1963 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1965 command = palloc0(sizeof(CollectedCommand));
1966 command->type = SCT_AlterTSConfig;
1967 command->in_extension = creating_extension;
1968 ObjectAddressSet(command->d.atscfg.address,
1969 TSConfigRelationId, cfgId);
1970 command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
1971 memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
1972 command->d.atscfg.ndicts = ndicts;
1973 command->parsetree = (Node *) copyObject(stmt);
1975 currentEventTriggerState->commandList =
1976 lappend(currentEventTriggerState->commandList, command);
1978 MemoryContextSwitchTo(oldcxt);
1982 * EventTriggerCollectAlterDefPrivs
1983 * Save data about an ALTER DEFAULT PRIVILEGES command being
1984 * executed
1986 void
1987 EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
1989 MemoryContext oldcxt;
1990 CollectedCommand *command;
1992 /* ignore if event trigger context not set, or collection disabled */
1993 if (!currentEventTriggerState ||
1994 currentEventTriggerState->commandCollectionInhibited)
1995 return;
1997 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1999 command = palloc0(sizeof(CollectedCommand));
2000 command->type = SCT_AlterDefaultPrivileges;
2001 command->d.defprivs.objtype = stmt->action->objtype;
2002 command->in_extension = creating_extension;
2003 command->parsetree = (Node *) copyObject(stmt);
2005 currentEventTriggerState->commandList =
2006 lappend(currentEventTriggerState->commandList, command);
2007 MemoryContextSwitchTo(oldcxt);
2011 * In a ddl_command_end event trigger, this function reports the DDL commands
2012 * being run.
2014 Datum
2015 pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
2017 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2018 ListCell *lc;
2021 * Protect this function from being called out of context
2023 if (!currentEventTriggerState)
2024 ereport(ERROR,
2025 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
2026 errmsg("%s can only be called in an event trigger function",
2027 "pg_event_trigger_ddl_commands()")));
2029 /* Build tuplestore to hold the result rows */
2030 InitMaterializedSRF(fcinfo, 0);
2032 foreach(lc, currentEventTriggerState->commandList)
2034 CollectedCommand *cmd = lfirst(lc);
2035 Datum values[9];
2036 bool nulls[9] = {0};
2037 ObjectAddress addr;
2038 int i = 0;
2041 * For IF NOT EXISTS commands that attempt to create an existing
2042 * object, the returned OID is Invalid. Don't return anything.
2044 * One might think that a viable alternative would be to look up the
2045 * Oid of the existing object and run the deparse with that. But
2046 * since the parse tree might be different from the one that created
2047 * the object in the first place, we might not end up in a consistent
2048 * state anyway.
2050 if (cmd->type == SCT_Simple &&
2051 !OidIsValid(cmd->d.simple.address.objectId))
2052 continue;
2054 switch (cmd->type)
2056 case SCT_Simple:
2057 case SCT_AlterTable:
2058 case SCT_AlterOpFamily:
2059 case SCT_CreateOpClass:
2060 case SCT_AlterTSConfig:
2062 char *identity;
2063 char *type;
2064 char *schema = NULL;
2066 if (cmd->type == SCT_Simple)
2067 addr = cmd->d.simple.address;
2068 else if (cmd->type == SCT_AlterTable)
2069 ObjectAddressSet(addr,
2070 cmd->d.alterTable.classId,
2071 cmd->d.alterTable.objectId);
2072 else if (cmd->type == SCT_AlterOpFamily)
2073 addr = cmd->d.opfam.address;
2074 else if (cmd->type == SCT_CreateOpClass)
2075 addr = cmd->d.createopc.address;
2076 else if (cmd->type == SCT_AlterTSConfig)
2077 addr = cmd->d.atscfg.address;
2080 * If an object was dropped in the same command we may end
2081 * up in a situation where we generated a message but can
2082 * no longer look for the object information, so skip it
2083 * rather than failing. This can happen for example with
2084 * some subcommand combinations of ALTER TABLE.
2086 identity = getObjectIdentity(&addr, true);
2087 if (identity == NULL)
2088 continue;
2090 /* The type can never be NULL. */
2091 type = getObjectTypeDescription(&addr, true);
2094 * Obtain schema name, if any ("pg_temp" if a temp
2095 * object). If the object class is not in the supported
2096 * list here, we assume it's a schema-less object type,
2097 * and thus "schema" remains set to NULL.
2099 if (is_objectclass_supported(addr.classId))
2101 AttrNumber nspAttnum;
2103 nspAttnum = get_object_attnum_namespace(addr.classId);
2104 if (nspAttnum != InvalidAttrNumber)
2106 Relation catalog;
2107 HeapTuple objtup;
2108 Oid schema_oid;
2109 bool isnull;
2111 catalog = table_open(addr.classId, AccessShareLock);
2112 objtup = get_catalog_object_by_oid(catalog,
2113 get_object_attnum_oid(addr.classId),
2114 addr.objectId);
2115 if (!HeapTupleIsValid(objtup))
2116 elog(ERROR, "cache lookup failed for object %u/%u",
2117 addr.classId, addr.objectId);
2118 schema_oid =
2119 heap_getattr(objtup, nspAttnum,
2120 RelationGetDescr(catalog), &isnull);
2121 if (isnull)
2122 elog(ERROR,
2123 "invalid null namespace in object %u/%u/%d",
2124 addr.classId, addr.objectId, addr.objectSubId);
2125 schema = get_namespace_name_or_temp(schema_oid);
2127 table_close(catalog, AccessShareLock);
2131 /* classid */
2132 values[i++] = ObjectIdGetDatum(addr.classId);
2133 /* objid */
2134 values[i++] = ObjectIdGetDatum(addr.objectId);
2135 /* objsubid */
2136 values[i++] = Int32GetDatum(addr.objectSubId);
2137 /* command tag */
2138 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2139 /* object_type */
2140 values[i++] = CStringGetTextDatum(type);
2141 /* schema */
2142 if (schema == NULL)
2143 nulls[i++] = true;
2144 else
2145 values[i++] = CStringGetTextDatum(schema);
2146 /* identity */
2147 values[i++] = CStringGetTextDatum(identity);
2148 /* in_extension */
2149 values[i++] = BoolGetDatum(cmd->in_extension);
2150 /* command */
2151 values[i++] = PointerGetDatum(cmd);
2153 break;
2155 case SCT_AlterDefaultPrivileges:
2156 /* classid */
2157 nulls[i++] = true;
2158 /* objid */
2159 nulls[i++] = true;
2160 /* objsubid */
2161 nulls[i++] = true;
2162 /* command tag */
2163 values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2164 /* object_type */
2165 values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
2166 /* schema */
2167 nulls[i++] = true;
2168 /* identity */
2169 nulls[i++] = true;
2170 /* in_extension */
2171 values[i++] = BoolGetDatum(cmd->in_extension);
2172 /* command */
2173 values[i++] = PointerGetDatum(cmd);
2174 break;
2176 case SCT_Grant:
2177 /* classid */
2178 nulls[i++] = true;
2179 /* objid */
2180 nulls[i++] = true;
2181 /* objsubid */
2182 nulls[i++] = true;
2183 /* command tag */
2184 values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
2185 "GRANT" : "REVOKE");
2186 /* object_type */
2187 values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
2188 /* schema */
2189 nulls[i++] = true;
2190 /* identity */
2191 nulls[i++] = true;
2192 /* in_extension */
2193 values[i++] = BoolGetDatum(cmd->in_extension);
2194 /* command */
2195 values[i++] = PointerGetDatum(cmd);
2196 break;
2199 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2200 values, nulls);
2203 PG_RETURN_VOID();
2207 * Return the ObjectType as a string, as it would appear in GRANT and
2208 * REVOKE commands.
2210 static const char *
2211 stringify_grant_objtype(ObjectType objtype)
2213 switch (objtype)
2215 case OBJECT_COLUMN:
2216 return "COLUMN";
2217 case OBJECT_TABLE:
2218 return "TABLE";
2219 case OBJECT_SEQUENCE:
2220 return "SEQUENCE";
2221 case OBJECT_DATABASE:
2222 return "DATABASE";
2223 case OBJECT_DOMAIN:
2224 return "DOMAIN";
2225 case OBJECT_FDW:
2226 return "FOREIGN DATA WRAPPER";
2227 case OBJECT_FOREIGN_SERVER:
2228 return "FOREIGN SERVER";
2229 case OBJECT_FUNCTION:
2230 return "FUNCTION";
2231 case OBJECT_LANGUAGE:
2232 return "LANGUAGE";
2233 case OBJECT_LARGEOBJECT:
2234 return "LARGE OBJECT";
2235 case OBJECT_SCHEMA:
2236 return "SCHEMA";
2237 case OBJECT_PARAMETER_ACL:
2238 return "PARAMETER";
2239 case OBJECT_PROCEDURE:
2240 return "PROCEDURE";
2241 case OBJECT_ROUTINE:
2242 return "ROUTINE";
2243 case OBJECT_TABLESPACE:
2244 return "TABLESPACE";
2245 case OBJECT_TYPE:
2246 return "TYPE";
2247 /* these currently aren't used */
2248 case OBJECT_ACCESS_METHOD:
2249 case OBJECT_AGGREGATE:
2250 case OBJECT_AMOP:
2251 case OBJECT_AMPROC:
2252 case OBJECT_ATTRIBUTE:
2253 case OBJECT_CAST:
2254 case OBJECT_COLLATION:
2255 case OBJECT_CONVERSION:
2256 case OBJECT_DEFAULT:
2257 case OBJECT_DEFACL:
2258 case OBJECT_DOMCONSTRAINT:
2259 case OBJECT_EVENT_TRIGGER:
2260 case OBJECT_EXTENSION:
2261 case OBJECT_FOREIGN_TABLE:
2262 case OBJECT_INDEX:
2263 case OBJECT_MATVIEW:
2264 case OBJECT_OPCLASS:
2265 case OBJECT_OPERATOR:
2266 case OBJECT_OPFAMILY:
2267 case OBJECT_POLICY:
2268 case OBJECT_PUBLICATION:
2269 case OBJECT_PUBLICATION_NAMESPACE:
2270 case OBJECT_PUBLICATION_REL:
2271 case OBJECT_ROLE:
2272 case OBJECT_RULE:
2273 case OBJECT_STATISTIC_EXT:
2274 case OBJECT_SUBSCRIPTION:
2275 case OBJECT_TABCONSTRAINT:
2276 case OBJECT_TRANSFORM:
2277 case OBJECT_TRIGGER:
2278 case OBJECT_TSCONFIGURATION:
2279 case OBJECT_TSDICTIONARY:
2280 case OBJECT_TSPARSER:
2281 case OBJECT_TSTEMPLATE:
2282 case OBJECT_USER_MAPPING:
2283 case OBJECT_VIEW:
2284 elog(ERROR, "unsupported object type: %d", (int) objtype);
2287 return "???"; /* keep compiler quiet */
2291 * Return the ObjectType as a string; as above, but use the spelling
2292 * in ALTER DEFAULT PRIVILEGES commands instead. Generally this is just
2293 * the plural.
2295 static const char *
2296 stringify_adefprivs_objtype(ObjectType objtype)
2298 switch (objtype)
2300 case OBJECT_COLUMN:
2301 return "COLUMNS";
2302 case OBJECT_TABLE:
2303 return "TABLES";
2304 case OBJECT_SEQUENCE:
2305 return "SEQUENCES";
2306 case OBJECT_DATABASE:
2307 return "DATABASES";
2308 case OBJECT_DOMAIN:
2309 return "DOMAINS";
2310 case OBJECT_FDW:
2311 return "FOREIGN DATA WRAPPERS";
2312 case OBJECT_FOREIGN_SERVER:
2313 return "FOREIGN SERVERS";
2314 case OBJECT_FUNCTION:
2315 return "FUNCTIONS";
2316 case OBJECT_LANGUAGE:
2317 return "LANGUAGES";
2318 case OBJECT_LARGEOBJECT:
2319 return "LARGE OBJECTS";
2320 case OBJECT_SCHEMA:
2321 return "SCHEMAS";
2322 case OBJECT_PROCEDURE:
2323 return "PROCEDURES";
2324 case OBJECT_ROUTINE:
2325 return "ROUTINES";
2326 case OBJECT_TABLESPACE:
2327 return "TABLESPACES";
2328 case OBJECT_TYPE:
2329 return "TYPES";
2330 /* these currently aren't used */
2331 case OBJECT_ACCESS_METHOD:
2332 case OBJECT_AGGREGATE:
2333 case OBJECT_AMOP:
2334 case OBJECT_AMPROC:
2335 case OBJECT_ATTRIBUTE:
2336 case OBJECT_CAST:
2337 case OBJECT_COLLATION:
2338 case OBJECT_CONVERSION:
2339 case OBJECT_DEFAULT:
2340 case OBJECT_DEFACL:
2341 case OBJECT_DOMCONSTRAINT:
2342 case OBJECT_EVENT_TRIGGER:
2343 case OBJECT_EXTENSION:
2344 case OBJECT_FOREIGN_TABLE:
2345 case OBJECT_INDEX:
2346 case OBJECT_MATVIEW:
2347 case OBJECT_OPCLASS:
2348 case OBJECT_OPERATOR:
2349 case OBJECT_OPFAMILY:
2350 case OBJECT_PARAMETER_ACL:
2351 case OBJECT_POLICY:
2352 case OBJECT_PUBLICATION:
2353 case OBJECT_PUBLICATION_NAMESPACE:
2354 case OBJECT_PUBLICATION_REL:
2355 case OBJECT_ROLE:
2356 case OBJECT_RULE:
2357 case OBJECT_STATISTIC_EXT:
2358 case OBJECT_SUBSCRIPTION:
2359 case OBJECT_TABCONSTRAINT:
2360 case OBJECT_TRANSFORM:
2361 case OBJECT_TRIGGER:
2362 case OBJECT_TSCONFIGURATION:
2363 case OBJECT_TSDICTIONARY:
2364 case OBJECT_TSPARSER:
2365 case OBJECT_TSTEMPLATE:
2366 case OBJECT_USER_MAPPING:
2367 case OBJECT_VIEW:
2368 elog(ERROR, "unsupported object type: %d", (int) objtype);
2371 return "???"; /* keep compiler quiet */