1 /*-------------------------------------------------------------------------
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
10 * src/backend/commands/event_trigger.c
12 *-------------------------------------------------------------------------
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"
41 #include "lib/ilist.h"
42 #include "miscadmin.h"
43 #include "parser/parse_func.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 */
65 slist_head SQLDropList
;
69 Oid table_rewrite_oid
; /* InvalidOid, or set for table_rewrite
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
;
84 bool event_triggers
= true;
86 /* Support for dropped objects */
87 typedef struct SQLDropObject
89 ObjectAddress address
;
90 const char *schemaname
;
92 const char *objidentity
;
93 const char *objecttype
;
102 static void AlterEventTriggerOwner_internal(Relation rel
,
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
)
125 Oid evtowner
= GetUserId();
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.
136 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
137 errmsg("permission denied to create event trigger \"%s\"",
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)
148 (errcode(ERRCODE_SYNTAX_ERROR
),
149 errmsg("unrecognized event name \"%s\"",
152 /* Validate filter conditions. */
153 foreach(lc
, stmt
->whenclause
)
155 DefElem
*def
= (DefElem
*) lfirst(lc
);
157 if (strcmp(def
->defname
, "tag") == 0)
160 error_duplicate_filter_variable(def
->defname
);
161 tags
= (List
*) def
->arg
;
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)
174 validate_ddl_tags("tag", tags
);
175 else if (strcmp(stmt
->eventname
, "table_rewrite") == 0
177 validate_table_rewrite_tags("tag", tags
);
178 else if (strcmp(stmt
->eventname
, "login") == 0 && tags
!= NULL
)
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
187 tuple
= SearchSysCache1(EVENTTRIGGERNAME
, CStringGetDatum(stmt
->trigname
));
188 if (HeapTupleIsValid(tuple
))
190 (errcode(ERRCODE_DUPLICATE_OBJECT
),
191 errmsg("event trigger \"%s\" already exists",
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
)
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.
212 validate_ddl_tags(const char *filtervar
, List
*taglist
)
218 const char *tagstr
= strVal(lfirst(lc
));
219 CommandTag commandTag
= GetCommandTagEnum(tagstr
);
221 if (commandTag
== CMDTAG_UNKNOWN
)
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
))
228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
229 /* translator: %s represents an SQL statement name */
230 errmsg("event triggers are not supported for %s",
236 * Validate DDL command tags for event table_rewrite.
239 validate_table_rewrite_tags(const char *filtervar
, List
*taglist
)
245 const char *tagstr
= strVal(lfirst(lc
));
246 CommandTag commandTag
= GetCommandTagEnum(tagstr
);
248 if (!command_tag_table_rewrite_ok(commandTag
))
250 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
251 /* translator: %s represents an SQL statement name */
252 errmsg("event triggers are not supported for %s",
258 * Complain about a duplicate filter variable.
261 error_duplicate_filter_variable(const char *defname
)
264 (errcode(ERRCODE_SYNTAX_ERROR
),
265 errmsg("filter variable \"%s\" specified more than once",
270 * Insert the new pg_event_trigger row and record dependencies.
273 insert_event_trigger_tuple(const char *trigname
, const char *eventname
, Oid evtOwner
,
274 Oid funcoid
, List
*taglist
)
279 Datum values
[Natts_pg_event_trigger
];
280 bool nulls
[Natts_pg_event_trigger
];
281 NameData evtnamedata
,
283 ObjectAddress myself
,
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
);
303 nulls
[Anum_pg_event_trigger_evttags
- 1] = true;
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
);
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.
356 filter_list_to_array(List
*filterlist
)
361 l
= list_length(filterlist
);
363 data
= (Datum
*) palloc(l
* sizeof(Datum
));
365 foreach(lc
, filterlist
)
367 const char *value
= strVal(lfirst(lc
));
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
));
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.
386 SetDatabaseHasLoginEventTriggers(void)
388 /* Set dathasloginevt flag in pg_database */
390 Relation pg_db
= table_open(DatabaseRelationId
, RowExclusiveLock
);
391 ItemPointerData otid
;
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
)
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
))
437 (errcode(ERRCODE_UNDEFINED_OBJECT
),
438 errmsg("event trigger \"%s\" does not exist",
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
,
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
,
466 table_close(tgrel
, RowExclusiveLock
);
472 * Change event trigger's owner -- by name
475 AlterEventTriggerOwner(const char *name
, Oid newOwnerId
)
479 Form_pg_event_trigger evtForm
;
481 ObjectAddress address
;
483 rel
= table_open(EventTriggerRelationId
, RowExclusiveLock
);
485 tup
= SearchSysCacheCopy1(EVENTTRIGGERNAME
, CStringGetDatum(name
));
487 if (!HeapTupleIsValid(tup
))
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
);
501 table_close(rel
, RowExclusiveLock
);
507 * Change event trigger owner, by OID
510 AlterEventTriggerOwner_oid(Oid trigOid
, Oid newOwnerId
)
515 rel
= table_open(EventTriggerRelationId
, RowExclusiveLock
);
517 tup
= SearchSysCacheCopy1(EVENTTRIGGEROID
, ObjectIdGetDatum(trigOid
));
519 if (!HeapTupleIsValid(tup
))
521 (errcode(ERRCODE_UNDEFINED_OBJECT
),
522 errmsg("event trigger with OID %u does not exist", trigOid
)));
524 AlterEventTriggerOwner_internal(rel
, tup
, newOwnerId
);
528 table_close(rel
, RowExclusiveLock
);
532 * Internal workhorse for changing an event trigger's owner
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
)
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
))
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
,
564 InvokeObjectPostAlterHook(EventTriggerRelationId
,
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
)
579 oid
= GetSysCacheOid1(EVENTTRIGGERNAME
, Anum_pg_event_trigger_oid
,
580 CStringGetDatum(trigname
));
581 if (!OidIsValid(oid
) && !missing_ok
)
583 (errcode(ERRCODE_UNDEFINED_OBJECT
),
584 errmsg("event trigger \"%s\" does not exist", trigname
)));
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
594 filter_event_trigger(CommandTag tag
, EventTriggerCacheItem
*item
)
597 * Filter by session replication role, knowing that we never see disabled
600 if (SessionReplicationRole
== SESSION_REPLICATION_ROLE_REPLICA
)
602 if (item
->enabled
== TRIGGER_FIRES_ON_ORIGIN
)
607 if (item
->enabled
== TRIGGER_FIRES_ON_REPLICA
)
611 /* Filter by tags, if any were specified. */
612 if (!bms_is_empty(item
->tagset
) && !bms_is_member(tag
, item
->tagset
))
615 /* if we reach that point, we're not filtering out this item */
620 EventTriggerGetTag(Node
*parsetree
, EventTriggerEvent event
)
622 if (event
== EVT_Login
)
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.
634 EventTriggerCommonSetup(Node
*parsetree
,
635 EventTriggerEvent event
, const char *eventstr
,
636 EventTriggerData
*trigdata
, bool unfiltered
)
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
661 dbgtag
= EventTriggerGetTag(parsetree
, event
);
663 if (event
== EVT_DDLCommandStart
||
664 event
== EVT_DDLCommandEnd
||
665 event
== EVT_SQLDrop
||
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
));
679 /* Use cache to find triggers for this event; fast exit if none. */
680 cachelist
= EventCacheLookup(event
);
681 if (cachelist
== 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
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 */
709 trigdata
->type
= T_EventTriggerData
;
710 trigdata
->event
= eventstr
;
711 trigdata
->parsetree
= parsetree
;
718 * Fire ddl_command_start triggers.
721 EventTriggerDDLCommandStart(Node
*parsetree
)
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
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
)
748 runlist
= EventTriggerCommonSetup(parsetree
,
755 /* Run the triggers. */
756 EventTriggerInvoke(runlist
, &trigdata
);
762 * Make sure anything the event triggers did will be visible to the main
765 CommandCounterIncrement();
769 * Fire ddl_command_end triggers.
772 EventTriggerDDLCommandEnd(Node
*parsetree
)
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
)
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
)
797 runlist
= EventTriggerCommonSetup(parsetree
,
798 EVT_DDLCommandEnd
, "ddl_command_end",
804 * Make sure anything the main command did will be visible to the event
807 CommandCounterIncrement();
809 /* Run the triggers. */
810 EventTriggerInvoke(runlist
, &trigdata
);
817 * Fire sql_drop triggers.
820 EventTriggerSQLDrop(Node
*parsetree
)
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
)
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
839 if (!currentEventTriggerState
||
840 slist_is_empty(¤tEventTriggerState
->SQLDropList
))
843 runlist
= EventTriggerCommonSetup(parsetree
,
844 EVT_SQLDrop
, "sql_drop",
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.
857 * Make sure anything the main command did will be visible to the event
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
869 currentEventTriggerState
->in_sql_drop
= true;
871 /* Run the triggers. */
874 EventTriggerInvoke(runlist
, &trigdata
);
878 currentEventTriggerState
->in_sql_drop
= false;
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.
893 EventTriggerOnLogin(void)
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
)
907 StartTransactionCommand();
908 runlist
= EventTriggerCommonSetup(NULL
,
915 * Event trigger execution may require an active snapshot.
917 PushActiveSnapshot(GetTransactionSnapshot());
919 /* Run the triggers. */
920 EventTriggerInvoke(runlist
, &trigdata
);
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
,
950 Relation pg_db
= table_open(DatabaseRelationId
, RowExclusiveLock
);
956 /* Fetch a copy of the tuple to scribble on */
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
);
982 systable_inplace_update_cancel(state
);
983 table_close(pg_db
, RowExclusiveLock
);
984 heap_freetuple(tuple
);
991 CommitTransactionCommand();
996 * Fire table_rewrite triggers.
999 EventTriggerTableRewrite(Node
*parsetree
, Oid tableOid
, int reason
)
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
)
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
)
1021 runlist
= EventTriggerCommonSetup(parsetree
,
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. */
1041 EventTriggerInvoke(runlist
, &trigdata
);
1045 currentEventTriggerState
->table_rewrite_oid
= InvalidOid
;
1046 currentEventTriggerState
->table_rewrite_reason
= 0;
1054 * Make sure anything the event triggers did will be visible to the main
1057 CommandCounterIncrement();
1061 * Invoke each event trigger in a list of event triggers.
1064 EventTriggerInvoke(List
*fn_oid_list
, EventTriggerData
*trigdata
)
1066 MemoryContext context
;
1067 MemoryContext oldcontext
;
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
);
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.
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.
1129 EventTriggerSupportsObjectType(ObjectType obtype
)
1133 case OBJECT_DATABASE
:
1134 case OBJECT_TABLESPACE
:
1136 case OBJECT_PARAMETER_ACL
:
1137 /* no support for global objects (except subscriptions) */
1139 case OBJECT_EVENT_TRIGGER
:
1140 /* no support for event triggers on event triggers */
1148 * Do event triggers support this object class?
1150 * See also event trigger documentation in event-trigger.sgml.
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) */
1164 case EventTriggerRelationId
:
1165 /* no support for event triggers on event triggers */
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.
1179 EventTriggerBeginCompleteQuery(void)
1181 EventTriggerQueryState
*state
;
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
1189 if (!trackDroppedObjectsNeeded())
1192 cxt
= AllocSetContextCreate(TopMemoryContext
,
1193 "event trigger state",
1194 ALLOCSET_DEFAULT_SIZES
);
1195 state
= MemoryContextAlloc(cxt
, sizeof(EventTriggerQueryState
));
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
;
1212 * Query completed (or errored out) -- clean up local state, return to previous
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.)
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.
1241 trackDroppedObjectsNeeded(void)
1244 * true if any sql_drop, table_rewrite, ddl_command_end event trigger
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.
1273 EventTriggerSQLDropAddObject(const ObjectAddress
*object
, bool original
, bool normal
)
1276 MemoryContext oldcxt
;
1278 if (!currentEventTriggerState
)
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
)))
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
1302 if (is_objectclass_supported(object
->classId
))
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
);
1318 attnum
= get_object_attnum_namespace(obj
->address
.classId
);
1319 if (attnum
!= InvalidAttrNumber
)
1321 datum
= heap_getattr(tuple
, attnum
,
1322 RelationGetDescr(catalog
), &isnull
);
1327 namespaceId
= DatumGetObjectId(datum
);
1328 /* temp objects are only reported if they are my own */
1329 if (isTempNamespace(namespaceId
))
1331 obj
->schemaname
= "pg_temp";
1334 else if (isAnyTempNamespace(namespaceId
))
1337 table_close(catalog
, AccessShareLock
);
1338 MemoryContextSwitchTo(oldcxt
);
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
);
1358 obj
->objname
= pstrdup(NameStr(*DatumGetName(datum
)));
1363 table_close(catalog
, AccessShareLock
);
1367 if (object
->classId
== NamespaceRelationId
&&
1368 isTempNamespace(object
->objectId
))
1372 /* object identity, objname and objargs */
1374 getObjectIdentityParts(&obj
->address
, &obj
->addrnames
, &obj
->addrargs
,
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
1392 pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS
)
1394 ReturnSetInfo
*rsinfo
= (ReturnSetInfo
*) fcinfo
->resultinfo
;
1398 * Protect this function from being called out of context
1400 if (!currentEventTriggerState
||
1401 !currentEventTriggerState
->in_sql_drop
)
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
))
1414 Datum values
[12] = {0};
1415 bool nulls
[12] = {0};
1417 obj
= slist_container(SQLDropObject
, next
, iter
.cur
);
1420 values
[i
++] = ObjectIdGetDatum(obj
->address
.classId
);
1423 values
[i
++] = ObjectIdGetDatum(obj
->address
.objectId
);
1426 values
[i
++] = Int32GetDatum(obj
->address
.objectSubId
);
1429 values
[i
++] = BoolGetDatum(obj
->original
);
1432 values
[i
++] = BoolGetDatum(obj
->normal
);
1435 values
[i
++] = BoolGetDatum(obj
->istemp
);
1438 values
[i
++] = CStringGetTextDatum(obj
->objecttype
);
1441 if (obj
->schemaname
)
1442 values
[i
++] = CStringGetTextDatum(obj
->schemaname
);
1448 values
[i
++] = CStringGetTextDatum(obj
->objname
);
1452 /* object_identity */
1453 if (obj
->objidentity
)
1454 values
[i
++] = CStringGetTextDatum(obj
->objidentity
);
1458 /* address_names and address_args */
1461 values
[i
++] = PointerGetDatum(strlist_to_textarray(obj
->addrnames
));
1464 values
[i
++] = PointerGetDatum(strlist_to_textarray(obj
->addrargs
));
1466 values
[i
++] = PointerGetDatum(construct_empty_array(TEXTOID
));
1474 tuplestore_putvalues(rsinfo
->setResult
, rsinfo
->setDesc
,
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.
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
)
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.
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)
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
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.
1549 EventTriggerInhibitCommandCollection(void)
1551 if (!currentEventTriggerState
)
1554 currentEventTriggerState
->commandCollectionInhibited
= true;
1558 * Re-establish DDL command collection.
1561 EventTriggerUndoInhibitCommandCollection(void)
1563 if (!currentEventTriggerState
)
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
1583 EventTriggerCollectSimpleCommand(ObjectAddress address
,
1584 ObjectAddress secondaryObject
,
1587 MemoryContext oldcxt
;
1588 CollectedCommand
*command
;
1590 /* ignore if event trigger context not set, or collection disabled */
1591 if (!currentEventTriggerState
||
1592 currentEventTriggerState
->commandCollectionInhibited
)
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
,
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.
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
)
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.
1655 EventTriggerAlterTableRelid(Oid objectId
)
1657 if (!currentEventTriggerState
||
1658 currentEventTriggerState
->commandCollectionInhibited
)
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.
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
)
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.
1708 EventTriggerAlterTableEnd(void)
1710 CollectedCommand
*parent
;
1712 /* ignore if event trigger context not set, or collection disabled */
1713 if (!currentEventTriggerState
||
1714 currentEventTriggerState
->commandCollectionInhibited
)
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
);
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.
1746 EventTriggerCollectGrant(InternalGrant
*istmt
)
1748 MemoryContext oldcxt
;
1749 CollectedCommand
*command
;
1750 InternalGrant
*icopy
;
1753 /* ignore if event trigger context not set, or collection disabled */
1754 if (!currentEventTriggerState
||
1755 currentEventTriggerState
->commandCollectionInhibited
)
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
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
)
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
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
)
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
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
)
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
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
)
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
1920 pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS
)
1922 ReturnSetInfo
*rsinfo
= (ReturnSetInfo
*) fcinfo
->resultinfo
;
1926 * Protect this function from being called out of context
1928 if (!currentEventTriggerState
)
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
);
1941 bool nulls
[9] = {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
1955 if (cmd
->type
== SCT_Simple
&&
1956 !OidIsValid(cmd
->d
.simple
.address
.objectId
))
1962 case SCT_AlterTable
:
1963 case SCT_AlterOpFamily
:
1964 case SCT_CreateOpClass
:
1965 case SCT_AlterTSConfig
:
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
)
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
)
2016 catalog
= table_open(addr
.classId
, AccessShareLock
);
2017 objtup
= get_catalog_object_by_oid(catalog
,
2018 get_object_attnum_oid(addr
.classId
),
2020 if (!HeapTupleIsValid(objtup
))
2021 elog(ERROR
, "cache lookup failed for object %u/%u",
2022 addr
.classId
, addr
.objectId
);
2024 heap_getattr(objtup
, nspAttnum
,
2025 RelationGetDescr(catalog
), &isnull
);
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
);
2037 values
[i
++] = ObjectIdGetDatum(addr
.classId
);
2039 values
[i
++] = ObjectIdGetDatum(addr
.objectId
);
2041 values
[i
++] = Int32GetDatum(addr
.objectSubId
);
2043 values
[i
++] = CStringGetTextDatum(CreateCommandName(cmd
->parsetree
));
2045 values
[i
++] = CStringGetTextDatum(type
);
2050 values
[i
++] = CStringGetTextDatum(schema
);
2052 values
[i
++] = CStringGetTextDatum(identity
);
2054 values
[i
++] = BoolGetDatum(cmd
->in_extension
);
2056 values
[i
++] = PointerGetDatum(cmd
);
2060 case SCT_AlterDefaultPrivileges
:
2068 values
[i
++] = CStringGetTextDatum(CreateCommandName(cmd
->parsetree
));
2070 values
[i
++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd
->d
.defprivs
.objtype
));
2076 values
[i
++] = BoolGetDatum(cmd
->in_extension
);
2078 values
[i
++] = PointerGetDatum(cmd
);
2089 values
[i
++] = CStringGetTextDatum(cmd
->d
.grant
.istmt
->is_grant
?
2090 "GRANT" : "REVOKE");
2092 values
[i
++] = CStringGetTextDatum(stringify_grant_objtype(cmd
->d
.grant
.istmt
->objtype
));
2098 values
[i
++] = BoolGetDatum(cmd
->in_extension
);
2100 values
[i
++] = PointerGetDatum(cmd
);
2104 tuplestore_putvalues(rsinfo
->setResult
, rsinfo
->setDesc
,
2112 * Return the ObjectType as a string, as it would appear in GRANT and
2116 stringify_grant_objtype(ObjectType objtype
)
2124 case OBJECT_SEQUENCE
:
2126 case OBJECT_DATABASE
:
2131 return "FOREIGN DATA WRAPPER";
2132 case OBJECT_FOREIGN_SERVER
:
2133 return "FOREIGN SERVER";
2134 case OBJECT_FUNCTION
:
2136 case OBJECT_LANGUAGE
:
2138 case OBJECT_LARGEOBJECT
:
2139 return "LARGE OBJECT";
2142 case OBJECT_PARAMETER_ACL
:
2144 case OBJECT_PROCEDURE
:
2146 case OBJECT_ROUTINE
:
2148 case OBJECT_TABLESPACE
:
2149 return "TABLESPACE";
2152 /* these currently aren't used */
2153 case OBJECT_ACCESS_METHOD
:
2154 case OBJECT_AGGREGATE
:
2157 case OBJECT_ATTRIBUTE
:
2159 case OBJECT_COLLATION
:
2160 case OBJECT_CONVERSION
:
2161 case OBJECT_DEFAULT
:
2163 case OBJECT_DOMCONSTRAINT
:
2164 case OBJECT_EVENT_TRIGGER
:
2165 case OBJECT_EXTENSION
:
2166 case OBJECT_FOREIGN_TABLE
:
2168 case OBJECT_MATVIEW
:
2169 case OBJECT_OPCLASS
:
2170 case OBJECT_OPERATOR
:
2171 case OBJECT_OPFAMILY
:
2173 case OBJECT_PUBLICATION
:
2174 case OBJECT_PUBLICATION_NAMESPACE
:
2175 case OBJECT_PUBLICATION_REL
:
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
:
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
2201 stringify_adefprivs_objtype(ObjectType objtype
)
2209 case OBJECT_SEQUENCE
:
2211 case OBJECT_DATABASE
:
2216 return "FOREIGN DATA WRAPPERS";
2217 case OBJECT_FOREIGN_SERVER
:
2218 return "FOREIGN SERVERS";
2219 case OBJECT_FUNCTION
:
2221 case OBJECT_LANGUAGE
:
2223 case OBJECT_LARGEOBJECT
:
2224 return "LARGE OBJECTS";
2227 case OBJECT_PROCEDURE
:
2228 return "PROCEDURES";
2229 case OBJECT_ROUTINE
:
2231 case OBJECT_TABLESPACE
:
2232 return "TABLESPACES";
2235 /* these currently aren't used */
2236 case OBJECT_ACCESS_METHOD
:
2237 case OBJECT_AGGREGATE
:
2240 case OBJECT_ATTRIBUTE
:
2242 case OBJECT_COLLATION
:
2243 case OBJECT_CONVERSION
:
2244 case OBJECT_DEFAULT
:
2246 case OBJECT_DOMCONSTRAINT
:
2247 case OBJECT_EVENT_TRIGGER
:
2248 case OBJECT_EXTENSION
:
2249 case OBJECT_FOREIGN_TABLE
:
2251 case OBJECT_MATVIEW
:
2252 case OBJECT_OPCLASS
:
2253 case OBJECT_OPERATOR
:
2254 case OBJECT_OPFAMILY
:
2255 case OBJECT_PARAMETER_ACL
:
2257 case OBJECT_PUBLICATION
:
2258 case OBJECT_PUBLICATION_NAMESPACE
:
2259 case OBJECT_PUBLICATION_REL
:
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
:
2273 elog(ERROR
, "unsupported object type: %d", (int) objtype
);
2276 return "???"; /* keep compiler quiet */