Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * event_trigger.c
4 : * PostgreSQL EVENT TRIGGER support code.
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/commands/event_trigger.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/genam.h"
17 : #include "access/heapam.h"
18 : #include "access/htup_details.h"
19 : #include "access/table.h"
20 : #include "access/xact.h"
21 : #include "catalog/catalog.h"
22 : #include "catalog/dependency.h"
23 : #include "catalog/indexing.h"
24 : #include "catalog/objectaccess.h"
25 : #include "catalog/pg_attrdef.h"
26 : #include "catalog/pg_authid.h"
27 : #include "catalog/pg_auth_members.h"
28 : #include "catalog/pg_database.h"
29 : #include "catalog/pg_event_trigger.h"
30 : #include "catalog/pg_namespace.h"
31 : #include "catalog/pg_opclass.h"
32 : #include "catalog/pg_opfamily.h"
33 : #include "catalog/pg_parameter_acl.h"
34 : #include "catalog/pg_policy.h"
35 : #include "catalog/pg_proc.h"
36 : #include "catalog/pg_tablespace.h"
37 : #include "catalog/pg_trigger.h"
38 : #include "catalog/pg_ts_config.h"
39 : #include "catalog/pg_type.h"
40 : #include "commands/event_trigger.h"
41 : #include "commands/extension.h"
42 : #include "commands/trigger.h"
43 : #include "funcapi.h"
44 : #include "lib/ilist.h"
45 : #include "miscadmin.h"
46 : #include "parser/parse_func.h"
47 : #include "pgstat.h"
48 : #include "storage/lmgr.h"
49 : #include "tcop/deparse_utility.h"
50 : #include "tcop/utility.h"
51 : #include "utils/acl.h"
52 : #include "utils/builtins.h"
53 : #include "utils/evtcache.h"
54 : #include "utils/fmgroids.h"
55 : #include "utils/fmgrprotos.h"
56 : #include "utils/lsyscache.h"
57 : #include "utils/memutils.h"
58 : #include "utils/rel.h"
59 : #include "utils/snapmgr.h"
60 : #include "utils/syscache.h"
61 : #include "utils/tuplestore.h"
62 :
63 : typedef struct EventTriggerQueryState
64 : {
65 : /* memory context for this state's objects */
66 : MemoryContext cxt;
67 :
68 : /* sql_drop */
69 : slist_head SQLDropList;
70 : bool in_sql_drop;
71 :
72 : /* table_rewrite */
73 : Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite
74 : * event */
75 : int table_rewrite_reason; /* AT_REWRITE reason */
76 :
77 : /* Support for command collection */
78 : bool commandCollectionInhibited;
79 : CollectedCommand *currentCommand;
80 : List *commandList; /* list of CollectedCommand; see
81 : * deparse_utility.h */
82 : struct EventTriggerQueryState *previous;
83 : } EventTriggerQueryState;
84 :
85 : static EventTriggerQueryState *currentEventTriggerState = NULL;
86 :
87 : /* GUC parameter */
88 : bool event_triggers = true;
89 :
90 : /* Support for dropped objects */
91 : typedef struct SQLDropObject
92 : {
93 : ObjectAddress address;
94 : const char *schemaname;
95 : const char *objname;
96 : const char *objidentity;
97 : const char *objecttype;
98 : List *addrnames;
99 : List *addrargs;
100 : bool original;
101 : bool normal;
102 : bool istemp;
103 : slist_node next;
104 : } SQLDropObject;
105 :
106 : static void AlterEventTriggerOwner_internal(Relation rel,
107 : HeapTuple tup,
108 : Oid newOwnerId);
109 : static void error_duplicate_filter_variable(const char *defname);
110 : static Datum filter_list_to_array(List *filterlist);
111 : static Oid insert_event_trigger_tuple(const char *trigname, const char *eventname,
112 : Oid evtOwner, Oid funcoid, List *taglist);
113 : static void validate_ddl_tags(const char *filtervar, List *taglist);
114 : static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
115 : static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
116 : static bool obtain_object_name_namespace(const ObjectAddress *object,
117 : SQLDropObject *obj);
118 : static const char *stringify_grant_objtype(ObjectType objtype);
119 : static const char *stringify_adefprivs_objtype(ObjectType objtype);
120 : static void SetDatabaseHasLoginEventTriggers(void);
121 :
122 : /*
123 : * Create an event trigger.
124 : */
125 : Oid
126 129 : CreateEventTrigger(CreateEventTrigStmt *stmt)
127 : {
128 : HeapTuple tuple;
129 : Oid funcoid;
130 : Oid funcrettype;
131 129 : Oid evtowner = GetUserId();
132 : ListCell *lc;
133 129 : List *tags = NULL;
134 :
135 : /*
136 : * It would be nice to allow database owners or even regular users to do
137 : * this, but there are obvious privilege escalation risks which would have
138 : * to somehow be plugged first.
139 : */
140 129 : if (!superuser())
141 4 : ereport(ERROR,
142 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
143 : errmsg("permission denied to create event trigger \"%s\"",
144 : stmt->trigname),
145 : errhint("Must be superuser to create an event trigger.")));
146 :
147 : /* Validate event name. */
148 125 : if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
149 68 : strcmp(stmt->eventname, "ddl_command_end") != 0 &&
150 43 : strcmp(stmt->eventname, "sql_drop") != 0 &&
151 21 : strcmp(stmt->eventname, "login") != 0 &&
152 14 : strcmp(stmt->eventname, "table_rewrite") != 0)
153 4 : ereport(ERROR,
154 : (errcode(ERRCODE_SYNTAX_ERROR),
155 : errmsg("unrecognized event name \"%s\"",
156 : stmt->eventname)));
157 :
158 : /* Validate filter conditions. */
159 182 : foreach(lc, stmt->whenclause)
160 : {
161 69 : DefElem *def = (DefElem *) lfirst(lc);
162 :
163 69 : if (strcmp(def->defname, "tag") == 0)
164 : {
165 65 : if (tags != NULL)
166 4 : error_duplicate_filter_variable(def->defname);
167 61 : tags = (List *) def->arg;
168 : }
169 : else
170 4 : ereport(ERROR,
171 : (errcode(ERRCODE_SYNTAX_ERROR),
172 : errmsg("unrecognized filter variable \"%s\"", def->defname)));
173 : }
174 :
175 : /* Validate tag list, if any. */
176 113 : if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
177 64 : strcmp(stmt->eventname, "ddl_command_end") == 0 ||
178 39 : strcmp(stmt->eventname, "sql_drop") == 0)
179 96 : && tags != NULL)
180 57 : validate_ddl_tags("tag", tags);
181 56 : else if (strcmp(stmt->eventname, "table_rewrite") == 0
182 10 : && tags != NULL)
183 0 : validate_table_rewrite_tags("tag", tags);
184 56 : else if (strcmp(stmt->eventname, "login") == 0 && tags != NULL)
185 0 : ereport(ERROR,
186 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
187 : errmsg("tag filtering is not supported for login event triggers")));
188 :
189 : /*
190 : * Give user a nice error message if an event trigger of the same name
191 : * already exists.
192 : */
193 89 : tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
194 89 : if (HeapTupleIsValid(tuple))
195 0 : ereport(ERROR,
196 : (errcode(ERRCODE_DUPLICATE_OBJECT),
197 : errmsg("event trigger \"%s\" already exists",
198 : stmt->trigname)));
199 :
200 : /* Find and validate the trigger function. */
201 89 : funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
202 89 : funcrettype = get_func_rettype(funcoid);
203 89 : if (funcrettype != EVENT_TRIGGEROID)
204 4 : ereport(ERROR,
205 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
206 : errmsg("function %s must return type %s",
207 : NameListToString(stmt->funcname), "event_trigger")));
208 :
209 : /* Insert catalog entries. */
210 85 : return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
211 : evtowner, funcoid, tags);
212 : }
213 :
214 : /*
215 : * Validate DDL command tags.
216 : */
217 : static void
218 57 : validate_ddl_tags(const char *filtervar, List *taglist)
219 : {
220 : ListCell *lc;
221 :
222 134 : foreach(lc, taglist)
223 : {
224 101 : const char *tagstr = strVal(lfirst(lc));
225 101 : CommandTag commandTag = GetCommandTagEnum(tagstr);
226 :
227 101 : if (commandTag == CMDTAG_UNKNOWN)
228 8 : ereport(ERROR,
229 : (errcode(ERRCODE_SYNTAX_ERROR),
230 : errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
231 : tagstr, filtervar)));
232 93 : if (!command_tag_event_trigger_ok(commandTag))
233 16 : ereport(ERROR,
234 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
235 : /* translator: %s represents an SQL statement name */
236 : errmsg("event triggers are not supported for %s",
237 : tagstr)));
238 : }
239 33 : }
240 :
241 : /*
242 : * Validate DDL command tags for event table_rewrite.
243 : */
244 : static void
245 0 : validate_table_rewrite_tags(const char *filtervar, List *taglist)
246 : {
247 : ListCell *lc;
248 :
249 0 : foreach(lc, taglist)
250 : {
251 0 : const char *tagstr = strVal(lfirst(lc));
252 0 : CommandTag commandTag = GetCommandTagEnum(tagstr);
253 :
254 0 : if (!command_tag_table_rewrite_ok(commandTag))
255 0 : ereport(ERROR,
256 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
257 : /* translator: %s represents an SQL statement name */
258 : errmsg("event triggers are not supported for %s",
259 : tagstr)));
260 : }
261 0 : }
262 :
263 : /*
264 : * Complain about a duplicate filter variable.
265 : */
266 : static void
267 4 : error_duplicate_filter_variable(const char *defname)
268 : {
269 4 : ereport(ERROR,
270 : (errcode(ERRCODE_SYNTAX_ERROR),
271 : errmsg("filter variable \"%s\" specified more than once",
272 : defname)));
273 : }
274 :
275 : /*
276 : * Insert the new pg_event_trigger row and record dependencies.
277 : */
278 : static Oid
279 85 : insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtOwner,
280 : Oid funcoid, List *taglist)
281 : {
282 : Relation tgrel;
283 : Oid trigoid;
284 : HeapTuple tuple;
285 : Datum values[Natts_pg_event_trigger];
286 : bool nulls[Natts_pg_event_trigger];
287 : NameData evtnamedata,
288 : evteventdata;
289 : ObjectAddress myself,
290 : referenced;
291 :
292 : /* Open pg_event_trigger. */
293 85 : tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
294 :
295 : /* Build the new pg_trigger tuple. */
296 85 : trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
297 : Anum_pg_event_trigger_oid);
298 85 : values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
299 85 : memset(nulls, false, sizeof(nulls));
300 85 : namestrcpy(&evtnamedata, trigname);
301 85 : values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
302 85 : namestrcpy(&evteventdata, eventname);
303 85 : values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
304 85 : values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
305 85 : values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
306 85 : values[Anum_pg_event_trigger_evtenabled - 1] =
307 85 : CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
308 85 : if (taglist == NIL)
309 52 : nulls[Anum_pg_event_trigger_evttags - 1] = true;
310 : else
311 33 : values[Anum_pg_event_trigger_evttags - 1] =
312 33 : filter_list_to_array(taglist);
313 :
314 : /* Insert heap tuple. */
315 85 : tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
316 85 : CatalogTupleInsert(tgrel, tuple);
317 85 : heap_freetuple(tuple);
318 :
319 : /*
320 : * Login event triggers have an additional flag in pg_database to enable
321 : * faster lookups in hot codepaths. Set the flag unless already True.
322 : */
323 85 : if (strcmp(eventname, "login") == 0)
324 7 : SetDatabaseHasLoginEventTriggers();
325 :
326 : /* Depend on owner. */
327 85 : recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
328 :
329 : /* Depend on event trigger function. */
330 85 : myself.classId = EventTriggerRelationId;
331 85 : myself.objectId = trigoid;
332 85 : myself.objectSubId = 0;
333 85 : referenced.classId = ProcedureRelationId;
334 85 : referenced.objectId = funcoid;
335 85 : referenced.objectSubId = 0;
336 85 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
337 :
338 : /* Depend on extension, if any. */
339 85 : recordDependencyOnCurrentExtension(&myself, false);
340 :
341 : /* Post creation hook for new event trigger */
342 85 : InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
343 :
344 : /* Close pg_event_trigger. */
345 85 : table_close(tgrel, RowExclusiveLock);
346 :
347 85 : return trigoid;
348 : }
349 :
350 : /*
351 : * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented
352 : * by a DefElem whose value is a List of String nodes; in the catalog, we
353 : * store the list of strings as a text array. This function transforms the
354 : * former representation into the latter one.
355 : *
356 : * For cleanliness, we store command tags in the catalog as text. It's
357 : * possible (although not currently anticipated) that we might have
358 : * a case-sensitive filter variable in the future, in which case this would
359 : * need some further adjustment.
360 : */
361 : static Datum
362 33 : filter_list_to_array(List *filterlist)
363 : {
364 : ListCell *lc;
365 : Datum *data;
366 33 : int i = 0,
367 33 : l = list_length(filterlist);
368 :
369 33 : data = palloc_array(Datum, l);
370 :
371 106 : foreach(lc, filterlist)
372 : {
373 73 : const char *value = strVal(lfirst(lc));
374 : char *result,
375 : *p;
376 :
377 73 : result = pstrdup(value);
378 880 : for (p = result; *p; p++)
379 807 : *p = pg_ascii_toupper((unsigned char) *p);
380 73 : data[i++] = PointerGetDatum(cstring_to_text(result));
381 73 : pfree(result);
382 : }
383 :
384 33 : return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
385 : }
386 :
387 : /*
388 : * Set pg_database.dathasloginevt flag for current database indicating that
389 : * current database has on login event triggers.
390 : */
391 : void
392 14 : SetDatabaseHasLoginEventTriggers(void)
393 : {
394 : /* Set dathasloginevt flag in pg_database */
395 : Form_pg_database db;
396 14 : Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
397 : ItemPointerData otid;
398 : HeapTuple tuple;
399 :
400 : /*
401 : * Use shared lock to prevent a conflict with EventTriggerOnLogin() trying
402 : * to reset pg_database.dathasloginevt flag. Note, this lock doesn't
403 : * effectively blocks database or other objection. It's just custom lock
404 : * tag used to prevent multiple backends changing
405 : * pg_database.dathasloginevt flag.
406 : */
407 14 : LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
408 :
409 14 : tuple = SearchSysCacheLockedCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
410 14 : if (!HeapTupleIsValid(tuple))
411 0 : elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
412 14 : otid = tuple->t_self;
413 14 : db = (Form_pg_database) GETSTRUCT(tuple);
414 14 : if (!db->dathasloginevt)
415 : {
416 7 : db->dathasloginevt = true;
417 7 : CatalogTupleUpdate(pg_db, &otid, tuple);
418 7 : CommandCounterIncrement();
419 : }
420 14 : UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
421 14 : table_close(pg_db, RowExclusiveLock);
422 14 : heap_freetuple(tuple);
423 14 : }
424 :
425 : /*
426 : * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
427 : */
428 : Oid
429 32 : AlterEventTrigger(AlterEventTrigStmt *stmt)
430 : {
431 : Relation tgrel;
432 : HeapTuple tup;
433 : Oid trigoid;
434 : Form_pg_event_trigger evtForm;
435 32 : char tgenabled = stmt->tgenabled;
436 :
437 32 : tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
438 :
439 32 : tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
440 : CStringGetDatum(stmt->trigname));
441 32 : if (!HeapTupleIsValid(tup))
442 0 : ereport(ERROR,
443 : (errcode(ERRCODE_UNDEFINED_OBJECT),
444 : errmsg("event trigger \"%s\" does not exist",
445 : stmt->trigname)));
446 :
447 32 : evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
448 32 : trigoid = evtForm->oid;
449 :
450 32 : if (!object_ownercheck(EventTriggerRelationId, trigoid, GetUserId()))
451 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
452 0 : stmt->trigname);
453 :
454 : /* tuple is a copy, so we can modify it below */
455 32 : evtForm->evtenabled = tgenabled;
456 :
457 32 : CatalogTupleUpdate(tgrel, &tup->t_self, tup);
458 :
459 : /*
460 : * Login event triggers have an additional flag in pg_database to enable
461 : * faster lookups in hot codepaths. Set the flag unless already True.
462 : */
463 32 : if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
464 : tgenabled != TRIGGER_DISABLED)
465 7 : SetDatabaseHasLoginEventTriggers();
466 :
467 32 : InvokeObjectPostAlterHook(EventTriggerRelationId,
468 : trigoid, 0);
469 :
470 : /* clean up */
471 32 : heap_freetuple(tup);
472 32 : table_close(tgrel, RowExclusiveLock);
473 :
474 32 : return trigoid;
475 : }
476 :
477 : /*
478 : * Change event trigger's owner -- by name
479 : */
480 : ObjectAddress
481 9 : AlterEventTriggerOwner(const char *name, Oid newOwnerId)
482 : {
483 : Oid evtOid;
484 : HeapTuple tup;
485 : Form_pg_event_trigger evtForm;
486 : Relation rel;
487 : ObjectAddress address;
488 :
489 9 : rel = table_open(EventTriggerRelationId, RowExclusiveLock);
490 :
491 9 : tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
492 :
493 9 : if (!HeapTupleIsValid(tup))
494 0 : ereport(ERROR,
495 : (errcode(ERRCODE_UNDEFINED_OBJECT),
496 : errmsg("event trigger \"%s\" does not exist", name)));
497 :
498 9 : evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
499 9 : evtOid = evtForm->oid;
500 :
501 9 : AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
502 :
503 5 : ObjectAddressSet(address, EventTriggerRelationId, evtOid);
504 :
505 5 : heap_freetuple(tup);
506 :
507 5 : table_close(rel, RowExclusiveLock);
508 :
509 5 : return address;
510 : }
511 :
512 : /*
513 : * Change event trigger owner, by OID
514 : */
515 : void
516 0 : AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId)
517 : {
518 : HeapTuple tup;
519 : Relation rel;
520 :
521 0 : rel = table_open(EventTriggerRelationId, RowExclusiveLock);
522 :
523 0 : tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
524 :
525 0 : if (!HeapTupleIsValid(tup))
526 0 : ereport(ERROR,
527 : (errcode(ERRCODE_UNDEFINED_OBJECT),
528 : errmsg("event trigger with OID %u does not exist", trigOid)));
529 :
530 0 : AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
531 :
532 0 : heap_freetuple(tup);
533 :
534 0 : table_close(rel, RowExclusiveLock);
535 0 : }
536 :
537 : /*
538 : * Internal workhorse for changing an event trigger's owner
539 : */
540 : static void
541 9 : AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
542 : {
543 : Form_pg_event_trigger form;
544 :
545 9 : form = (Form_pg_event_trigger) GETSTRUCT(tup);
546 :
547 9 : if (form->evtowner == newOwnerId)
548 1 : return;
549 :
550 8 : if (!object_ownercheck(EventTriggerRelationId, form->oid, GetUserId()))
551 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_EVENT_TRIGGER,
552 0 : NameStr(form->evtname));
553 :
554 : /* New owner must be a superuser */
555 8 : if (!superuser_arg(newOwnerId))
556 4 : ereport(ERROR,
557 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
558 : errmsg("permission denied to change owner of event trigger \"%s\"",
559 : NameStr(form->evtname)),
560 : errhint("The owner of an event trigger must be a superuser.")));
561 :
562 4 : form->evtowner = newOwnerId;
563 4 : CatalogTupleUpdate(rel, &tup->t_self, tup);
564 :
565 : /* Update owner dependency reference */
566 4 : changeDependencyOnOwner(EventTriggerRelationId,
567 : form->oid,
568 : newOwnerId);
569 :
570 4 : InvokeObjectPostAlterHook(EventTriggerRelationId,
571 : form->oid, 0);
572 : }
573 :
574 : /*
575 : * get_event_trigger_oid - Look up an event trigger by name to find its OID.
576 : *
577 : * If missing_ok is false, throw an error if trigger not found. If
578 : * true, just return InvalidOid.
579 : */
580 : Oid
581 112 : get_event_trigger_oid(const char *trigname, bool missing_ok)
582 : {
583 : Oid oid;
584 :
585 112 : oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
586 : CStringGetDatum(trigname));
587 112 : if (!OidIsValid(oid) && !missing_ok)
588 8 : ereport(ERROR,
589 : (errcode(ERRCODE_UNDEFINED_OBJECT),
590 : errmsg("event trigger \"%s\" does not exist", trigname)));
591 104 : return oid;
592 : }
593 :
594 : /*
595 : * Return true when we want to fire given Event Trigger and false otherwise,
596 : * filtering on the session replication role and the event trigger registered
597 : * tags matching.
598 : */
599 : static bool
600 1583 : filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
601 : {
602 : /*
603 : * Filter by session replication role, knowing that we never see disabled
604 : * items down here.
605 : */
606 1583 : if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
607 : {
608 36 : if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
609 28 : return false;
610 : }
611 : else
612 : {
613 1547 : if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
614 0 : return false;
615 : }
616 :
617 : /* Filter by tags, if any were specified. */
618 1555 : if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
619 336 : return false;
620 :
621 : /* if we reach that point, we're not filtering out this item */
622 1219 : return true;
623 : }
624 :
625 : static CommandTag
626 1383 : EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
627 : {
628 1383 : if (event == EVT_Login)
629 139 : return CMDTAG_LOGIN;
630 : else
631 1244 : return CreateCommandTag(parsetree);
632 : }
633 :
634 : /*
635 : * Setup for running triggers for the given event. Return value is an OID list
636 : * of functions to run; if there are any, trigdata is filled with an
637 : * appropriate EventTriggerData for them to receive.
638 : */
639 : static List *
640 105547 : EventTriggerCommonSetup(Node *parsetree,
641 : EventTriggerEvent event, const char *eventstr,
642 : EventTriggerData *trigdata, bool unfiltered)
643 : {
644 : CommandTag tag;
645 : List *cachelist;
646 : ListCell *lc;
647 105547 : List *runlist = NIL;
648 :
649 : /*
650 : * We want the list of command tags for which this procedure is actually
651 : * invoked to match up exactly with the list that CREATE EVENT TRIGGER
652 : * accepts. This debugging cross-check will throw an error if this
653 : * function is invoked for a command tag that CREATE EVENT TRIGGER won't
654 : * accept. (Unfortunately, there doesn't seem to be any simple, automated
655 : * way to verify that CREATE EVENT TRIGGER doesn't accept extra stuff that
656 : * never reaches this control point.)
657 : *
658 : * If this cross-check fails for you, you probably need to either adjust
659 : * standard_ProcessUtility() not to invoke event triggers for the command
660 : * type in question, or you need to adjust event_trigger_ok to accept the
661 : * relevant command tag.
662 : */
663 : #ifdef USE_ASSERT_CHECKING
664 : {
665 : CommandTag dbgtag;
666 :
667 : dbgtag = EventTriggerGetTag(parsetree, event);
668 :
669 : if (event == EVT_DDLCommandStart ||
670 : event == EVT_DDLCommandEnd ||
671 : event == EVT_SQLDrop ||
672 : event == EVT_Login)
673 : {
674 : if (!command_tag_event_trigger_ok(dbgtag))
675 : elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
676 : }
677 : else if (event == EVT_TableRewrite)
678 : {
679 : if (!command_tag_table_rewrite_ok(dbgtag))
680 : elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
681 : }
682 : }
683 : #endif
684 :
685 : /* Use cache to find triggers for this event; fast exit if none. */
686 105547 : cachelist = EventCacheLookup(event);
687 105547 : if (cachelist == NIL)
688 104164 : return NIL;
689 :
690 : /* Get the command tag. */
691 1383 : tag = EventTriggerGetTag(parsetree, event);
692 :
693 : /*
694 : * Filter list of event triggers by command tag, and copy them into our
695 : * memory context. Once we start running the command triggers, or indeed
696 : * once we do anything at all that touches the catalogs, an invalidation
697 : * might leave cachelist pointing at garbage, so we must do this before we
698 : * can do much else.
699 : */
700 2966 : foreach(lc, cachelist)
701 : {
702 1583 : EventTriggerCacheItem *item = lfirst(lc);
703 :
704 1583 : if (unfiltered || filter_event_trigger(tag, item))
705 : {
706 : /* We must plan to fire this trigger. */
707 1219 : runlist = lappend_oid(runlist, item->fnoid);
708 : }
709 : }
710 :
711 : /* Don't spend any more time on this if no functions to run */
712 1383 : if (runlist == NIL)
713 300 : return NIL;
714 :
715 1083 : trigdata->type = T_EventTriggerData;
716 1083 : trigdata->event = eventstr;
717 1083 : trigdata->parsetree = parsetree;
718 1083 : trigdata->tag = tag;
719 :
720 1083 : return runlist;
721 : }
722 :
723 : /*
724 : * Fire ddl_command_start triggers.
725 : */
726 : void
727 140300 : EventTriggerDDLCommandStart(Node *parsetree)
728 : {
729 : List *runlist;
730 : EventTriggerData trigdata;
731 :
732 : /*
733 : * Event Triggers are completely disabled in standalone mode. There are
734 : * (at least) two reasons for this:
735 : *
736 : * 1. A sufficiently broken event trigger might not only render the
737 : * database unusable, but prevent disabling itself to fix the situation.
738 : * In this scenario, restarting in standalone mode provides an escape
739 : * hatch.
740 : *
741 : * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
742 : * therefore will malfunction if pg_event_trigger's indexes are damaged.
743 : * To allow recovery from a damaged index, we need some operating mode
744 : * wherein event triggers are disabled. (Or we could implement
745 : * heapscan-and-sort logic for that case, but having disaster recovery
746 : * scenarios depend on code that's otherwise untested isn't appetizing.)
747 : *
748 : * Additionally, event triggers can be disabled with a superuser-only GUC
749 : * to make fixing database easier as per 1 above.
750 : */
751 140300 : if (!IsUnderPostmaster || !event_triggers)
752 140064 : return;
753 :
754 103116 : runlist = EventTriggerCommonSetup(parsetree,
755 : EVT_DDLCommandStart,
756 : "ddl_command_start",
757 : &trigdata, false);
758 103116 : if (runlist == NIL)
759 102880 : return;
760 :
761 : /* Run the triggers. */
762 236 : EventTriggerInvoke(runlist, &trigdata);
763 :
764 : /* Cleanup. */
765 236 : list_free(runlist);
766 :
767 : /*
768 : * Make sure anything the event triggers did will be visible to the main
769 : * command.
770 : */
771 236 : CommandCounterIncrement();
772 : }
773 :
774 : /*
775 : * Fire ddl_command_end triggers.
776 : */
777 : void
778 131200 : EventTriggerDDLCommandEnd(Node *parsetree)
779 : {
780 : List *runlist;
781 : EventTriggerData trigdata;
782 :
783 : /*
784 : * See EventTriggerDDLCommandStart for a discussion about why event
785 : * triggers are disabled in single user mode or via GUC.
786 : */
787 131200 : if (!IsUnderPostmaster || !event_triggers)
788 130632 : return;
789 :
790 : /*
791 : * Also do nothing if our state isn't set up, which it won't be if there
792 : * weren't any relevant event triggers at the start of the current DDL
793 : * command. This test might therefore seem optional, but it's important
794 : * because EventTriggerCommonSetup might find triggers that didn't exist
795 : * at the time the command started. Although this function itself
796 : * wouldn't crash, the event trigger functions would presumably call
797 : * pg_event_trigger_ddl_commands which would fail. Better to do nothing
798 : * until the next command.
799 : */
800 94016 : if (!currentEventTriggerState)
801 92202 : return;
802 :
803 1814 : runlist = EventTriggerCommonSetup(parsetree,
804 : EVT_DDLCommandEnd, "ddl_command_end",
805 : &trigdata, false);
806 1814 : if (runlist == NIL)
807 1246 : return;
808 :
809 : /*
810 : * Make sure anything the main command did will be visible to the event
811 : * triggers.
812 : */
813 568 : CommandCounterIncrement();
814 :
815 : /* Run the triggers. */
816 568 : EventTriggerInvoke(runlist, &trigdata);
817 :
818 : /* Cleanup. */
819 568 : list_free(runlist);
820 : }
821 :
822 : /*
823 : * Fire sql_drop triggers.
824 : */
825 : void
826 131212 : EventTriggerSQLDrop(Node *parsetree)
827 : {
828 : List *runlist;
829 : EventTriggerData trigdata;
830 :
831 : /*
832 : * See EventTriggerDDLCommandStart for a discussion about why event
833 : * triggers are disabled in single user mode or via a GUC.
834 : */
835 131212 : if (!IsUnderPostmaster || !event_triggers)
836 131128 : return;
837 :
838 : /*
839 : * Use current state to determine whether this event fires at all. If
840 : * there are no triggers for the sql_drop event, then we don't have
841 : * anything to do here. Note that dropped object collection is disabled
842 : * if this is the case, so even if we were to try to run, the list would
843 : * be empty.
844 : */
845 94028 : if (!currentEventTriggerState ||
846 1826 : slist_is_empty(¤tEventTriggerState->SQLDropList))
847 93658 : return;
848 :
849 370 : runlist = EventTriggerCommonSetup(parsetree,
850 : EVT_SQLDrop, "sql_drop",
851 : &trigdata, false);
852 :
853 : /*
854 : * Nothing to do if run list is empty. Note this typically can't happen,
855 : * because if there are no sql_drop events, then objects-to-drop wouldn't
856 : * have been collected in the first place and we would have quit above.
857 : * But it could occur if event triggers were dropped partway through.
858 : */
859 370 : if (runlist == NIL)
860 286 : return;
861 :
862 : /*
863 : * Make sure anything the main command did will be visible to the event
864 : * triggers.
865 : */
866 84 : CommandCounterIncrement();
867 :
868 : /*
869 : * Make sure pg_event_trigger_dropped_objects only works when running
870 : * these triggers. Use PG_TRY to ensure in_sql_drop is reset even when
871 : * one trigger fails. (This is perhaps not necessary, as the currentState
872 : * variable will be removed shortly by our caller, but it seems better to
873 : * play safe.)
874 : */
875 84 : currentEventTriggerState->in_sql_drop = true;
876 :
877 : /* Run the triggers. */
878 84 : PG_TRY();
879 : {
880 84 : EventTriggerInvoke(runlist, &trigdata);
881 : }
882 12 : PG_FINALLY();
883 : {
884 84 : currentEventTriggerState->in_sql_drop = false;
885 : }
886 84 : PG_END_TRY();
887 :
888 : /* Cleanup. */
889 72 : list_free(runlist);
890 : }
891 :
892 : /*
893 : * Fire login event triggers if any are present. The dathasloginevt
894 : * pg_database flag is left unchanged when an event trigger is dropped to avoid
895 : * complicating the codepath in the case of multiple event triggers. This
896 : * function will instead unset the flag if no trigger is defined.
897 : */
898 : void
899 14794 : EventTriggerOnLogin(void)
900 : {
901 : List *runlist;
902 : EventTriggerData trigdata;
903 :
904 : /*
905 : * See EventTriggerDDLCommandStart for a discussion about why event
906 : * triggers are disabled in single user mode or via a GUC. We also need a
907 : * database connection (some background workers don't have it).
908 : */
909 14794 : if (!IsUnderPostmaster || !event_triggers ||
910 14720 : !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
911 14649 : return;
912 :
913 145 : StartTransactionCommand();
914 145 : runlist = EventTriggerCommonSetup(NULL,
915 : EVT_Login, "login",
916 : &trigdata, false);
917 :
918 145 : if (runlist != NIL)
919 : {
920 : /*
921 : * Event trigger execution may require an active snapshot.
922 : */
923 139 : PushActiveSnapshot(GetTransactionSnapshot());
924 :
925 : /* Run the triggers. */
926 139 : EventTriggerInvoke(runlist, &trigdata);
927 :
928 : /* Cleanup. */
929 139 : list_free(runlist);
930 :
931 139 : PopActiveSnapshot();
932 : }
933 :
934 : /*
935 : * There is no active login event trigger, but our
936 : * pg_database.dathasloginevt is set. Try to unset this flag. We use the
937 : * lock to prevent concurrent SetDatabaseHasLoginEventTriggers(), but we
938 : * don't want to hang the connection waiting on the lock. Thus, we are
939 : * just trying to acquire the lock conditionally.
940 : *
941 : * Skip this on a hot standby: the conditional AccessExclusiveLock on the
942 : * database object would fail with "cannot acquire lock mode ... while
943 : * recovery is in progress", which the caller would surface as a FATAL
944 : * connection error. On a standby, we cannot (and must not) clear the
945 : * pg_database flag ourselves; it will be cleared via WAL replay once the
946 : * primary's next login event trigger run clears it on the primary.
947 : */
948 11 : else if (!RecoveryInProgress() &&
949 5 : ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
950 : 0, AccessExclusiveLock))
951 : {
952 : /*
953 : * The lock is held. Now we need to recheck that login event triggers
954 : * list is still empty. Once the list is empty, we know that even if
955 : * there is a backend which concurrently inserts/enables a login event
956 : * trigger, it will update pg_database.dathasloginevt *afterwards*.
957 : */
958 5 : runlist = EventTriggerCommonSetup(NULL,
959 : EVT_Login, "login",
960 : &trigdata, true);
961 :
962 5 : if (runlist == NIL)
963 : {
964 5 : Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
965 : HeapTuple tuple;
966 : void *state;
967 : Form_pg_database db;
968 : ScanKeyData key[1];
969 :
970 : /* Fetch a copy of the tuple to scribble on */
971 5 : ScanKeyInit(&key[0],
972 : Anum_pg_database_oid,
973 : BTEqualStrategyNumber, F_OIDEQ,
974 : ObjectIdGetDatum(MyDatabaseId));
975 :
976 5 : systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
977 : NULL, 1, key, &tuple, &state);
978 :
979 5 : if (!HeapTupleIsValid(tuple))
980 0 : elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
981 :
982 5 : db = (Form_pg_database) GETSTRUCT(tuple);
983 5 : if (db->dathasloginevt)
984 : {
985 5 : db->dathasloginevt = false;
986 :
987 : /*
988 : * Do an "in place" update of the pg_database tuple. Doing
989 : * this instead of regular updates serves two purposes. First,
990 : * that avoids possible waiting on the row-level lock. Second,
991 : * that avoids dealing with TOAST.
992 : */
993 5 : systable_inplace_update_finish(state, tuple);
994 : }
995 : else
996 0 : systable_inplace_update_cancel(state);
997 5 : table_close(pg_db, RowExclusiveLock);
998 5 : heap_freetuple(tuple);
999 : }
1000 : else
1001 : {
1002 0 : list_free(runlist);
1003 : }
1004 : }
1005 145 : CommitTransactionCommand();
1006 : }
1007 :
1008 :
1009 : /*
1010 : * Fire table_rewrite triggers.
1011 : */
1012 : void
1013 773 : EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
1014 : {
1015 : List *runlist;
1016 : EventTriggerData trigdata;
1017 :
1018 : /*
1019 : * See EventTriggerDDLCommandStart for a discussion about why event
1020 : * triggers are disabled in single user mode or via a GUC.
1021 : */
1022 773 : if (!IsUnderPostmaster || !event_triggers)
1023 717 : return;
1024 :
1025 : /*
1026 : * Also do nothing if our state isn't set up, which it won't be if there
1027 : * weren't any relevant event triggers at the start of the current DDL
1028 : * command. This test might therefore seem optional, but it's
1029 : * *necessary*, because EventTriggerCommonSetup might find triggers that
1030 : * didn't exist at the time the command started.
1031 : */
1032 773 : if (!currentEventTriggerState)
1033 676 : return;
1034 :
1035 97 : runlist = EventTriggerCommonSetup(parsetree,
1036 : EVT_TableRewrite,
1037 : "table_rewrite",
1038 : &trigdata, false);
1039 97 : if (runlist == NIL)
1040 41 : return;
1041 :
1042 : /*
1043 : * Make sure pg_event_trigger_table_rewrite_oid only works when running
1044 : * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
1045 : * when one trigger fails. (This is perhaps not necessary, as the
1046 : * currentState variable will be removed shortly by our caller, but it
1047 : * seems better to play safe.)
1048 : */
1049 56 : currentEventTriggerState->table_rewrite_oid = tableOid;
1050 56 : currentEventTriggerState->table_rewrite_reason = reason;
1051 :
1052 : /* Run the triggers. */
1053 56 : PG_TRY();
1054 : {
1055 56 : EventTriggerInvoke(runlist, &trigdata);
1056 : }
1057 4 : PG_FINALLY();
1058 : {
1059 56 : currentEventTriggerState->table_rewrite_oid = InvalidOid;
1060 56 : currentEventTriggerState->table_rewrite_reason = 0;
1061 : }
1062 56 : PG_END_TRY();
1063 :
1064 : /* Cleanup. */
1065 52 : list_free(runlist);
1066 :
1067 : /*
1068 : * Make sure anything the event triggers did will be visible to the main
1069 : * command.
1070 : */
1071 52 : CommandCounterIncrement();
1072 : }
1073 :
1074 : /*
1075 : * Invoke each event trigger in a list of event triggers.
1076 : */
1077 : static void
1078 1083 : EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
1079 : {
1080 : MemoryContext context;
1081 : MemoryContext oldcontext;
1082 : ListCell *lc;
1083 1083 : bool first = true;
1084 :
1085 : /* Guard against stack overflow due to recursive event trigger */
1086 1083 : check_stack_depth();
1087 :
1088 : /*
1089 : * Let's evaluate event triggers in their own memory context, so that any
1090 : * leaks get cleaned up promptly.
1091 : */
1092 1083 : context = AllocSetContextCreate(CurrentMemoryContext,
1093 : "event trigger context",
1094 : ALLOCSET_DEFAULT_SIZES);
1095 1083 : oldcontext = MemoryContextSwitchTo(context);
1096 :
1097 : /* Call each event trigger. */
1098 2282 : foreach(lc, fn_oid_list)
1099 : {
1100 1215 : LOCAL_FCINFO(fcinfo, 0);
1101 1215 : Oid fnoid = lfirst_oid(lc);
1102 : FmgrInfo flinfo;
1103 : PgStat_FunctionCallUsage fcusage;
1104 :
1105 1215 : elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
1106 :
1107 : /*
1108 : * We want each event trigger to be able to see the results of the
1109 : * previous event trigger's action. Caller is responsible for any
1110 : * command-counter increment that is needed between the event trigger
1111 : * and anything else in the transaction.
1112 : */
1113 1215 : if (first)
1114 1083 : first = false;
1115 : else
1116 132 : CommandCounterIncrement();
1117 :
1118 : /* Look up the function */
1119 1215 : fmgr_info(fnoid, &flinfo);
1120 :
1121 : /* Call the function, passing no arguments but setting a context. */
1122 1215 : InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
1123 : InvalidOid, (Node *) trigdata, NULL);
1124 1215 : pgstat_init_function_usage(fcinfo, &fcusage);
1125 1215 : FunctionCallInvoke(fcinfo);
1126 1199 : pgstat_end_function_usage(&fcusage, true);
1127 :
1128 : /* Reclaim memory. */
1129 1199 : MemoryContextReset(context);
1130 : }
1131 :
1132 : /* Restore old memory context and delete the temporary one. */
1133 1067 : MemoryContextSwitchTo(oldcontext);
1134 1067 : MemoryContextDelete(context);
1135 1067 : }
1136 :
1137 : /*
1138 : * Do event triggers support this object type?
1139 : *
1140 : * See also event trigger documentation in event-trigger.sgml.
1141 : */
1142 : bool
1143 50508 : EventTriggerSupportsObjectType(ObjectType obtype)
1144 : {
1145 50508 : switch (obtype)
1146 : {
1147 803 : case OBJECT_DATABASE:
1148 : case OBJECT_TABLESPACE:
1149 : case OBJECT_ROLE:
1150 : case OBJECT_PARAMETER_ACL:
1151 : /* no support for global objects (except subscriptions) */
1152 803 : return false;
1153 102 : case OBJECT_EVENT_TRIGGER:
1154 : /* no support for event triggers on event triggers */
1155 102 : return false;
1156 49603 : default:
1157 49603 : return true;
1158 : }
1159 : }
1160 :
1161 : /*
1162 : * Do event triggers support this object class?
1163 : *
1164 : * See also event trigger documentation in event-trigger.sgml.
1165 : */
1166 : bool
1167 2886 : EventTriggerSupportsObject(const ObjectAddress *object)
1168 : {
1169 2886 : switch (object->classId)
1170 : {
1171 0 : case DatabaseRelationId:
1172 : case TableSpaceRelationId:
1173 : case AuthIdRelationId:
1174 : case AuthMemRelationId:
1175 : case ParameterAclRelationId:
1176 : /* no support for global objects (except subscriptions) */
1177 0 : return false;
1178 74 : case EventTriggerRelationId:
1179 : /* no support for event triggers on event triggers */
1180 74 : return false;
1181 2812 : default:
1182 2812 : return true;
1183 : }
1184 : }
1185 :
1186 : /*
1187 : * Prepare event trigger state for a new complete query to run, if necessary;
1188 : * returns whether this was done. If it was, EventTriggerEndCompleteQuery must
1189 : * be called when the query is done, regardless of whether it succeeds or fails
1190 : * -- so use of a PG_TRY block is mandatory.
1191 : */
1192 : bool
1193 140300 : EventTriggerBeginCompleteQuery(void)
1194 : {
1195 : EventTriggerQueryState *state;
1196 : MemoryContext cxt;
1197 :
1198 : /*
1199 : * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
1200 : * reason to have event trigger state at all; so if there are none, don't
1201 : * install one.
1202 : */
1203 140300 : if (!trackDroppedObjectsNeeded())
1204 138365 : return false;
1205 :
1206 1935 : cxt = AllocSetContextCreate(TopMemoryContext,
1207 : "event trigger state",
1208 : ALLOCSET_DEFAULT_SIZES);
1209 1935 : state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
1210 1935 : state->cxt = cxt;
1211 1935 : slist_init(&(state->SQLDropList));
1212 1935 : state->in_sql_drop = false;
1213 1935 : state->table_rewrite_oid = InvalidOid;
1214 :
1215 3870 : state->commandCollectionInhibited = currentEventTriggerState ?
1216 1935 : currentEventTriggerState->commandCollectionInhibited : false;
1217 1935 : state->currentCommand = NULL;
1218 1935 : state->commandList = NIL;
1219 1935 : state->previous = currentEventTriggerState;
1220 1935 : currentEventTriggerState = state;
1221 :
1222 1935 : return true;
1223 : }
1224 :
1225 : /*
1226 : * Query completed (or errored out) -- clean up local state, return to previous
1227 : * one.
1228 : *
1229 : * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
1230 : * returned false previously.
1231 : *
1232 : * Note: this might be called in the PG_CATCH block of a failing transaction,
1233 : * so be wary of running anything unnecessary. (In particular, it's probably
1234 : * unwise to try to allocate memory.)
1235 : */
1236 : void
1237 1935 : EventTriggerEndCompleteQuery(void)
1238 : {
1239 : EventTriggerQueryState *prevstate;
1240 :
1241 1935 : prevstate = currentEventTriggerState->previous;
1242 :
1243 : /* this avoids the need for retail pfree of SQLDropList items: */
1244 1935 : MemoryContextDelete(currentEventTriggerState->cxt);
1245 :
1246 1935 : currentEventTriggerState = prevstate;
1247 1935 : }
1248 :
1249 : /*
1250 : * Do we need to keep close track of objects being dropped?
1251 : *
1252 : * This is useful because there is a cost to running with them enabled.
1253 : */
1254 : bool
1255 163129 : trackDroppedObjectsNeeded(void)
1256 : {
1257 : /*
1258 : * true if any sql_drop, table_rewrite, ddl_command_end event trigger
1259 : * exists
1260 : */
1261 324206 : return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
1262 324206 : (EventCacheLookup(EVT_TableRewrite) != NIL) ||
1263 160960 : (EventCacheLookup(EVT_DDLCommandEnd) != NIL);
1264 : }
1265 :
1266 : /*
1267 : * Support for dropped objects information on event trigger functions.
1268 : *
1269 : * We keep the list of objects dropped by the current command in current
1270 : * state's SQLDropList (comprising SQLDropObject items). Each time a new
1271 : * command is to start, a clean EventTriggerQueryState is created; commands
1272 : * that drop objects do the dependency.c dance to drop objects, which
1273 : * populates the current state's SQLDropList; when the event triggers are
1274 : * invoked they can consume the list via pg_event_trigger_dropped_objects().
1275 : * When the command finishes, the EventTriggerQueryState is cleared, and
1276 : * the one from the previous command is restored (when no command is in
1277 : * execution, the current state is NULL).
1278 : *
1279 : * All this lets us support the case that an event trigger function drops
1280 : * objects "reentrantly".
1281 : */
1282 :
1283 : /*
1284 : * Register one object as being dropped by the current command.
1285 : */
1286 : void
1287 2955 : EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
1288 : {
1289 : SQLDropObject *obj;
1290 : MemoryContext oldcxt;
1291 :
1292 2955 : if (!currentEventTriggerState)
1293 143 : return;
1294 :
1295 : Assert(EventTriggerSupportsObject(object));
1296 :
1297 2812 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1298 :
1299 2812 : obj = palloc0_object(SQLDropObject);
1300 2812 : obj->address = *object;
1301 2812 : obj->original = original;
1302 2812 : obj->normal = normal;
1303 :
1304 2812 : if (object->classId == NamespaceRelationId)
1305 : {
1306 : /* Special handling is needed for temp namespaces */
1307 46 : if (isTempNamespace(object->objectId))
1308 0 : obj->istemp = true;
1309 46 : else if (isAnyTempNamespace(object->objectId))
1310 : {
1311 : /* don't report temp schemas except my own */
1312 0 : pfree(obj);
1313 0 : MemoryContextSwitchTo(oldcxt);
1314 0 : return;
1315 : }
1316 46 : obj->objname = get_namespace_name(object->objectId);
1317 : }
1318 2766 : else if (object->classId == AttrDefaultRelationId)
1319 : {
1320 : /* We treat a column default as temp if its table is temp */
1321 : ObjectAddress colobject;
1322 :
1323 282 : colobject = GetAttrDefaultColumnAddress(object->objectId);
1324 282 : if (OidIsValid(colobject.objectId))
1325 : {
1326 282 : if (!obtain_object_name_namespace(&colobject, obj))
1327 : {
1328 0 : pfree(obj);
1329 0 : MemoryContextSwitchTo(oldcxt);
1330 0 : return;
1331 : }
1332 : }
1333 : }
1334 2484 : else if (object->classId == TriggerRelationId)
1335 : {
1336 : /* Similarly, a trigger is temp if its table is temp */
1337 : /* Sadly, there's no lsyscache.c support for trigger objects */
1338 : Relation pg_trigger_rel;
1339 : ScanKeyData skey[1];
1340 : SysScanDesc sscan;
1341 : HeapTuple tuple;
1342 : Oid relid;
1343 :
1344 : /* Fetch the trigger's table OID the hard way */
1345 112 : pg_trigger_rel = table_open(TriggerRelationId, AccessShareLock);
1346 112 : ScanKeyInit(&skey[0],
1347 : Anum_pg_trigger_oid,
1348 : BTEqualStrategyNumber, F_OIDEQ,
1349 112 : ObjectIdGetDatum(object->objectId));
1350 112 : sscan = systable_beginscan(pg_trigger_rel, TriggerOidIndexId, true,
1351 : NULL, 1, skey);
1352 112 : tuple = systable_getnext(sscan);
1353 112 : if (HeapTupleIsValid(tuple))
1354 112 : relid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid;
1355 : else
1356 0 : relid = InvalidOid; /* shouldn't happen */
1357 112 : systable_endscan(sscan);
1358 112 : table_close(pg_trigger_rel, AccessShareLock);
1359 : /* Do nothing if we didn't find the trigger */
1360 112 : if (OidIsValid(relid))
1361 : {
1362 : ObjectAddress relobject;
1363 :
1364 112 : relobject.classId = RelationRelationId;
1365 112 : relobject.objectId = relid;
1366 : /* Arbitrarily set objectSubId nonzero so as not to fill objname */
1367 112 : relobject.objectSubId = 1;
1368 112 : if (!obtain_object_name_namespace(&relobject, obj))
1369 : {
1370 0 : pfree(obj);
1371 0 : MemoryContextSwitchTo(oldcxt);
1372 0 : return;
1373 : }
1374 : }
1375 : }
1376 2372 : else if (object->classId == PolicyRelationId)
1377 : {
1378 : /* Similarly, a policy is temp if its table is temp */
1379 : /* Sadly, there's no lsyscache.c support for policy objects */
1380 : Relation pg_policy_rel;
1381 : ScanKeyData skey[1];
1382 : SysScanDesc sscan;
1383 : HeapTuple tuple;
1384 : Oid relid;
1385 :
1386 : /* Fetch the policy's table OID the hard way */
1387 20 : pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1388 20 : ScanKeyInit(&skey[0],
1389 : Anum_pg_policy_oid,
1390 : BTEqualStrategyNumber, F_OIDEQ,
1391 20 : ObjectIdGetDatum(object->objectId));
1392 20 : sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
1393 : NULL, 1, skey);
1394 20 : tuple = systable_getnext(sscan);
1395 20 : if (HeapTupleIsValid(tuple))
1396 20 : relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
1397 : else
1398 0 : relid = InvalidOid; /* shouldn't happen */
1399 20 : systable_endscan(sscan);
1400 20 : table_close(pg_policy_rel, AccessShareLock);
1401 : /* Do nothing if we didn't find the policy */
1402 20 : if (OidIsValid(relid))
1403 : {
1404 : ObjectAddress relobject;
1405 :
1406 20 : relobject.classId = RelationRelationId;
1407 20 : relobject.objectId = relid;
1408 : /* Arbitrarily set objectSubId nonzero so as not to fill objname */
1409 20 : relobject.objectSubId = 1;
1410 20 : if (!obtain_object_name_namespace(&relobject, obj))
1411 : {
1412 0 : pfree(obj);
1413 0 : MemoryContextSwitchTo(oldcxt);
1414 0 : return;
1415 : }
1416 : }
1417 : }
1418 : else
1419 : {
1420 : /* Generic handling for all other object classes */
1421 2352 : if (!obtain_object_name_namespace(object, obj))
1422 : {
1423 : /* don't report temp objects except my own */
1424 0 : pfree(obj);
1425 0 : MemoryContextSwitchTo(oldcxt);
1426 0 : return;
1427 : }
1428 : }
1429 :
1430 : /* object identity, objname and objargs */
1431 2812 : obj->objidentity =
1432 2812 : getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
1433 : false);
1434 :
1435 : /* object type */
1436 2812 : obj->objecttype = getObjectTypeDescription(&obj->address, false);
1437 :
1438 2812 : slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
1439 :
1440 2812 : MemoryContextSwitchTo(oldcxt);
1441 : }
1442 :
1443 : /*
1444 : * Fill obj->objname, obj->schemaname, and obj->istemp based on object.
1445 : *
1446 : * Returns true if this object should be reported, false if it should
1447 : * be ignored because it is a temporary object of another session.
1448 : */
1449 : static bool
1450 2766 : obtain_object_name_namespace(const ObjectAddress *object, SQLDropObject *obj)
1451 : {
1452 : /*
1453 : * Obtain schema names from the object's catalog tuple, if one exists;
1454 : * this lets us skip objects in temp schemas. We trust that
1455 : * ObjectProperty contains all object classes that can be
1456 : * schema-qualified.
1457 : *
1458 : * Currently, this function does nothing for object classes that are not
1459 : * in ObjectProperty, but we might sometime add special cases for that.
1460 : */
1461 2766 : if (is_objectclass_supported(object->classId))
1462 : {
1463 : Relation catalog;
1464 : HeapTuple tuple;
1465 :
1466 2766 : catalog = table_open(object->classId, AccessShareLock);
1467 2766 : tuple = get_catalog_object_by_oid(catalog,
1468 2766 : get_object_attnum_oid(object->classId),
1469 2766 : object->objectId);
1470 :
1471 2766 : if (tuple)
1472 : {
1473 : AttrNumber attnum;
1474 : Datum datum;
1475 : bool isnull;
1476 :
1477 2766 : attnum = get_object_attnum_namespace(object->classId);
1478 2766 : if (attnum != InvalidAttrNumber)
1479 : {
1480 2677 : datum = heap_getattr(tuple, attnum,
1481 : RelationGetDescr(catalog), &isnull);
1482 2677 : if (!isnull)
1483 : {
1484 : Oid namespaceId;
1485 :
1486 2677 : namespaceId = DatumGetObjectId(datum);
1487 : /* temp objects are only reported if they are my own */
1488 2677 : if (isTempNamespace(namespaceId))
1489 : {
1490 48 : obj->schemaname = "pg_temp";
1491 48 : obj->istemp = true;
1492 : }
1493 2629 : else if (isAnyTempNamespace(namespaceId))
1494 : {
1495 : /* no need to fill any fields of *obj */
1496 0 : table_close(catalog, AccessShareLock);
1497 0 : return false;
1498 : }
1499 : else
1500 : {
1501 2629 : obj->schemaname = get_namespace_name(namespaceId);
1502 2629 : obj->istemp = false;
1503 : }
1504 : }
1505 : }
1506 :
1507 2766 : if (get_object_namensp_unique(object->classId) &&
1508 2221 : object->objectSubId == 0)
1509 : {
1510 1791 : attnum = get_object_attnum_name(object->classId);
1511 1791 : if (attnum != InvalidAttrNumber)
1512 : {
1513 1791 : datum = heap_getattr(tuple, attnum,
1514 : RelationGetDescr(catalog), &isnull);
1515 1791 : if (!isnull)
1516 1791 : obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
1517 : }
1518 : }
1519 : }
1520 :
1521 2766 : table_close(catalog, AccessShareLock);
1522 : }
1523 :
1524 2766 : return true;
1525 : }
1526 :
1527 : /*
1528 : * pg_event_trigger_dropped_objects
1529 : *
1530 : * Make the list of dropped objects available to the user function run by the
1531 : * Event Trigger.
1532 : */
1533 : Datum
1534 96 : pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
1535 : {
1536 96 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1537 : slist_iter iter;
1538 :
1539 : /*
1540 : * Protect this function from being called out of context
1541 : */
1542 96 : if (!currentEventTriggerState ||
1543 96 : !currentEventTriggerState->in_sql_drop)
1544 0 : ereport(ERROR,
1545 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1546 : errmsg("%s can only be called in a sql_drop event trigger function",
1547 : "pg_event_trigger_dropped_objects()")));
1548 :
1549 : /* Build tuplestore to hold the result rows */
1550 96 : InitMaterializedSRF(fcinfo, 0);
1551 :
1552 928 : slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
1553 : {
1554 : SQLDropObject *obj;
1555 832 : int i = 0;
1556 832 : Datum values[12] = {0};
1557 832 : bool nulls[12] = {0};
1558 :
1559 832 : obj = slist_container(SQLDropObject, next, iter.cur);
1560 :
1561 : /* classid */
1562 832 : values[i++] = ObjectIdGetDatum(obj->address.classId);
1563 :
1564 : /* objid */
1565 832 : values[i++] = ObjectIdGetDatum(obj->address.objectId);
1566 :
1567 : /* objsubid */
1568 832 : values[i++] = Int32GetDatum(obj->address.objectSubId);
1569 :
1570 : /* original */
1571 832 : values[i++] = BoolGetDatum(obj->original);
1572 :
1573 : /* normal */
1574 832 : values[i++] = BoolGetDatum(obj->normal);
1575 :
1576 : /* is_temporary */
1577 832 : values[i++] = BoolGetDatum(obj->istemp);
1578 :
1579 : /* object_type */
1580 832 : values[i++] = CStringGetTextDatum(obj->objecttype);
1581 :
1582 : /* schema_name */
1583 832 : if (obj->schemaname)
1584 780 : values[i++] = CStringGetTextDatum(obj->schemaname);
1585 : else
1586 52 : nulls[i++] = true;
1587 :
1588 : /* object_name */
1589 832 : if (obj->objname)
1590 628 : values[i++] = CStringGetTextDatum(obj->objname);
1591 : else
1592 204 : nulls[i++] = true;
1593 :
1594 : /* object_identity */
1595 832 : if (obj->objidentity)
1596 832 : values[i++] = CStringGetTextDatum(obj->objidentity);
1597 : else
1598 0 : nulls[i++] = true;
1599 :
1600 : /* address_names and address_args */
1601 832 : if (obj->addrnames)
1602 : {
1603 832 : values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
1604 :
1605 832 : if (obj->addrargs)
1606 40 : values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
1607 : else
1608 792 : values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
1609 : }
1610 : else
1611 : {
1612 0 : nulls[i++] = true;
1613 0 : nulls[i++] = true;
1614 : }
1615 :
1616 832 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1617 : values, nulls);
1618 : }
1619 :
1620 96 : return (Datum) 0;
1621 : }
1622 :
1623 : /*
1624 : * pg_event_trigger_table_rewrite_oid
1625 : *
1626 : * Make the Oid of the table going to be rewritten available to the user
1627 : * function run by the Event Trigger.
1628 : */
1629 : Datum
1630 85 : pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
1631 : {
1632 : /*
1633 : * Protect this function from being called out of context
1634 : */
1635 85 : if (!currentEventTriggerState ||
1636 81 : currentEventTriggerState->table_rewrite_oid == InvalidOid)
1637 4 : ereport(ERROR,
1638 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1639 : errmsg("%s can only be called in a table_rewrite event trigger function",
1640 : "pg_event_trigger_table_rewrite_oid()")));
1641 :
1642 81 : PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
1643 : }
1644 :
1645 : /*
1646 : * pg_event_trigger_table_rewrite_reason
1647 : *
1648 : * Make the rewrite reason available to the user.
1649 : */
1650 : Datum
1651 52 : pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
1652 : {
1653 : /*
1654 : * Protect this function from being called out of context
1655 : */
1656 52 : if (!currentEventTriggerState ||
1657 52 : currentEventTriggerState->table_rewrite_reason == 0)
1658 0 : ereport(ERROR,
1659 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1660 : errmsg("%s can only be called in a table_rewrite event trigger function",
1661 : "pg_event_trigger_table_rewrite_reason()")));
1662 :
1663 52 : PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
1664 : }
1665 :
1666 : /*-------------------------------------------------------------------------
1667 : * Support for DDL command deparsing
1668 : *
1669 : * The routines below enable an event trigger function to obtain a list of
1670 : * DDL commands as they are executed. There are three main pieces to this
1671 : * feature:
1672 : *
1673 : * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
1674 : * adds a struct CollectedCommand representation of itself to the command list,
1675 : * using the routines below.
1676 : *
1677 : * 2) Some time after that, ddl_command_end fires and the command list is made
1678 : * available to the event trigger function via pg_event_trigger_ddl_commands();
1679 : * the complete command details are exposed as a column of type pg_ddl_command.
1680 : *
1681 : * 3) An extension can install a function capable of taking a value of type
1682 : * pg_ddl_command and transform it into some external, user-visible and/or
1683 : * -modifiable representation.
1684 : *-------------------------------------------------------------------------
1685 : */
1686 :
1687 : /*
1688 : * Inhibit DDL command collection.
1689 : */
1690 : void
1691 174 : EventTriggerInhibitCommandCollection(void)
1692 : {
1693 174 : if (!currentEventTriggerState)
1694 169 : return;
1695 :
1696 5 : currentEventTriggerState->commandCollectionInhibited = true;
1697 : }
1698 :
1699 : /*
1700 : * Re-establish DDL command collection.
1701 : */
1702 : void
1703 174 : EventTriggerUndoInhibitCommandCollection(void)
1704 : {
1705 174 : if (!currentEventTriggerState)
1706 169 : return;
1707 :
1708 5 : currentEventTriggerState->commandCollectionInhibited = false;
1709 : }
1710 :
1711 : /*
1712 : * EventTriggerCollectSimpleCommand
1713 : * Save data about a simple DDL command that was just executed
1714 : *
1715 : * address identifies the object being operated on. secondaryObject is an
1716 : * object address that was related in some way to the executed command; its
1717 : * meaning is command-specific.
1718 : *
1719 : * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
1720 : * object being moved, objectId is its OID, and secondaryOid is the OID of the
1721 : * old schema. (The destination schema OID can be obtained by catalog lookup
1722 : * of the object.)
1723 : */
1724 : void
1725 86939 : EventTriggerCollectSimpleCommand(ObjectAddress address,
1726 : ObjectAddress secondaryObject,
1727 : const Node *parsetree)
1728 : {
1729 : MemoryContext oldcxt;
1730 : CollectedCommand *command;
1731 :
1732 : /* ignore if event trigger context not set, or collection disabled */
1733 86939 : if (!currentEventTriggerState ||
1734 1149 : currentEventTriggerState->commandCollectionInhibited)
1735 85790 : return;
1736 :
1737 1149 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1738 :
1739 1149 : command = palloc_object(CollectedCommand);
1740 :
1741 1149 : command->type = SCT_Simple;
1742 1149 : command->in_extension = creating_extension;
1743 :
1744 1149 : command->d.simple.address = address;
1745 1149 : command->d.simple.secondaryObject = secondaryObject;
1746 1149 : command->parsetree = copyObject(parsetree);
1747 :
1748 1149 : currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
1749 : command);
1750 :
1751 1149 : MemoryContextSwitchTo(oldcxt);
1752 : }
1753 :
1754 : /*
1755 : * EventTriggerAlterTableStart
1756 : * Prepare to receive data on an ALTER TABLE command about to be executed
1757 : *
1758 : * Note we don't collect the command immediately; instead we keep it in
1759 : * currentCommand, and only when we're done processing the subcommands we will
1760 : * add it to the command list.
1761 : */
1762 : void
1763 42081 : EventTriggerAlterTableStart(const Node *parsetree)
1764 : {
1765 : MemoryContext oldcxt;
1766 : CollectedCommand *command;
1767 :
1768 : /* ignore if event trigger context not set, or collection disabled */
1769 42081 : if (!currentEventTriggerState ||
1770 839 : currentEventTriggerState->commandCollectionInhibited)
1771 41242 : return;
1772 :
1773 839 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1774 :
1775 839 : command = palloc_object(CollectedCommand);
1776 :
1777 839 : command->type = SCT_AlterTable;
1778 839 : command->in_extension = creating_extension;
1779 :
1780 839 : command->d.alterTable.classId = RelationRelationId;
1781 839 : command->d.alterTable.objectId = InvalidOid;
1782 839 : command->d.alterTable.subcmds = NIL;
1783 839 : command->parsetree = copyObject(parsetree);
1784 :
1785 839 : command->parent = currentEventTriggerState->currentCommand;
1786 839 : currentEventTriggerState->currentCommand = command;
1787 :
1788 839 : MemoryContextSwitchTo(oldcxt);
1789 : }
1790 :
1791 : /*
1792 : * Remember the OID of the object being affected by an ALTER TABLE.
1793 : *
1794 : * This is needed because in some cases we don't know the OID until later.
1795 : */
1796 : void
1797 22094 : EventTriggerAlterTableRelid(Oid objectId)
1798 : {
1799 22094 : if (!currentEventTriggerState ||
1800 617 : currentEventTriggerState->commandCollectionInhibited)
1801 21477 : return;
1802 :
1803 617 : currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
1804 : }
1805 :
1806 : /*
1807 : * EventTriggerCollectAlterTableSubcmd
1808 : * Save data about a single part of an ALTER TABLE.
1809 : *
1810 : * Several different commands go through this path, but apart from ALTER TABLE
1811 : * itself, they are all concerned with AlterTableCmd nodes that are generated
1812 : * internally, so that's all that this code needs to handle at the moment.
1813 : */
1814 : void
1815 26457 : EventTriggerCollectAlterTableSubcmd(const Node *subcmd, ObjectAddress address)
1816 : {
1817 : MemoryContext oldcxt;
1818 : CollectedATSubcmd *newsub;
1819 :
1820 : /* ignore if event trigger context not set, or collection disabled */
1821 26457 : if (!currentEventTriggerState ||
1822 994 : currentEventTriggerState->commandCollectionInhibited)
1823 25463 : return;
1824 :
1825 : Assert(IsA(subcmd, AlterTableCmd));
1826 : Assert(currentEventTriggerState->currentCommand != NULL);
1827 : Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
1828 :
1829 994 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1830 :
1831 994 : newsub = palloc_object(CollectedATSubcmd);
1832 994 : newsub->address = address;
1833 994 : newsub->parsetree = copyObject(subcmd);
1834 :
1835 1988 : currentEventTriggerState->currentCommand->d.alterTable.subcmds =
1836 994 : lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
1837 :
1838 994 : MemoryContextSwitchTo(oldcxt);
1839 : }
1840 :
1841 : /*
1842 : * EventTriggerAlterTableEnd
1843 : * Finish up saving an ALTER TABLE command, and add it to command list.
1844 : *
1845 : * FIXME this API isn't considering the possibility that an xact/subxact is
1846 : * aborted partway through. Probably it's best to add an
1847 : * AtEOSubXact_EventTriggers() to fix this.
1848 : */
1849 : void
1850 38672 : EventTriggerAlterTableEnd(void)
1851 : {
1852 : CollectedCommand *parent;
1853 :
1854 : /* ignore if event trigger context not set, or collection disabled */
1855 38672 : if (!currentEventTriggerState ||
1856 819 : currentEventTriggerState->commandCollectionInhibited)
1857 37853 : return;
1858 :
1859 819 : parent = currentEventTriggerState->currentCommand->parent;
1860 :
1861 : /* If no subcommands, don't collect */
1862 819 : if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
1863 : {
1864 : MemoryContext oldcxt;
1865 :
1866 587 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1867 :
1868 1174 : currentEventTriggerState->commandList =
1869 587 : lappend(currentEventTriggerState->commandList,
1870 587 : currentEventTriggerState->currentCommand);
1871 :
1872 587 : MemoryContextSwitchTo(oldcxt);
1873 : }
1874 : else
1875 232 : pfree(currentEventTriggerState->currentCommand);
1876 :
1877 819 : currentEventTriggerState->currentCommand = parent;
1878 : }
1879 :
1880 : /*
1881 : * EventTriggerCollectGrant
1882 : * Save data about a GRANT/REVOKE command being executed
1883 : *
1884 : * This function creates a copy of the InternalGrant, as the original might
1885 : * not have the right lifetime.
1886 : */
1887 : void
1888 12596 : EventTriggerCollectGrant(InternalGrant *istmt)
1889 : {
1890 : MemoryContext oldcxt;
1891 : CollectedCommand *command;
1892 : InternalGrant *icopy;
1893 : ListCell *cell;
1894 :
1895 : /* ignore if event trigger context not set, or collection disabled */
1896 12596 : if (!currentEventTriggerState ||
1897 29 : currentEventTriggerState->commandCollectionInhibited)
1898 12567 : return;
1899 :
1900 29 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1901 :
1902 : /*
1903 : * This is tedious, but necessary.
1904 : */
1905 29 : icopy = palloc_object(InternalGrant);
1906 29 : memcpy(icopy, istmt, sizeof(InternalGrant));
1907 29 : icopy->objects = list_copy(istmt->objects);
1908 29 : icopy->grantees = list_copy(istmt->grantees);
1909 29 : icopy->col_privs = NIL;
1910 29 : foreach(cell, istmt->col_privs)
1911 0 : icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
1912 :
1913 : /* Now collect it, using the copied InternalGrant */
1914 29 : command = palloc_object(CollectedCommand);
1915 29 : command->type = SCT_Grant;
1916 29 : command->in_extension = creating_extension;
1917 29 : command->d.grant.istmt = icopy;
1918 29 : command->parsetree = NULL;
1919 :
1920 58 : currentEventTriggerState->commandList =
1921 29 : lappend(currentEventTriggerState->commandList, command);
1922 :
1923 29 : MemoryContextSwitchTo(oldcxt);
1924 : }
1925 :
1926 : /*
1927 : * EventTriggerCollectAlterOpFam
1928 : * Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
1929 : * executed
1930 : */
1931 : void
1932 168 : EventTriggerCollectAlterOpFam(const AlterOpFamilyStmt *stmt, Oid opfamoid,
1933 : List *operators, List *procedures)
1934 : {
1935 : MemoryContext oldcxt;
1936 : CollectedCommand *command;
1937 :
1938 : /* ignore if event trigger context not set, or collection disabled */
1939 168 : if (!currentEventTriggerState ||
1940 1 : currentEventTriggerState->commandCollectionInhibited)
1941 167 : return;
1942 :
1943 1 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1944 :
1945 1 : command = palloc_object(CollectedCommand);
1946 1 : command->type = SCT_AlterOpFamily;
1947 1 : command->in_extension = creating_extension;
1948 1 : ObjectAddressSet(command->d.opfam.address,
1949 : OperatorFamilyRelationId, opfamoid);
1950 1 : command->d.opfam.operators = operators;
1951 1 : command->d.opfam.procedures = procedures;
1952 1 : command->parsetree = (Node *) copyObject(stmt);
1953 :
1954 2 : currentEventTriggerState->commandList =
1955 1 : lappend(currentEventTriggerState->commandList, command);
1956 :
1957 1 : MemoryContextSwitchTo(oldcxt);
1958 : }
1959 :
1960 : /*
1961 : * EventTriggerCollectCreateOpClass
1962 : * Save data about a CREATE OPERATOR CLASS command being executed
1963 : */
1964 : void
1965 295 : EventTriggerCollectCreateOpClass(const CreateOpClassStmt *stmt, Oid opcoid,
1966 : List *operators, List *procedures)
1967 : {
1968 : MemoryContext oldcxt;
1969 : CollectedCommand *command;
1970 :
1971 : /* ignore if event trigger context not set, or collection disabled */
1972 295 : if (!currentEventTriggerState ||
1973 5 : currentEventTriggerState->commandCollectionInhibited)
1974 290 : return;
1975 :
1976 5 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1977 :
1978 5 : command = palloc0_object(CollectedCommand);
1979 5 : command->type = SCT_CreateOpClass;
1980 5 : command->in_extension = creating_extension;
1981 5 : ObjectAddressSet(command->d.createopc.address,
1982 : OperatorClassRelationId, opcoid);
1983 5 : command->d.createopc.operators = operators;
1984 5 : command->d.createopc.procedures = procedures;
1985 5 : command->parsetree = (Node *) copyObject(stmt);
1986 :
1987 10 : currentEventTriggerState->commandList =
1988 5 : lappend(currentEventTriggerState->commandList, command);
1989 :
1990 5 : MemoryContextSwitchTo(oldcxt);
1991 : }
1992 :
1993 : /*
1994 : * EventTriggerCollectAlterTSConfig
1995 : * Save data about an ALTER TEXT SEARCH CONFIGURATION command being
1996 : * executed
1997 : */
1998 : void
1999 5234 : EventTriggerCollectAlterTSConfig(const AlterTSConfigurationStmt *stmt, Oid cfgId,
2000 : Oid *dictIds, int ndicts)
2001 : {
2002 : MemoryContext oldcxt;
2003 : CollectedCommand *command;
2004 :
2005 : /* ignore if event trigger context not set, or collection disabled */
2006 5234 : if (!currentEventTriggerState ||
2007 2 : currentEventTriggerState->commandCollectionInhibited)
2008 5232 : return;
2009 :
2010 2 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
2011 :
2012 2 : command = palloc0_object(CollectedCommand);
2013 2 : command->type = SCT_AlterTSConfig;
2014 2 : command->in_extension = creating_extension;
2015 2 : ObjectAddressSet(command->d.atscfg.address,
2016 : TSConfigRelationId, cfgId);
2017 2 : if (ndicts > 0)
2018 : {
2019 1 : command->d.atscfg.dictIds = palloc_array(Oid, ndicts);
2020 1 : memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
2021 : }
2022 2 : command->d.atscfg.ndicts = ndicts;
2023 2 : command->parsetree = (Node *) copyObject(stmt);
2024 :
2025 4 : currentEventTriggerState->commandList =
2026 2 : lappend(currentEventTriggerState->commandList, command);
2027 :
2028 2 : MemoryContextSwitchTo(oldcxt);
2029 : }
2030 :
2031 : /*
2032 : * EventTriggerCollectAlterDefPrivs
2033 : * Save data about an ALTER DEFAULT PRIVILEGES command being
2034 : * executed
2035 : */
2036 : void
2037 127 : EventTriggerCollectAlterDefPrivs(const AlterDefaultPrivilegesStmt *stmt)
2038 : {
2039 : MemoryContext oldcxt;
2040 : CollectedCommand *command;
2041 :
2042 : /* ignore if event trigger context not set, or collection disabled */
2043 127 : if (!currentEventTriggerState ||
2044 5 : currentEventTriggerState->commandCollectionInhibited)
2045 122 : return;
2046 :
2047 5 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
2048 :
2049 5 : command = palloc0_object(CollectedCommand);
2050 5 : command->type = SCT_AlterDefaultPrivileges;
2051 5 : command->d.defprivs.objtype = stmt->action->objtype;
2052 5 : command->in_extension = creating_extension;
2053 5 : command->parsetree = (Node *) copyObject(stmt);
2054 :
2055 10 : currentEventTriggerState->commandList =
2056 5 : lappend(currentEventTriggerState->commandList, command);
2057 5 : MemoryContextSwitchTo(oldcxt);
2058 : }
2059 :
2060 : /*
2061 : * In a ddl_command_end event trigger, this function reports the DDL commands
2062 : * being run.
2063 : */
2064 : Datum
2065 396 : pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
2066 : {
2067 396 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2068 : ListCell *lc;
2069 :
2070 : /*
2071 : * Protect this function from being called out of context
2072 : */
2073 396 : if (!currentEventTriggerState)
2074 0 : ereport(ERROR,
2075 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
2076 : errmsg("%s can only be called in an event trigger function",
2077 : "pg_event_trigger_ddl_commands()")));
2078 :
2079 : /* Build tuplestore to hold the result rows */
2080 396 : InitMaterializedSRF(fcinfo, 0);
2081 :
2082 830 : foreach(lc, currentEventTriggerState->commandList)
2083 : {
2084 434 : CollectedCommand *cmd = lfirst(lc);
2085 : Datum values[9];
2086 434 : bool nulls[9] = {0};
2087 : ObjectAddress addr;
2088 434 : int i = 0;
2089 :
2090 : /*
2091 : * For IF NOT EXISTS commands that attempt to create an existing
2092 : * object, the returned OID is Invalid. Don't return anything.
2093 : *
2094 : * One might think that a viable alternative would be to look up the
2095 : * Oid of the existing object and run the deparse with that. But
2096 : * since the parse tree might be different from the one that created
2097 : * the object in the first place, we might not end up in a consistent
2098 : * state anyway.
2099 : */
2100 434 : if (cmd->type == SCT_Simple &&
2101 342 : !OidIsValid(cmd->d.simple.address.objectId))
2102 4 : continue;
2103 :
2104 434 : switch (cmd->type)
2105 : {
2106 420 : case SCT_Simple:
2107 : case SCT_AlterTable:
2108 : case SCT_AlterOpFamily:
2109 : case SCT_CreateOpClass:
2110 : case SCT_AlterTSConfig:
2111 : {
2112 : char *identity;
2113 : char *type;
2114 420 : char *schema = NULL;
2115 :
2116 420 : if (cmd->type == SCT_Simple)
2117 342 : addr = cmd->d.simple.address;
2118 78 : else if (cmd->type == SCT_AlterTable)
2119 70 : ObjectAddressSet(addr,
2120 : cmd->d.alterTable.classId,
2121 : cmd->d.alterTable.objectId);
2122 8 : else if (cmd->type == SCT_AlterOpFamily)
2123 1 : addr = cmd->d.opfam.address;
2124 7 : else if (cmd->type == SCT_CreateOpClass)
2125 5 : addr = cmd->d.createopc.address;
2126 2 : else if (cmd->type == SCT_AlterTSConfig)
2127 2 : addr = cmd->d.atscfg.address;
2128 :
2129 : /*
2130 : * If an object was dropped in the same command we may end
2131 : * up in a situation where we generated a message but can
2132 : * no longer look for the object information, so skip it
2133 : * rather than failing. This can happen for example with
2134 : * some subcommand combinations of ALTER TABLE.
2135 : */
2136 420 : identity = getObjectIdentity(&addr, true);
2137 420 : if (identity == NULL)
2138 4 : continue;
2139 :
2140 : /* The type can never be NULL. */
2141 416 : type = getObjectTypeDescription(&addr, true);
2142 :
2143 : /*
2144 : * Obtain schema name, if any ("pg_temp" if a temp
2145 : * object). If the object class is not in the supported
2146 : * list here, we assume it's a schema-less object type,
2147 : * and thus "schema" remains set to NULL.
2148 : */
2149 416 : if (is_objectclass_supported(addr.classId))
2150 : {
2151 : AttrNumber nspAttnum;
2152 :
2153 416 : nspAttnum = get_object_attnum_namespace(addr.classId);
2154 416 : if (nspAttnum != InvalidAttrNumber)
2155 : {
2156 : Relation catalog;
2157 : HeapTuple objtup;
2158 : Oid schema_oid;
2159 : bool isnull;
2160 :
2161 366 : catalog = table_open(addr.classId, AccessShareLock);
2162 366 : objtup = get_catalog_object_by_oid(catalog,
2163 366 : get_object_attnum_oid(addr.classId),
2164 : addr.objectId);
2165 366 : if (!HeapTupleIsValid(objtup))
2166 0 : elog(ERROR, "cache lookup failed for object %u/%u",
2167 : addr.classId, addr.objectId);
2168 : schema_oid =
2169 366 : DatumGetObjectId(heap_getattr(objtup, nspAttnum,
2170 : RelationGetDescr(catalog), &isnull));
2171 366 : if (isnull)
2172 0 : elog(ERROR,
2173 : "invalid null namespace in object %u/%u/%d",
2174 : addr.classId, addr.objectId, addr.objectSubId);
2175 366 : schema = get_namespace_name_or_temp(schema_oid);
2176 :
2177 366 : table_close(catalog, AccessShareLock);
2178 : }
2179 : }
2180 :
2181 : /* classid */
2182 416 : values[i++] = ObjectIdGetDatum(addr.classId);
2183 : /* objid */
2184 416 : values[i++] = ObjectIdGetDatum(addr.objectId);
2185 : /* objsubid */
2186 416 : values[i++] = Int32GetDatum(addr.objectSubId);
2187 : /* command tag */
2188 416 : values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2189 : /* object_type */
2190 416 : values[i++] = CStringGetTextDatum(type);
2191 : /* schema */
2192 416 : if (schema == NULL)
2193 50 : nulls[i++] = true;
2194 : else
2195 366 : values[i++] = CStringGetTextDatum(schema);
2196 : /* identity */
2197 416 : values[i++] = CStringGetTextDatum(identity);
2198 : /* in_extension */
2199 416 : values[i++] = BoolGetDatum(cmd->in_extension);
2200 : /* command */
2201 416 : values[i++] = PointerGetDatum(cmd);
2202 : }
2203 416 : break;
2204 :
2205 1 : case SCT_AlterDefaultPrivileges:
2206 : /* classid */
2207 1 : nulls[i++] = true;
2208 : /* objid */
2209 1 : nulls[i++] = true;
2210 : /* objsubid */
2211 1 : nulls[i++] = true;
2212 : /* command tag */
2213 1 : values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2214 : /* object_type */
2215 1 : values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
2216 : /* schema */
2217 1 : nulls[i++] = true;
2218 : /* identity */
2219 1 : nulls[i++] = true;
2220 : /* in_extension */
2221 1 : values[i++] = BoolGetDatum(cmd->in_extension);
2222 : /* command */
2223 1 : values[i++] = PointerGetDatum(cmd);
2224 1 : break;
2225 :
2226 13 : case SCT_Grant:
2227 : /* classid */
2228 13 : nulls[i++] = true;
2229 : /* objid */
2230 13 : nulls[i++] = true;
2231 : /* objsubid */
2232 13 : nulls[i++] = true;
2233 : /* command tag */
2234 13 : values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
2235 : "GRANT" : "REVOKE");
2236 : /* object_type */
2237 13 : values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
2238 : /* schema */
2239 13 : nulls[i++] = true;
2240 : /* identity */
2241 13 : nulls[i++] = true;
2242 : /* in_extension */
2243 13 : values[i++] = BoolGetDatum(cmd->in_extension);
2244 : /* command */
2245 13 : values[i++] = PointerGetDatum(cmd);
2246 13 : break;
2247 : }
2248 :
2249 430 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2250 : values, nulls);
2251 : }
2252 :
2253 396 : PG_RETURN_VOID();
2254 : }
2255 :
2256 : /*
2257 : * Return the ObjectType as a string, as it would appear in GRANT and
2258 : * REVOKE commands.
2259 : */
2260 : static const char *
2261 13 : stringify_grant_objtype(ObjectType objtype)
2262 : {
2263 13 : switch (objtype)
2264 : {
2265 0 : case OBJECT_COLUMN:
2266 0 : return "COLUMN";
2267 8 : case OBJECT_TABLE:
2268 8 : return "TABLE";
2269 0 : case OBJECT_SEQUENCE:
2270 0 : return "SEQUENCE";
2271 0 : case OBJECT_DATABASE:
2272 0 : return "DATABASE";
2273 0 : case OBJECT_DOMAIN:
2274 0 : return "DOMAIN";
2275 0 : case OBJECT_FDW:
2276 0 : return "FOREIGN DATA WRAPPER";
2277 0 : case OBJECT_FOREIGN_SERVER:
2278 0 : return "FOREIGN SERVER";
2279 5 : case OBJECT_FUNCTION:
2280 5 : return "FUNCTION";
2281 0 : case OBJECT_LANGUAGE:
2282 0 : return "LANGUAGE";
2283 0 : case OBJECT_LARGEOBJECT:
2284 0 : return "LARGE OBJECT";
2285 0 : case OBJECT_SCHEMA:
2286 0 : return "SCHEMA";
2287 0 : case OBJECT_PARAMETER_ACL:
2288 0 : return "PARAMETER";
2289 0 : case OBJECT_PROCEDURE:
2290 0 : return "PROCEDURE";
2291 0 : case OBJECT_ROUTINE:
2292 0 : return "ROUTINE";
2293 0 : case OBJECT_TABLESPACE:
2294 0 : return "TABLESPACE";
2295 0 : case OBJECT_TYPE:
2296 0 : return "TYPE";
2297 : /* these currently aren't used */
2298 0 : case OBJECT_ACCESS_METHOD:
2299 : case OBJECT_AGGREGATE:
2300 : case OBJECT_AMOP:
2301 : case OBJECT_AMPROC:
2302 : case OBJECT_ATTRIBUTE:
2303 : case OBJECT_CAST:
2304 : case OBJECT_COLLATION:
2305 : case OBJECT_CONVERSION:
2306 : case OBJECT_DEFAULT:
2307 : case OBJECT_DEFACL:
2308 : case OBJECT_DOMCONSTRAINT:
2309 : case OBJECT_EVENT_TRIGGER:
2310 : case OBJECT_EXTENSION:
2311 : case OBJECT_FOREIGN_TABLE:
2312 : case OBJECT_INDEX:
2313 : case OBJECT_MATVIEW:
2314 : case OBJECT_OPCLASS:
2315 : case OBJECT_OPERATOR:
2316 : case OBJECT_OPFAMILY:
2317 : case OBJECT_POLICY:
2318 : case OBJECT_PROPGRAPH:
2319 : case OBJECT_PUBLICATION:
2320 : case OBJECT_PUBLICATION_NAMESPACE:
2321 : case OBJECT_PUBLICATION_REL:
2322 : case OBJECT_ROLE:
2323 : case OBJECT_RULE:
2324 : case OBJECT_STATISTIC_EXT:
2325 : case OBJECT_SUBSCRIPTION:
2326 : case OBJECT_TABCONSTRAINT:
2327 : case OBJECT_TRANSFORM:
2328 : case OBJECT_TRIGGER:
2329 : case OBJECT_TSCONFIGURATION:
2330 : case OBJECT_TSDICTIONARY:
2331 : case OBJECT_TSPARSER:
2332 : case OBJECT_TSTEMPLATE:
2333 : case OBJECT_USER_MAPPING:
2334 : case OBJECT_VIEW:
2335 0 : elog(ERROR, "unsupported object type: %d", (int) objtype);
2336 : }
2337 :
2338 0 : return "???"; /* keep compiler quiet */
2339 : }
2340 :
2341 : /*
2342 : * Return the ObjectType as a string; as above, but use the spelling
2343 : * in ALTER DEFAULT PRIVILEGES commands instead. Generally this is just
2344 : * the plural.
2345 : */
2346 : static const char *
2347 1 : stringify_adefprivs_objtype(ObjectType objtype)
2348 : {
2349 1 : switch (objtype)
2350 : {
2351 0 : case OBJECT_COLUMN:
2352 0 : return "COLUMNS";
2353 1 : case OBJECT_TABLE:
2354 1 : return "TABLES";
2355 0 : case OBJECT_SEQUENCE:
2356 0 : return "SEQUENCES";
2357 0 : case OBJECT_DATABASE:
2358 0 : return "DATABASES";
2359 0 : case OBJECT_DOMAIN:
2360 0 : return "DOMAINS";
2361 0 : case OBJECT_FDW:
2362 0 : return "FOREIGN DATA WRAPPERS";
2363 0 : case OBJECT_FOREIGN_SERVER:
2364 0 : return "FOREIGN SERVERS";
2365 0 : case OBJECT_FUNCTION:
2366 0 : return "FUNCTIONS";
2367 0 : case OBJECT_LANGUAGE:
2368 0 : return "LANGUAGES";
2369 0 : case OBJECT_LARGEOBJECT:
2370 0 : return "LARGE OBJECTS";
2371 0 : case OBJECT_SCHEMA:
2372 0 : return "SCHEMAS";
2373 0 : case OBJECT_PROCEDURE:
2374 0 : return "PROCEDURES";
2375 0 : case OBJECT_ROUTINE:
2376 0 : return "ROUTINES";
2377 0 : case OBJECT_TABLESPACE:
2378 0 : return "TABLESPACES";
2379 0 : case OBJECT_TYPE:
2380 0 : return "TYPES";
2381 : /* these currently aren't used */
2382 0 : case OBJECT_ACCESS_METHOD:
2383 : case OBJECT_AGGREGATE:
2384 : case OBJECT_AMOP:
2385 : case OBJECT_AMPROC:
2386 : case OBJECT_ATTRIBUTE:
2387 : case OBJECT_CAST:
2388 : case OBJECT_COLLATION:
2389 : case OBJECT_CONVERSION:
2390 : case OBJECT_DEFAULT:
2391 : case OBJECT_DEFACL:
2392 : case OBJECT_DOMCONSTRAINT:
2393 : case OBJECT_EVENT_TRIGGER:
2394 : case OBJECT_EXTENSION:
2395 : case OBJECT_FOREIGN_TABLE:
2396 : case OBJECT_INDEX:
2397 : case OBJECT_MATVIEW:
2398 : case OBJECT_OPCLASS:
2399 : case OBJECT_OPERATOR:
2400 : case OBJECT_OPFAMILY:
2401 : case OBJECT_PARAMETER_ACL:
2402 : case OBJECT_POLICY:
2403 : case OBJECT_PROPGRAPH:
2404 : case OBJECT_PUBLICATION:
2405 : case OBJECT_PUBLICATION_NAMESPACE:
2406 : case OBJECT_PUBLICATION_REL:
2407 : case OBJECT_ROLE:
2408 : case OBJECT_RULE:
2409 : case OBJECT_STATISTIC_EXT:
2410 : case OBJECT_SUBSCRIPTION:
2411 : case OBJECT_TABCONSTRAINT:
2412 : case OBJECT_TRANSFORM:
2413 : case OBJECT_TRIGGER:
2414 : case OBJECT_TSCONFIGURATION:
2415 : case OBJECT_TSDICTIONARY:
2416 : case OBJECT_TSPARSER:
2417 : case OBJECT_TSTEMPLATE:
2418 : case OBJECT_USER_MAPPING:
2419 : case OBJECT_VIEW:
2420 0 : elog(ERROR, "unsupported object type: %d", (int) objtype);
2421 : }
2422 :
2423 0 : return "???"; /* keep compiler quiet */
2424 : }
|