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 128 : CreateEventTrigger(CreateEventTrigStmt *stmt)
127 : {
128 : HeapTuple tuple;
129 : Oid funcoid;
130 : Oid funcrettype;
131 128 : Oid evtowner = GetUserId();
132 : ListCell *lc;
133 128 : 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 128 : 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 124 : if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
149 67 : strcmp(stmt->eventname, "ddl_command_end") != 0 &&
150 42 : strcmp(stmt->eventname, "sql_drop") != 0 &&
151 20 : 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 181 : 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 112 : if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
177 63 : strcmp(stmt->eventname, "ddl_command_end") == 0 ||
178 38 : strcmp(stmt->eventname, "sql_drop") == 0)
179 96 : && tags != NULL)
180 57 : validate_ddl_tags("tag", tags);
181 55 : else if (strcmp(stmt->eventname, "table_rewrite") == 0
182 10 : && tags != NULL)
183 0 : validate_table_rewrite_tags("tag", tags);
184 55 : 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 88 : tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
194 88 : 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 88 : funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
202 88 : funcrettype = get_func_rettype(funcoid);
203 88 : 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 84 : 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 84 : 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 84 : tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
294 :
295 : /* Build the new pg_trigger tuple. */
296 84 : trigoid = GetNewOidWithIndex(tgrel, EventTriggerOidIndexId,
297 : Anum_pg_event_trigger_oid);
298 84 : values[Anum_pg_event_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
299 84 : memset(nulls, false, sizeof(nulls));
300 84 : namestrcpy(&evtnamedata, trigname);
301 84 : values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
302 84 : namestrcpy(&evteventdata, eventname);
303 84 : values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
304 84 : values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
305 84 : values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
306 84 : values[Anum_pg_event_trigger_evtenabled - 1] =
307 84 : CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
308 84 : if (taglist == NIL)
309 51 : 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 84 : tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
316 84 : CatalogTupleInsert(tgrel, tuple);
317 84 : 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 84 : if (strcmp(eventname, "login") == 0)
324 6 : SetDatabaseHasLoginEventTriggers();
325 :
326 : /* Depend on owner. */
327 84 : recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
328 :
329 : /* Depend on event trigger function. */
330 84 : myself.classId = EventTriggerRelationId;
331 84 : myself.objectId = trigoid;
332 84 : myself.objectSubId = 0;
333 84 : referenced.classId = ProcedureRelationId;
334 84 : referenced.objectId = funcoid;
335 84 : referenced.objectSubId = 0;
336 84 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
337 :
338 : /* Depend on extension, if any. */
339 84 : recordDependencyOnCurrentExtension(&myself, false);
340 :
341 : /* Post creation hook for new event trigger */
342 84 : InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
343 :
344 : /* Close pg_event_trigger. */
345 84 : table_close(tgrel, RowExclusiveLock);
346 :
347 84 : 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 12 : SetDatabaseHasLoginEventTriggers(void)
393 : {
394 : /* Set dathasloginevt flag in pg_database */
395 : Form_pg_database db;
396 12 : 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 12 : LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
408 :
409 12 : tuple = SearchSysCacheLockedCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
410 12 : if (!HeapTupleIsValid(tuple))
411 0 : elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
412 12 : otid = tuple->t_self;
413 12 : db = (Form_pg_database) GETSTRUCT(tuple);
414 12 : if (!db->dathasloginevt)
415 : {
416 6 : db->dathasloginevt = true;
417 6 : CatalogTupleUpdate(pg_db, &otid, tuple);
418 6 : CommandCounterIncrement();
419 : }
420 12 : UnlockTuple(pg_db, &otid, InplaceUpdateTupleLock);
421 12 : table_close(pg_db, RowExclusiveLock);
422 12 : heap_freetuple(tuple);
423 12 : }
424 :
425 : /*
426 : * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
427 : */
428 : Oid
429 31 : AlterEventTrigger(AlterEventTrigStmt *stmt)
430 : {
431 : Relation tgrel;
432 : HeapTuple tup;
433 : Oid trigoid;
434 : Form_pg_event_trigger evtForm;
435 31 : char tgenabled = stmt->tgenabled;
436 :
437 31 : tgrel = table_open(EventTriggerRelationId, RowExclusiveLock);
438 :
439 31 : tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
440 : CStringGetDatum(stmt->trigname));
441 31 : 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 31 : evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
448 31 : trigoid = evtForm->oid;
449 :
450 31 : 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 31 : evtForm->evtenabled = tgenabled;
456 :
457 31 : 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 31 : if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
464 : tgenabled != TRIGGER_DISABLED)
465 6 : SetDatabaseHasLoginEventTriggers();
466 :
467 31 : InvokeObjectPostAlterHook(EventTriggerRelationId,
468 : trigoid, 0);
469 :
470 : /* clean up */
471 31 : heap_freetuple(tup);
472 31 : table_close(tgrel, RowExclusiveLock);
473 :
474 31 : 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 111 : get_event_trigger_oid(const char *trigname, bool missing_ok)
582 : {
583 : Oid oid;
584 :
585 111 : oid = GetSysCacheOid1(EVENTTRIGGERNAME, Anum_pg_event_trigger_oid,
586 : CStringGetDatum(trigname));
587 111 : if (!OidIsValid(oid) && !missing_ok)
588 8 : ereport(ERROR,
589 : (errcode(ERRCODE_UNDEFINED_OBJECT),
590 : errmsg("event trigger \"%s\" does not exist", trigname)));
591 103 : 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 1586 : 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 1586 : 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 1550 : if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
614 0 : return false;
615 : }
616 :
617 : /* Filter by tags, if any were specified. */
618 1558 : if (!bms_is_empty(item->tagset) && !bms_is_member(tag, item->tagset))
619 347 : return false;
620 :
621 : /* if we reach that point, we're not filtering out this item */
622 1211 : return true;
623 : }
624 :
625 : static CommandTag
626 1386 : EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
627 : {
628 1386 : if (event == EVT_Login)
629 142 : 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 103337 : 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 103337 : 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 103337 : cachelist = EventCacheLookup(event);
687 103337 : if (cachelist == NIL)
688 101951 : return NIL;
689 :
690 : /* Get the command tag. */
691 1386 : 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 2972 : foreach(lc, cachelist)
701 : {
702 1586 : EventTriggerCacheItem *item = lfirst(lc);
703 :
704 1586 : if (unfiltered || filter_event_trigger(tag, item))
705 : {
706 : /* We must plan to fire this trigger. */
707 1211 : runlist = lappend_oid(runlist, item->fnoid);
708 : }
709 : }
710 :
711 : /* Don't spend any more time on this if no functions to run */
712 1386 : if (runlist == NIL)
713 311 : return NIL;
714 :
715 1075 : trigdata->type = T_EventTriggerData;
716 1075 : trigdata->event = eventstr;
717 1075 : trigdata->parsetree = parsetree;
718 1075 : trigdata->tag = tag;
719 :
720 1075 : return runlist;
721 : }
722 :
723 : /*
724 : * Fire ddl_command_start triggers.
725 : */
726 : void
727 137949 : 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 137949 : if (!IsUnderPostmaster || !event_triggers)
752 137725 : return;
753 :
754 100820 : runlist = EventTriggerCommonSetup(parsetree,
755 : EVT_DDLCommandStart,
756 : "ddl_command_start",
757 : &trigdata, false);
758 100820 : if (runlist == NIL)
759 100596 : return;
760 :
761 : /* Run the triggers. */
762 224 : EventTriggerInvoke(runlist, &trigdata);
763 :
764 : /* Cleanup. */
765 224 : list_free(runlist);
766 :
767 : /*
768 : * Make sure anything the event triggers did will be visible to the main
769 : * command.
770 : */
771 224 : CommandCounterIncrement();
772 : }
773 :
774 : /*
775 : * Fire ddl_command_end triggers.
776 : */
777 : void
778 128927 : 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 128927 : if (!IsUnderPostmaster || !event_triggers)
788 128390 : 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 91798 : if (!currentEventTriggerState)
801 89942 : return;
802 :
803 1856 : runlist = EventTriggerCommonSetup(parsetree,
804 : EVT_DDLCommandEnd, "ddl_command_end",
805 : &trigdata, false);
806 1856 : if (runlist == NIL)
807 1319 : return;
808 :
809 : /*
810 : * Make sure anything the main command did will be visible to the event
811 : * triggers.
812 : */
813 537 : CommandCounterIncrement();
814 :
815 : /* Run the triggers. */
816 537 : EventTriggerInvoke(runlist, &trigdata);
817 :
818 : /* Cleanup. */
819 537 : list_free(runlist);
820 : }
821 :
822 : /*
823 : * Fire sql_drop triggers.
824 : */
825 : void
826 128939 : 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 128939 : if (!IsUnderPostmaster || !event_triggers)
836 128855 : 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 91810 : if (!currentEventTriggerState ||
846 1868 : slist_is_empty(¤tEventTriggerState->SQLDropList))
847 91427 : return;
848 :
849 383 : 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 383 : if (runlist == NIL)
860 299 : 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 14448 : 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 14448 : if (!IsUnderPostmaster || !event_triggers ||
910 14374 : !OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
911 14302 : return;
912 :
913 146 : StartTransactionCommand();
914 146 : runlist = EventTriggerCommonSetup(NULL,
915 : EVT_Login, "login",
916 : &trigdata, false);
917 :
918 146 : if (runlist != NIL)
919 : {
920 : /*
921 : * Event trigger execution may require an active snapshot.
922 : */
923 142 : PushActiveSnapshot(GetTransactionSnapshot());
924 :
925 : /* Run the triggers. */
926 142 : EventTriggerInvoke(runlist, &trigdata);
927 :
928 : /* Cleanup. */
929 142 : list_free(runlist);
930 :
931 142 : 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 4 : else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
942 : 0, AccessExclusiveLock))
943 : {
944 : /*
945 : * The lock is held. Now we need to recheck that login event triggers
946 : * list is still empty. Once the list is empty, we know that even if
947 : * there is a backend which concurrently inserts/enables a login event
948 : * trigger, it will update pg_database.dathasloginevt *afterwards*.
949 : */
950 4 : runlist = EventTriggerCommonSetup(NULL,
951 : EVT_Login, "login",
952 : &trigdata, true);
953 :
954 4 : if (runlist == NIL)
955 : {
956 4 : Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
957 : HeapTuple tuple;
958 : void *state;
959 : Form_pg_database db;
960 : ScanKeyData key[1];
961 :
962 : /* Fetch a copy of the tuple to scribble on */
963 4 : ScanKeyInit(&key[0],
964 : Anum_pg_database_oid,
965 : BTEqualStrategyNumber, F_OIDEQ,
966 : ObjectIdGetDatum(MyDatabaseId));
967 :
968 4 : systable_inplace_update_begin(pg_db, DatabaseOidIndexId, true,
969 : NULL, 1, key, &tuple, &state);
970 :
971 4 : if (!HeapTupleIsValid(tuple))
972 0 : elog(ERROR, "could not find tuple for database %u", MyDatabaseId);
973 :
974 4 : db = (Form_pg_database) GETSTRUCT(tuple);
975 4 : if (db->dathasloginevt)
976 : {
977 4 : db->dathasloginevt = false;
978 :
979 : /*
980 : * Do an "in place" update of the pg_database tuple. Doing
981 : * this instead of regular updates serves two purposes. First,
982 : * that avoids possible waiting on the row-level lock. Second,
983 : * that avoids dealing with TOAST.
984 : */
985 4 : systable_inplace_update_finish(state, tuple);
986 : }
987 : else
988 0 : systable_inplace_update_cancel(state);
989 4 : table_close(pg_db, RowExclusiveLock);
990 4 : heap_freetuple(tuple);
991 : }
992 : else
993 : {
994 0 : list_free(runlist);
995 : }
996 : }
997 146 : CommitTransactionCommand();
998 : }
999 :
1000 :
1001 : /*
1002 : * Fire table_rewrite triggers.
1003 : */
1004 : void
1005 795 : EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
1006 : {
1007 : List *runlist;
1008 : EventTriggerData trigdata;
1009 :
1010 : /*
1011 : * See EventTriggerDDLCommandStart for a discussion about why event
1012 : * triggers are disabled in single user mode or via a GUC.
1013 : */
1014 795 : if (!IsUnderPostmaster || !event_triggers)
1015 707 : return;
1016 :
1017 : /*
1018 : * Also do nothing if our state isn't set up, which it won't be if there
1019 : * weren't any relevant event triggers at the start of the current DDL
1020 : * command. This test might therefore seem optional, but it's
1021 : * *necessary*, because EventTriggerCommonSetup might find triggers that
1022 : * didn't exist at the time the command started.
1023 : */
1024 795 : if (!currentEventTriggerState)
1025 667 : return;
1026 :
1027 128 : runlist = EventTriggerCommonSetup(parsetree,
1028 : EVT_TableRewrite,
1029 : "table_rewrite",
1030 : &trigdata, false);
1031 128 : if (runlist == NIL)
1032 40 : return;
1033 :
1034 : /*
1035 : * Make sure pg_event_trigger_table_rewrite_oid only works when running
1036 : * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even
1037 : * when one trigger fails. (This is perhaps not necessary, as the
1038 : * currentState variable will be removed shortly by our caller, but it
1039 : * seems better to play safe.)
1040 : */
1041 88 : currentEventTriggerState->table_rewrite_oid = tableOid;
1042 88 : currentEventTriggerState->table_rewrite_reason = reason;
1043 :
1044 : /* Run the triggers. */
1045 88 : PG_TRY();
1046 : {
1047 88 : EventTriggerInvoke(runlist, &trigdata);
1048 : }
1049 4 : PG_FINALLY();
1050 : {
1051 88 : currentEventTriggerState->table_rewrite_oid = InvalidOid;
1052 88 : currentEventTriggerState->table_rewrite_reason = 0;
1053 : }
1054 88 : PG_END_TRY();
1055 :
1056 : /* Cleanup. */
1057 84 : list_free(runlist);
1058 :
1059 : /*
1060 : * Make sure anything the event triggers did will be visible to the main
1061 : * command.
1062 : */
1063 84 : CommandCounterIncrement();
1064 : }
1065 :
1066 : /*
1067 : * Invoke each event trigger in a list of event triggers.
1068 : */
1069 : static void
1070 1075 : EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
1071 : {
1072 : MemoryContext context;
1073 : MemoryContext oldcontext;
1074 : ListCell *lc;
1075 1075 : bool first = true;
1076 :
1077 : /* Guard against stack overflow due to recursive event trigger */
1078 1075 : check_stack_depth();
1079 :
1080 : /*
1081 : * Let's evaluate event triggers in their own memory context, so that any
1082 : * leaks get cleaned up promptly.
1083 : */
1084 1075 : context = AllocSetContextCreate(CurrentMemoryContext,
1085 : "event trigger context",
1086 : ALLOCSET_DEFAULT_SIZES);
1087 1075 : oldcontext = MemoryContextSwitchTo(context);
1088 :
1089 : /* Call each event trigger. */
1090 2266 : foreach(lc, fn_oid_list)
1091 : {
1092 1207 : LOCAL_FCINFO(fcinfo, 0);
1093 1207 : Oid fnoid = lfirst_oid(lc);
1094 : FmgrInfo flinfo;
1095 : PgStat_FunctionCallUsage fcusage;
1096 :
1097 1207 : elog(DEBUG1, "EventTriggerInvoke %u", fnoid);
1098 :
1099 : /*
1100 : * We want each event trigger to be able to see the results of the
1101 : * previous event trigger's action. Caller is responsible for any
1102 : * command-counter increment that is needed between the event trigger
1103 : * and anything else in the transaction.
1104 : */
1105 1207 : if (first)
1106 1075 : first = false;
1107 : else
1108 132 : CommandCounterIncrement();
1109 :
1110 : /* Look up the function */
1111 1207 : fmgr_info(fnoid, &flinfo);
1112 :
1113 : /* Call the function, passing no arguments but setting a context. */
1114 1207 : InitFunctionCallInfoData(*fcinfo, &flinfo, 0,
1115 : InvalidOid, (Node *) trigdata, NULL);
1116 1207 : pgstat_init_function_usage(fcinfo, &fcusage);
1117 1207 : FunctionCallInvoke(fcinfo);
1118 1191 : pgstat_end_function_usage(&fcusage, true);
1119 :
1120 : /* Reclaim memory. */
1121 1191 : MemoryContextReset(context);
1122 : }
1123 :
1124 : /* Restore old memory context and delete the temporary one. */
1125 1059 : MemoryContextSwitchTo(oldcontext);
1126 1059 : MemoryContextDelete(context);
1127 1059 : }
1128 :
1129 : /*
1130 : * Do event triggers support this object type?
1131 : *
1132 : * See also event trigger documentation in event-trigger.sgml.
1133 : */
1134 : bool
1135 49019 : EventTriggerSupportsObjectType(ObjectType obtype)
1136 : {
1137 49019 : switch (obtype)
1138 : {
1139 808 : case OBJECT_DATABASE:
1140 : case OBJECT_TABLESPACE:
1141 : case OBJECT_ROLE:
1142 : case OBJECT_PARAMETER_ACL:
1143 : /* no support for global objects (except subscriptions) */
1144 808 : return false;
1145 101 : case OBJECT_EVENT_TRIGGER:
1146 : /* no support for event triggers on event triggers */
1147 101 : return false;
1148 48110 : default:
1149 48110 : return true;
1150 : }
1151 : }
1152 :
1153 : /*
1154 : * Do event triggers support this object class?
1155 : *
1156 : * See also event trigger documentation in event-trigger.sgml.
1157 : */
1158 : bool
1159 2650 : EventTriggerSupportsObject(const ObjectAddress *object)
1160 : {
1161 2650 : switch (object->classId)
1162 : {
1163 0 : case DatabaseRelationId:
1164 : case TableSpaceRelationId:
1165 : case AuthIdRelationId:
1166 : case AuthMemRelationId:
1167 : case ParameterAclRelationId:
1168 : /* no support for global objects (except subscriptions) */
1169 0 : return false;
1170 74 : case EventTriggerRelationId:
1171 : /* no support for event triggers on event triggers */
1172 74 : return false;
1173 2576 : default:
1174 2576 : return true;
1175 : }
1176 : }
1177 :
1178 : /*
1179 : * Prepare event trigger state for a new complete query to run, if necessary;
1180 : * returns whether this was done. If it was, EventTriggerEndCompleteQuery must
1181 : * be called when the query is done, regardless of whether it succeeds or fails
1182 : * -- so use of a PG_TRY block is mandatory.
1183 : */
1184 : bool
1185 137949 : EventTriggerBeginCompleteQuery(void)
1186 : {
1187 : EventTriggerQueryState *state;
1188 : MemoryContext cxt;
1189 :
1190 : /*
1191 : * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
1192 : * reason to have event trigger state at all; so if there are none, don't
1193 : * install one.
1194 : */
1195 137949 : if (!trackDroppedObjectsNeeded())
1196 135959 : return false;
1197 :
1198 1990 : cxt = AllocSetContextCreate(TopMemoryContext,
1199 : "event trigger state",
1200 : ALLOCSET_DEFAULT_SIZES);
1201 1990 : state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
1202 1990 : state->cxt = cxt;
1203 1990 : slist_init(&(state->SQLDropList));
1204 1990 : state->in_sql_drop = false;
1205 1990 : state->table_rewrite_oid = InvalidOid;
1206 :
1207 3980 : state->commandCollectionInhibited = currentEventTriggerState ?
1208 1990 : currentEventTriggerState->commandCollectionInhibited : false;
1209 1990 : state->currentCommand = NULL;
1210 1990 : state->commandList = NIL;
1211 1990 : state->previous = currentEventTriggerState;
1212 1990 : currentEventTriggerState = state;
1213 :
1214 1990 : return true;
1215 : }
1216 :
1217 : /*
1218 : * Query completed (or errored out) -- clean up local state, return to previous
1219 : * one.
1220 : *
1221 : * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
1222 : * returned false previously.
1223 : *
1224 : * Note: this might be called in the PG_CATCH block of a failing transaction,
1225 : * so be wary of running anything unnecessary. (In particular, it's probably
1226 : * unwise to try to allocate memory.)
1227 : */
1228 : void
1229 1990 : EventTriggerEndCompleteQuery(void)
1230 : {
1231 : EventTriggerQueryState *prevstate;
1232 :
1233 1990 : prevstate = currentEventTriggerState->previous;
1234 :
1235 : /* this avoids the need for retail pfree of SQLDropList items: */
1236 1990 : MemoryContextDelete(currentEventTriggerState->cxt);
1237 :
1238 1990 : currentEventTriggerState = prevstate;
1239 1990 : }
1240 :
1241 : /*
1242 : * Do we need to keep close track of objects being dropped?
1243 : *
1244 : * This is useful because there is a cost to running with them enabled.
1245 : */
1246 : bool
1247 160134 : trackDroppedObjectsNeeded(void)
1248 : {
1249 : /*
1250 : * true if any sql_drop, table_rewrite, ddl_command_end event trigger
1251 : * exists
1252 : */
1253 318096 : return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
1254 318096 : (EventCacheLookup(EVT_TableRewrite) != NIL) ||
1255 157845 : (EventCacheLookup(EVT_DDLCommandEnd) != NIL);
1256 : }
1257 :
1258 : /*
1259 : * Support for dropped objects information on event trigger functions.
1260 : *
1261 : * We keep the list of objects dropped by the current command in current
1262 : * state's SQLDropList (comprising SQLDropObject items). Each time a new
1263 : * command is to start, a clean EventTriggerQueryState is created; commands
1264 : * that drop objects do the dependency.c dance to drop objects, which
1265 : * populates the current state's SQLDropList; when the event triggers are
1266 : * invoked they can consume the list via pg_event_trigger_dropped_objects().
1267 : * When the command finishes, the EventTriggerQueryState is cleared, and
1268 : * the one from the previous command is restored (when no command is in
1269 : * execution, the current state is NULL).
1270 : *
1271 : * All this lets us support the case that an event trigger function drops
1272 : * objects "reentrantly".
1273 : */
1274 :
1275 : /*
1276 : * Register one object as being dropped by the current command.
1277 : */
1278 : void
1279 2719 : EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
1280 : {
1281 : SQLDropObject *obj;
1282 : MemoryContext oldcxt;
1283 :
1284 2719 : if (!currentEventTriggerState)
1285 143 : return;
1286 :
1287 : Assert(EventTriggerSupportsObject(object));
1288 :
1289 2576 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1290 :
1291 2576 : obj = palloc0_object(SQLDropObject);
1292 2576 : obj->address = *object;
1293 2576 : obj->original = original;
1294 2576 : obj->normal = normal;
1295 :
1296 2576 : if (object->classId == NamespaceRelationId)
1297 : {
1298 : /* Special handling is needed for temp namespaces */
1299 43 : if (isTempNamespace(object->objectId))
1300 0 : obj->istemp = true;
1301 43 : else if (isAnyTempNamespace(object->objectId))
1302 : {
1303 : /* don't report temp schemas except my own */
1304 0 : pfree(obj);
1305 0 : MemoryContextSwitchTo(oldcxt);
1306 0 : return;
1307 : }
1308 43 : obj->objname = get_namespace_name(object->objectId);
1309 : }
1310 2533 : else if (object->classId == AttrDefaultRelationId)
1311 : {
1312 : /* We treat a column default as temp if its table is temp */
1313 : ObjectAddress colobject;
1314 :
1315 286 : colobject = GetAttrDefaultColumnAddress(object->objectId);
1316 286 : if (OidIsValid(colobject.objectId))
1317 : {
1318 286 : if (!obtain_object_name_namespace(&colobject, obj))
1319 : {
1320 0 : pfree(obj);
1321 0 : MemoryContextSwitchTo(oldcxt);
1322 0 : return;
1323 : }
1324 : }
1325 : }
1326 2247 : else if (object->classId == TriggerRelationId)
1327 : {
1328 : /* Similarly, a trigger is temp if its table is temp */
1329 : /* Sadly, there's no lsyscache.c support for trigger objects */
1330 : Relation pg_trigger_rel;
1331 : ScanKeyData skey[1];
1332 : SysScanDesc sscan;
1333 : HeapTuple tuple;
1334 : Oid relid;
1335 :
1336 : /* Fetch the trigger's table OID the hard way */
1337 72 : pg_trigger_rel = table_open(TriggerRelationId, AccessShareLock);
1338 72 : ScanKeyInit(&skey[0],
1339 : Anum_pg_trigger_oid,
1340 : BTEqualStrategyNumber, F_OIDEQ,
1341 72 : ObjectIdGetDatum(object->objectId));
1342 72 : sscan = systable_beginscan(pg_trigger_rel, TriggerOidIndexId, true,
1343 : NULL, 1, skey);
1344 72 : tuple = systable_getnext(sscan);
1345 72 : if (HeapTupleIsValid(tuple))
1346 72 : relid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid;
1347 : else
1348 0 : relid = InvalidOid; /* shouldn't happen */
1349 72 : systable_endscan(sscan);
1350 72 : table_close(pg_trigger_rel, AccessShareLock);
1351 : /* Do nothing if we didn't find the trigger */
1352 72 : if (OidIsValid(relid))
1353 : {
1354 : ObjectAddress relobject;
1355 :
1356 72 : relobject.classId = RelationRelationId;
1357 72 : relobject.objectId = relid;
1358 : /* Arbitrarily set objectSubId nonzero so as not to fill objname */
1359 72 : relobject.objectSubId = 1;
1360 72 : if (!obtain_object_name_namespace(&relobject, obj))
1361 : {
1362 0 : pfree(obj);
1363 0 : MemoryContextSwitchTo(oldcxt);
1364 0 : return;
1365 : }
1366 : }
1367 : }
1368 2175 : else if (object->classId == PolicyRelationId)
1369 : {
1370 : /* Similarly, a policy is temp if its table is temp */
1371 : /* Sadly, there's no lsyscache.c support for policy objects */
1372 : Relation pg_policy_rel;
1373 : ScanKeyData skey[1];
1374 : SysScanDesc sscan;
1375 : HeapTuple tuple;
1376 : Oid relid;
1377 :
1378 : /* Fetch the policy's table OID the hard way */
1379 20 : pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);
1380 20 : ScanKeyInit(&skey[0],
1381 : Anum_pg_policy_oid,
1382 : BTEqualStrategyNumber, F_OIDEQ,
1383 20 : ObjectIdGetDatum(object->objectId));
1384 20 : sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
1385 : NULL, 1, skey);
1386 20 : tuple = systable_getnext(sscan);
1387 20 : if (HeapTupleIsValid(tuple))
1388 20 : relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
1389 : else
1390 0 : relid = InvalidOid; /* shouldn't happen */
1391 20 : systable_endscan(sscan);
1392 20 : table_close(pg_policy_rel, AccessShareLock);
1393 : /* Do nothing if we didn't find the policy */
1394 20 : if (OidIsValid(relid))
1395 : {
1396 : ObjectAddress relobject;
1397 :
1398 20 : relobject.classId = RelationRelationId;
1399 20 : relobject.objectId = relid;
1400 : /* Arbitrarily set objectSubId nonzero so as not to fill objname */
1401 20 : relobject.objectSubId = 1;
1402 20 : if (!obtain_object_name_namespace(&relobject, obj))
1403 : {
1404 0 : pfree(obj);
1405 0 : MemoryContextSwitchTo(oldcxt);
1406 0 : return;
1407 : }
1408 : }
1409 : }
1410 : else
1411 : {
1412 : /* Generic handling for all other object classes */
1413 2155 : if (!obtain_object_name_namespace(object, obj))
1414 : {
1415 : /* don't report temp objects except my own */
1416 0 : pfree(obj);
1417 0 : MemoryContextSwitchTo(oldcxt);
1418 0 : return;
1419 : }
1420 : }
1421 :
1422 : /* object identity, objname and objargs */
1423 2576 : obj->objidentity =
1424 2576 : getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs,
1425 : false);
1426 :
1427 : /* object type */
1428 2576 : obj->objecttype = getObjectTypeDescription(&obj->address, false);
1429 :
1430 2576 : slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
1431 :
1432 2576 : MemoryContextSwitchTo(oldcxt);
1433 : }
1434 :
1435 : /*
1436 : * Fill obj->objname, obj->schemaname, and obj->istemp based on object.
1437 : *
1438 : * Returns true if this object should be reported, false if it should
1439 : * be ignored because it is a temporary object of another session.
1440 : */
1441 : static bool
1442 2533 : obtain_object_name_namespace(const ObjectAddress *object, SQLDropObject *obj)
1443 : {
1444 : /*
1445 : * Obtain schema names from the object's catalog tuple, if one exists;
1446 : * this lets us skip objects in temp schemas. We trust that
1447 : * ObjectProperty contains all object classes that can be
1448 : * schema-qualified.
1449 : *
1450 : * Currently, this function does nothing for object classes that are not
1451 : * in ObjectProperty, but we might sometime add special cases for that.
1452 : */
1453 2533 : if (is_objectclass_supported(object->classId))
1454 : {
1455 : Relation catalog;
1456 : HeapTuple tuple;
1457 :
1458 2533 : catalog = table_open(object->classId, AccessShareLock);
1459 2533 : tuple = get_catalog_object_by_oid(catalog,
1460 2533 : get_object_attnum_oid(object->classId),
1461 2533 : object->objectId);
1462 :
1463 2533 : if (tuple)
1464 : {
1465 : AttrNumber attnum;
1466 : Datum datum;
1467 : bool isnull;
1468 :
1469 2533 : attnum = get_object_attnum_namespace(object->classId);
1470 2533 : if (attnum != InvalidAttrNumber)
1471 : {
1472 2499 : datum = heap_getattr(tuple, attnum,
1473 : RelationGetDescr(catalog), &isnull);
1474 2499 : if (!isnull)
1475 : {
1476 : Oid namespaceId;
1477 :
1478 2499 : namespaceId = DatumGetObjectId(datum);
1479 : /* temp objects are only reported if they are my own */
1480 2499 : if (isTempNamespace(namespaceId))
1481 : {
1482 48 : obj->schemaname = "pg_temp";
1483 48 : obj->istemp = true;
1484 : }
1485 2451 : else if (isAnyTempNamespace(namespaceId))
1486 : {
1487 : /* no need to fill any fields of *obj */
1488 0 : table_close(catalog, AccessShareLock);
1489 0 : return false;
1490 : }
1491 : else
1492 : {
1493 2451 : obj->schemaname = get_namespace_name(namespaceId);
1494 2451 : obj->istemp = false;
1495 : }
1496 : }
1497 : }
1498 :
1499 2533 : if (get_object_namensp_unique(object->classId) &&
1500 2077 : object->objectSubId == 0)
1501 : {
1502 1679 : attnum = get_object_attnum_name(object->classId);
1503 1679 : if (attnum != InvalidAttrNumber)
1504 : {
1505 1679 : datum = heap_getattr(tuple, attnum,
1506 : RelationGetDescr(catalog), &isnull);
1507 1679 : if (!isnull)
1508 1679 : obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
1509 : }
1510 : }
1511 : }
1512 :
1513 2533 : table_close(catalog, AccessShareLock);
1514 : }
1515 :
1516 2533 : return true;
1517 : }
1518 :
1519 : /*
1520 : * pg_event_trigger_dropped_objects
1521 : *
1522 : * Make the list of dropped objects available to the user function run by the
1523 : * Event Trigger.
1524 : */
1525 : Datum
1526 96 : pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
1527 : {
1528 96 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1529 : slist_iter iter;
1530 :
1531 : /*
1532 : * Protect this function from being called out of context
1533 : */
1534 96 : if (!currentEventTriggerState ||
1535 96 : !currentEventTriggerState->in_sql_drop)
1536 0 : ereport(ERROR,
1537 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1538 : errmsg("%s can only be called in a sql_drop event trigger function",
1539 : "pg_event_trigger_dropped_objects()")));
1540 :
1541 : /* Build tuplestore to hold the result rows */
1542 96 : InitMaterializedSRF(fcinfo, 0);
1543 :
1544 928 : slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
1545 : {
1546 : SQLDropObject *obj;
1547 832 : int i = 0;
1548 832 : Datum values[12] = {0};
1549 832 : bool nulls[12] = {0};
1550 :
1551 832 : obj = slist_container(SQLDropObject, next, iter.cur);
1552 :
1553 : /* classid */
1554 832 : values[i++] = ObjectIdGetDatum(obj->address.classId);
1555 :
1556 : /* objid */
1557 832 : values[i++] = ObjectIdGetDatum(obj->address.objectId);
1558 :
1559 : /* objsubid */
1560 832 : values[i++] = Int32GetDatum(obj->address.objectSubId);
1561 :
1562 : /* original */
1563 832 : values[i++] = BoolGetDatum(obj->original);
1564 :
1565 : /* normal */
1566 832 : values[i++] = BoolGetDatum(obj->normal);
1567 :
1568 : /* is_temporary */
1569 832 : values[i++] = BoolGetDatum(obj->istemp);
1570 :
1571 : /* object_type */
1572 832 : values[i++] = CStringGetTextDatum(obj->objecttype);
1573 :
1574 : /* schema_name */
1575 832 : if (obj->schemaname)
1576 780 : values[i++] = CStringGetTextDatum(obj->schemaname);
1577 : else
1578 52 : nulls[i++] = true;
1579 :
1580 : /* object_name */
1581 832 : if (obj->objname)
1582 628 : values[i++] = CStringGetTextDatum(obj->objname);
1583 : else
1584 204 : nulls[i++] = true;
1585 :
1586 : /* object_identity */
1587 832 : if (obj->objidentity)
1588 832 : values[i++] = CStringGetTextDatum(obj->objidentity);
1589 : else
1590 0 : nulls[i++] = true;
1591 :
1592 : /* address_names and address_args */
1593 832 : if (obj->addrnames)
1594 : {
1595 832 : values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
1596 :
1597 832 : if (obj->addrargs)
1598 40 : values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
1599 : else
1600 792 : values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
1601 : }
1602 : else
1603 : {
1604 0 : nulls[i++] = true;
1605 0 : nulls[i++] = true;
1606 : }
1607 :
1608 832 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1609 : values, nulls);
1610 : }
1611 :
1612 96 : return (Datum) 0;
1613 : }
1614 :
1615 : /*
1616 : * pg_event_trigger_table_rewrite_oid
1617 : *
1618 : * Make the Oid of the table going to be rewritten available to the user
1619 : * function run by the Event Trigger.
1620 : */
1621 : Datum
1622 149 : pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
1623 : {
1624 : /*
1625 : * Protect this function from being called out of context
1626 : */
1627 149 : if (!currentEventTriggerState ||
1628 145 : currentEventTriggerState->table_rewrite_oid == InvalidOid)
1629 4 : ereport(ERROR,
1630 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1631 : errmsg("%s can only be called in a table_rewrite event trigger function",
1632 : "pg_event_trigger_table_rewrite_oid()")));
1633 :
1634 145 : PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
1635 : }
1636 :
1637 : /*
1638 : * pg_event_trigger_table_rewrite_reason
1639 : *
1640 : * Make the rewrite reason available to the user.
1641 : */
1642 : Datum
1643 84 : pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
1644 : {
1645 : /*
1646 : * Protect this function from being called out of context
1647 : */
1648 84 : if (!currentEventTriggerState ||
1649 84 : currentEventTriggerState->table_rewrite_reason == 0)
1650 0 : ereport(ERROR,
1651 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1652 : errmsg("%s can only be called in a table_rewrite event trigger function",
1653 : "pg_event_trigger_table_rewrite_reason()")));
1654 :
1655 84 : PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
1656 : }
1657 :
1658 : /*-------------------------------------------------------------------------
1659 : * Support for DDL command deparsing
1660 : *
1661 : * The routines below enable an event trigger function to obtain a list of
1662 : * DDL commands as they are executed. There are three main pieces to this
1663 : * feature:
1664 : *
1665 : * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
1666 : * adds a struct CollectedCommand representation of itself to the command list,
1667 : * using the routines below.
1668 : *
1669 : * 2) Some time after that, ddl_command_end fires and the command list is made
1670 : * available to the event trigger function via pg_event_trigger_ddl_commands();
1671 : * the complete command details are exposed as a column of type pg_ddl_command.
1672 : *
1673 : * 3) An extension can install a function capable of taking a value of type
1674 : * pg_ddl_command and transform it into some external, user-visible and/or
1675 : * -modifiable representation.
1676 : *-------------------------------------------------------------------------
1677 : */
1678 :
1679 : /*
1680 : * Inhibit DDL command collection.
1681 : */
1682 : void
1683 174 : EventTriggerInhibitCommandCollection(void)
1684 : {
1685 174 : if (!currentEventTriggerState)
1686 169 : return;
1687 :
1688 5 : currentEventTriggerState->commandCollectionInhibited = true;
1689 : }
1690 :
1691 : /*
1692 : * Re-establish DDL command collection.
1693 : */
1694 : void
1695 174 : EventTriggerUndoInhibitCommandCollection(void)
1696 : {
1697 174 : if (!currentEventTriggerState)
1698 169 : return;
1699 :
1700 5 : currentEventTriggerState->commandCollectionInhibited = false;
1701 : }
1702 :
1703 : /*
1704 : * EventTriggerCollectSimpleCommand
1705 : * Save data about a simple DDL command that was just executed
1706 : *
1707 : * address identifies the object being operated on. secondaryObject is an
1708 : * object address that was related in some way to the executed command; its
1709 : * meaning is command-specific.
1710 : *
1711 : * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
1712 : * object being moved, objectId is its OID, and secondaryOid is the OID of the
1713 : * old schema. (The destination schema OID can be obtained by catalog lookup
1714 : * of the object.)
1715 : */
1716 : void
1717 85499 : EventTriggerCollectSimpleCommand(ObjectAddress address,
1718 : ObjectAddress secondaryObject,
1719 : const Node *parsetree)
1720 : {
1721 : MemoryContext oldcxt;
1722 : CollectedCommand *command;
1723 :
1724 : /* ignore if event trigger context not set, or collection disabled */
1725 85499 : if (!currentEventTriggerState ||
1726 1120 : currentEventTriggerState->commandCollectionInhibited)
1727 84379 : return;
1728 :
1729 1120 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1730 :
1731 1120 : command = palloc_object(CollectedCommand);
1732 :
1733 1120 : command->type = SCT_Simple;
1734 1120 : command->in_extension = creating_extension;
1735 :
1736 1120 : command->d.simple.address = address;
1737 1120 : command->d.simple.secondaryObject = secondaryObject;
1738 1120 : command->parsetree = copyObject(parsetree);
1739 :
1740 1120 : currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
1741 : command);
1742 :
1743 1120 : MemoryContextSwitchTo(oldcxt);
1744 : }
1745 :
1746 : /*
1747 : * EventTriggerAlterTableStart
1748 : * Prepare to receive data on an ALTER TABLE command about to be executed
1749 : *
1750 : * Note we don't collect the command immediately; instead we keep it in
1751 : * currentCommand, and only when we're done processing the subcommands we will
1752 : * add it to the command list.
1753 : */
1754 : void
1755 41545 : EventTriggerAlterTableStart(const Node *parsetree)
1756 : {
1757 : MemoryContext oldcxt;
1758 : CollectedCommand *command;
1759 :
1760 : /* ignore if event trigger context not set, or collection disabled */
1761 41545 : if (!currentEventTriggerState ||
1762 837 : currentEventTriggerState->commandCollectionInhibited)
1763 40708 : return;
1764 :
1765 837 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1766 :
1767 837 : command = palloc_object(CollectedCommand);
1768 :
1769 837 : command->type = SCT_AlterTable;
1770 837 : command->in_extension = creating_extension;
1771 :
1772 837 : command->d.alterTable.classId = RelationRelationId;
1773 837 : command->d.alterTable.objectId = InvalidOid;
1774 837 : command->d.alterTable.subcmds = NIL;
1775 837 : command->parsetree = copyObject(parsetree);
1776 :
1777 837 : command->parent = currentEventTriggerState->currentCommand;
1778 837 : currentEventTriggerState->currentCommand = command;
1779 :
1780 837 : MemoryContextSwitchTo(oldcxt);
1781 : }
1782 :
1783 : /*
1784 : * Remember the OID of the object being affected by an ALTER TABLE.
1785 : *
1786 : * This is needed because in some cases we don't know the OID until later.
1787 : */
1788 : void
1789 21929 : EventTriggerAlterTableRelid(Oid objectId)
1790 : {
1791 21929 : if (!currentEventTriggerState ||
1792 634 : currentEventTriggerState->commandCollectionInhibited)
1793 21295 : return;
1794 :
1795 634 : currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
1796 : }
1797 :
1798 : /*
1799 : * EventTriggerCollectAlterTableSubcmd
1800 : * Save data about a single part of an ALTER TABLE.
1801 : *
1802 : * Several different commands go through this path, but apart from ALTER TABLE
1803 : * itself, they are all concerned with AlterTableCmd nodes that are generated
1804 : * internally, so that's all that this code needs to handle at the moment.
1805 : */
1806 : void
1807 26301 : EventTriggerCollectAlterTableSubcmd(const Node *subcmd, ObjectAddress address)
1808 : {
1809 : MemoryContext oldcxt;
1810 : CollectedATSubcmd *newsub;
1811 :
1812 : /* ignore if event trigger context not set, or collection disabled */
1813 26301 : if (!currentEventTriggerState ||
1814 994 : currentEventTriggerState->commandCollectionInhibited)
1815 25307 : return;
1816 :
1817 : Assert(IsA(subcmd, AlterTableCmd));
1818 : Assert(currentEventTriggerState->currentCommand != NULL);
1819 : Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
1820 :
1821 994 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1822 :
1823 994 : newsub = palloc_object(CollectedATSubcmd);
1824 994 : newsub->address = address;
1825 994 : newsub->parsetree = copyObject(subcmd);
1826 :
1827 1988 : currentEventTriggerState->currentCommand->d.alterTable.subcmds =
1828 994 : lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
1829 :
1830 994 : MemoryContextSwitchTo(oldcxt);
1831 : }
1832 :
1833 : /*
1834 : * EventTriggerAlterTableEnd
1835 : * Finish up saving an ALTER TABLE command, and add it to command list.
1836 : *
1837 : * FIXME this API isn't considering the possibility that an xact/subxact is
1838 : * aborted partway through. Probably it's best to add an
1839 : * AtEOSubXact_EventTriggers() to fix this.
1840 : */
1841 : void
1842 38160 : EventTriggerAlterTableEnd(void)
1843 : {
1844 : CollectedCommand *parent;
1845 :
1846 : /* ignore if event trigger context not set, or collection disabled */
1847 38160 : if (!currentEventTriggerState ||
1848 801 : currentEventTriggerState->commandCollectionInhibited)
1849 37359 : return;
1850 :
1851 801 : parent = currentEventTriggerState->currentCommand->parent;
1852 :
1853 : /* If no subcommands, don't collect */
1854 801 : if (currentEventTriggerState->currentCommand->d.alterTable.subcmds != NIL)
1855 : {
1856 : MemoryContext oldcxt;
1857 :
1858 588 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1859 :
1860 1176 : currentEventTriggerState->commandList =
1861 588 : lappend(currentEventTriggerState->commandList,
1862 588 : currentEventTriggerState->currentCommand);
1863 :
1864 588 : MemoryContextSwitchTo(oldcxt);
1865 : }
1866 : else
1867 213 : pfree(currentEventTriggerState->currentCommand);
1868 :
1869 801 : currentEventTriggerState->currentCommand = parent;
1870 : }
1871 :
1872 : /*
1873 : * EventTriggerCollectGrant
1874 : * Save data about a GRANT/REVOKE command being executed
1875 : *
1876 : * This function creates a copy of the InternalGrant, as the original might
1877 : * not have the right lifetime.
1878 : */
1879 : void
1880 12224 : EventTriggerCollectGrant(InternalGrant *istmt)
1881 : {
1882 : MemoryContext oldcxt;
1883 : CollectedCommand *command;
1884 : InternalGrant *icopy;
1885 : ListCell *cell;
1886 :
1887 : /* ignore if event trigger context not set, or collection disabled */
1888 12224 : if (!currentEventTriggerState ||
1889 44 : currentEventTriggerState->commandCollectionInhibited)
1890 12180 : return;
1891 :
1892 44 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1893 :
1894 : /*
1895 : * This is tedious, but necessary.
1896 : */
1897 44 : icopy = palloc_object(InternalGrant);
1898 44 : memcpy(icopy, istmt, sizeof(InternalGrant));
1899 44 : icopy->objects = list_copy(istmt->objects);
1900 44 : icopy->grantees = list_copy(istmt->grantees);
1901 44 : icopy->col_privs = NIL;
1902 44 : foreach(cell, istmt->col_privs)
1903 0 : icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
1904 :
1905 : /* Now collect it, using the copied InternalGrant */
1906 44 : command = palloc_object(CollectedCommand);
1907 44 : command->type = SCT_Grant;
1908 44 : command->in_extension = creating_extension;
1909 44 : command->d.grant.istmt = icopy;
1910 44 : command->parsetree = NULL;
1911 :
1912 88 : currentEventTriggerState->commandList =
1913 44 : lappend(currentEventTriggerState->commandList, command);
1914 :
1915 44 : MemoryContextSwitchTo(oldcxt);
1916 : }
1917 :
1918 : /*
1919 : * EventTriggerCollectAlterOpFam
1920 : * Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
1921 : * executed
1922 : */
1923 : void
1924 168 : EventTriggerCollectAlterOpFam(const AlterOpFamilyStmt *stmt, Oid opfamoid,
1925 : List *operators, List *procedures)
1926 : {
1927 : MemoryContext oldcxt;
1928 : CollectedCommand *command;
1929 :
1930 : /* ignore if event trigger context not set, or collection disabled */
1931 168 : if (!currentEventTriggerState ||
1932 1 : currentEventTriggerState->commandCollectionInhibited)
1933 167 : return;
1934 :
1935 1 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1936 :
1937 1 : command = palloc_object(CollectedCommand);
1938 1 : command->type = SCT_AlterOpFamily;
1939 1 : command->in_extension = creating_extension;
1940 1 : ObjectAddressSet(command->d.opfam.address,
1941 : OperatorFamilyRelationId, opfamoid);
1942 1 : command->d.opfam.operators = operators;
1943 1 : command->d.opfam.procedures = procedures;
1944 1 : command->parsetree = (Node *) copyObject(stmt);
1945 :
1946 2 : currentEventTriggerState->commandList =
1947 1 : lappend(currentEventTriggerState->commandList, command);
1948 :
1949 1 : MemoryContextSwitchTo(oldcxt);
1950 : }
1951 :
1952 : /*
1953 : * EventTriggerCollectCreateOpClass
1954 : * Save data about a CREATE OPERATOR CLASS command being executed
1955 : */
1956 : void
1957 295 : EventTriggerCollectCreateOpClass(const CreateOpClassStmt *stmt, Oid opcoid,
1958 : List *operators, List *procedures)
1959 : {
1960 : MemoryContext oldcxt;
1961 : CollectedCommand *command;
1962 :
1963 : /* ignore if event trigger context not set, or collection disabled */
1964 295 : if (!currentEventTriggerState ||
1965 5 : currentEventTriggerState->commandCollectionInhibited)
1966 290 : return;
1967 :
1968 5 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1969 :
1970 5 : command = palloc0_object(CollectedCommand);
1971 5 : command->type = SCT_CreateOpClass;
1972 5 : command->in_extension = creating_extension;
1973 5 : ObjectAddressSet(command->d.createopc.address,
1974 : OperatorClassRelationId, opcoid);
1975 5 : command->d.createopc.operators = operators;
1976 5 : command->d.createopc.procedures = procedures;
1977 5 : command->parsetree = (Node *) copyObject(stmt);
1978 :
1979 10 : currentEventTriggerState->commandList =
1980 5 : lappend(currentEventTriggerState->commandList, command);
1981 :
1982 5 : MemoryContextSwitchTo(oldcxt);
1983 : }
1984 :
1985 : /*
1986 : * EventTriggerCollectAlterTSConfig
1987 : * Save data about an ALTER TEXT SEARCH CONFIGURATION command being
1988 : * executed
1989 : */
1990 : void
1991 5234 : EventTriggerCollectAlterTSConfig(const AlterTSConfigurationStmt *stmt, Oid cfgId,
1992 : Oid *dictIds, int ndicts)
1993 : {
1994 : MemoryContext oldcxt;
1995 : CollectedCommand *command;
1996 :
1997 : /* ignore if event trigger context not set, or collection disabled */
1998 5234 : if (!currentEventTriggerState ||
1999 2 : currentEventTriggerState->commandCollectionInhibited)
2000 5232 : return;
2001 :
2002 2 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
2003 :
2004 2 : command = palloc0_object(CollectedCommand);
2005 2 : command->type = SCT_AlterTSConfig;
2006 2 : command->in_extension = creating_extension;
2007 2 : ObjectAddressSet(command->d.atscfg.address,
2008 : TSConfigRelationId, cfgId);
2009 2 : if (ndicts > 0)
2010 : {
2011 1 : command->d.atscfg.dictIds = palloc_array(Oid, ndicts);
2012 1 : memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
2013 : }
2014 2 : command->d.atscfg.ndicts = ndicts;
2015 2 : command->parsetree = (Node *) copyObject(stmt);
2016 :
2017 4 : currentEventTriggerState->commandList =
2018 2 : lappend(currentEventTriggerState->commandList, command);
2019 :
2020 2 : MemoryContextSwitchTo(oldcxt);
2021 : }
2022 :
2023 : /*
2024 : * EventTriggerCollectAlterDefPrivs
2025 : * Save data about an ALTER DEFAULT PRIVILEGES command being
2026 : * executed
2027 : */
2028 : void
2029 127 : EventTriggerCollectAlterDefPrivs(const AlterDefaultPrivilegesStmt *stmt)
2030 : {
2031 : MemoryContext oldcxt;
2032 : CollectedCommand *command;
2033 :
2034 : /* ignore if event trigger context not set, or collection disabled */
2035 127 : if (!currentEventTriggerState ||
2036 5 : currentEventTriggerState->commandCollectionInhibited)
2037 122 : return;
2038 :
2039 5 : oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
2040 :
2041 5 : command = palloc0_object(CollectedCommand);
2042 5 : command->type = SCT_AlterDefaultPrivileges;
2043 5 : command->d.defprivs.objtype = stmt->action->objtype;
2044 5 : command->in_extension = creating_extension;
2045 5 : command->parsetree = (Node *) copyObject(stmt);
2046 :
2047 10 : currentEventTriggerState->commandList =
2048 5 : lappend(currentEventTriggerState->commandList, command);
2049 5 : MemoryContextSwitchTo(oldcxt);
2050 : }
2051 :
2052 : /*
2053 : * In a ddl_command_end event trigger, this function reports the DDL commands
2054 : * being run.
2055 : */
2056 : Datum
2057 393 : pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
2058 : {
2059 393 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2060 : ListCell *lc;
2061 :
2062 : /*
2063 : * Protect this function from being called out of context
2064 : */
2065 393 : if (!currentEventTriggerState)
2066 0 : ereport(ERROR,
2067 : (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
2068 : errmsg("%s can only be called in an event trigger function",
2069 : "pg_event_trigger_ddl_commands()")));
2070 :
2071 : /* Build tuplestore to hold the result rows */
2072 393 : InitMaterializedSRF(fcinfo, 0);
2073 :
2074 809 : foreach(lc, currentEventTriggerState->commandList)
2075 : {
2076 416 : CollectedCommand *cmd = lfirst(lc);
2077 : Datum values[9];
2078 416 : bool nulls[9] = {0};
2079 : ObjectAddress addr;
2080 416 : int i = 0;
2081 :
2082 : /*
2083 : * For IF NOT EXISTS commands that attempt to create an existing
2084 : * object, the returned OID is Invalid. Don't return anything.
2085 : *
2086 : * One might think that a viable alternative would be to look up the
2087 : * Oid of the existing object and run the deparse with that. But
2088 : * since the parse tree might be different from the one that created
2089 : * the object in the first place, we might not end up in a consistent
2090 : * state anyway.
2091 : */
2092 416 : if (cmd->type == SCT_Simple &&
2093 327 : !OidIsValid(cmd->d.simple.address.objectId))
2094 4 : continue;
2095 :
2096 416 : switch (cmd->type)
2097 : {
2098 402 : case SCT_Simple:
2099 : case SCT_AlterTable:
2100 : case SCT_AlterOpFamily:
2101 : case SCT_CreateOpClass:
2102 : case SCT_AlterTSConfig:
2103 : {
2104 : char *identity;
2105 : char *type;
2106 402 : char *schema = NULL;
2107 :
2108 402 : if (cmd->type == SCT_Simple)
2109 327 : addr = cmd->d.simple.address;
2110 75 : else if (cmd->type == SCT_AlterTable)
2111 67 : ObjectAddressSet(addr,
2112 : cmd->d.alterTable.classId,
2113 : cmd->d.alterTable.objectId);
2114 8 : else if (cmd->type == SCT_AlterOpFamily)
2115 1 : addr = cmd->d.opfam.address;
2116 7 : else if (cmd->type == SCT_CreateOpClass)
2117 5 : addr = cmd->d.createopc.address;
2118 2 : else if (cmd->type == SCT_AlterTSConfig)
2119 2 : addr = cmd->d.atscfg.address;
2120 :
2121 : /*
2122 : * If an object was dropped in the same command we may end
2123 : * up in a situation where we generated a message but can
2124 : * no longer look for the object information, so skip it
2125 : * rather than failing. This can happen for example with
2126 : * some subcommand combinations of ALTER TABLE.
2127 : */
2128 402 : identity = getObjectIdentity(&addr, true);
2129 402 : if (identity == NULL)
2130 4 : continue;
2131 :
2132 : /* The type can never be NULL. */
2133 398 : type = getObjectTypeDescription(&addr, true);
2134 :
2135 : /*
2136 : * Obtain schema name, if any ("pg_temp" if a temp
2137 : * object). If the object class is not in the supported
2138 : * list here, we assume it's a schema-less object type,
2139 : * and thus "schema" remains set to NULL.
2140 : */
2141 398 : if (is_objectclass_supported(addr.classId))
2142 : {
2143 : AttrNumber nspAttnum;
2144 :
2145 398 : nspAttnum = get_object_attnum_namespace(addr.classId);
2146 398 : if (nspAttnum != InvalidAttrNumber)
2147 : {
2148 : Relation catalog;
2149 : HeapTuple objtup;
2150 : Oid schema_oid;
2151 : bool isnull;
2152 :
2153 349 : catalog = table_open(addr.classId, AccessShareLock);
2154 349 : objtup = get_catalog_object_by_oid(catalog,
2155 349 : get_object_attnum_oid(addr.classId),
2156 : addr.objectId);
2157 349 : if (!HeapTupleIsValid(objtup))
2158 0 : elog(ERROR, "cache lookup failed for object %u/%u",
2159 : addr.classId, addr.objectId);
2160 : schema_oid =
2161 349 : DatumGetObjectId(heap_getattr(objtup, nspAttnum,
2162 : RelationGetDescr(catalog), &isnull));
2163 349 : if (isnull)
2164 0 : elog(ERROR,
2165 : "invalid null namespace in object %u/%u/%d",
2166 : addr.classId, addr.objectId, addr.objectSubId);
2167 349 : schema = get_namespace_name_or_temp(schema_oid);
2168 :
2169 349 : table_close(catalog, AccessShareLock);
2170 : }
2171 : }
2172 :
2173 : /* classid */
2174 398 : values[i++] = ObjectIdGetDatum(addr.classId);
2175 : /* objid */
2176 398 : values[i++] = ObjectIdGetDatum(addr.objectId);
2177 : /* objsubid */
2178 398 : values[i++] = Int32GetDatum(addr.objectSubId);
2179 : /* command tag */
2180 398 : values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2181 : /* object_type */
2182 398 : values[i++] = CStringGetTextDatum(type);
2183 : /* schema */
2184 398 : if (schema == NULL)
2185 49 : nulls[i++] = true;
2186 : else
2187 349 : values[i++] = CStringGetTextDatum(schema);
2188 : /* identity */
2189 398 : values[i++] = CStringGetTextDatum(identity);
2190 : /* in_extension */
2191 398 : values[i++] = BoolGetDatum(cmd->in_extension);
2192 : /* command */
2193 398 : values[i++] = PointerGetDatum(cmd);
2194 : }
2195 398 : break;
2196 :
2197 1 : case SCT_AlterDefaultPrivileges:
2198 : /* classid */
2199 1 : nulls[i++] = true;
2200 : /* objid */
2201 1 : nulls[i++] = true;
2202 : /* objsubid */
2203 1 : nulls[i++] = true;
2204 : /* command tag */
2205 1 : values[i++] = CStringGetTextDatum(CreateCommandName(cmd->parsetree));
2206 : /* object_type */
2207 1 : values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(cmd->d.defprivs.objtype));
2208 : /* schema */
2209 1 : nulls[i++] = true;
2210 : /* identity */
2211 1 : nulls[i++] = true;
2212 : /* in_extension */
2213 1 : values[i++] = BoolGetDatum(cmd->in_extension);
2214 : /* command */
2215 1 : values[i++] = PointerGetDatum(cmd);
2216 1 : break;
2217 :
2218 13 : case SCT_Grant:
2219 : /* classid */
2220 13 : nulls[i++] = true;
2221 : /* objid */
2222 13 : nulls[i++] = true;
2223 : /* objsubid */
2224 13 : nulls[i++] = true;
2225 : /* command tag */
2226 13 : values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
2227 : "GRANT" : "REVOKE");
2228 : /* object_type */
2229 13 : values[i++] = CStringGetTextDatum(stringify_grant_objtype(cmd->d.grant.istmt->objtype));
2230 : /* schema */
2231 13 : nulls[i++] = true;
2232 : /* identity */
2233 13 : nulls[i++] = true;
2234 : /* in_extension */
2235 13 : values[i++] = BoolGetDatum(cmd->in_extension);
2236 : /* command */
2237 13 : values[i++] = PointerGetDatum(cmd);
2238 13 : break;
2239 : }
2240 :
2241 412 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2242 : values, nulls);
2243 : }
2244 :
2245 393 : PG_RETURN_VOID();
2246 : }
2247 :
2248 : /*
2249 : * Return the ObjectType as a string, as it would appear in GRANT and
2250 : * REVOKE commands.
2251 : */
2252 : static const char *
2253 13 : stringify_grant_objtype(ObjectType objtype)
2254 : {
2255 13 : switch (objtype)
2256 : {
2257 0 : case OBJECT_COLUMN:
2258 0 : return "COLUMN";
2259 8 : case OBJECT_TABLE:
2260 8 : return "TABLE";
2261 0 : case OBJECT_SEQUENCE:
2262 0 : return "SEQUENCE";
2263 0 : case OBJECT_DATABASE:
2264 0 : return "DATABASE";
2265 0 : case OBJECT_DOMAIN:
2266 0 : return "DOMAIN";
2267 0 : case OBJECT_FDW:
2268 0 : return "FOREIGN DATA WRAPPER";
2269 0 : case OBJECT_FOREIGN_SERVER:
2270 0 : return "FOREIGN SERVER";
2271 5 : case OBJECT_FUNCTION:
2272 5 : return "FUNCTION";
2273 0 : case OBJECT_LANGUAGE:
2274 0 : return "LANGUAGE";
2275 0 : case OBJECT_LARGEOBJECT:
2276 0 : return "LARGE OBJECT";
2277 0 : case OBJECT_SCHEMA:
2278 0 : return "SCHEMA";
2279 0 : case OBJECT_PARAMETER_ACL:
2280 0 : return "PARAMETER";
2281 0 : case OBJECT_PROCEDURE:
2282 0 : return "PROCEDURE";
2283 0 : case OBJECT_ROUTINE:
2284 0 : return "ROUTINE";
2285 0 : case OBJECT_TABLESPACE:
2286 0 : return "TABLESPACE";
2287 0 : case OBJECT_TYPE:
2288 0 : return "TYPE";
2289 : /* these currently aren't used */
2290 0 : case OBJECT_ACCESS_METHOD:
2291 : case OBJECT_AGGREGATE:
2292 : case OBJECT_AMOP:
2293 : case OBJECT_AMPROC:
2294 : case OBJECT_ATTRIBUTE:
2295 : case OBJECT_CAST:
2296 : case OBJECT_COLLATION:
2297 : case OBJECT_CONVERSION:
2298 : case OBJECT_DEFAULT:
2299 : case OBJECT_DEFACL:
2300 : case OBJECT_DOMCONSTRAINT:
2301 : case OBJECT_EVENT_TRIGGER:
2302 : case OBJECT_EXTENSION:
2303 : case OBJECT_FOREIGN_TABLE:
2304 : case OBJECT_INDEX:
2305 : case OBJECT_MATVIEW:
2306 : case OBJECT_OPCLASS:
2307 : case OBJECT_OPERATOR:
2308 : case OBJECT_OPFAMILY:
2309 : case OBJECT_POLICY:
2310 : case OBJECT_PROPGRAPH:
2311 : case OBJECT_PUBLICATION:
2312 : case OBJECT_PUBLICATION_NAMESPACE:
2313 : case OBJECT_PUBLICATION_REL:
2314 : case OBJECT_ROLE:
2315 : case OBJECT_RULE:
2316 : case OBJECT_STATISTIC_EXT:
2317 : case OBJECT_SUBSCRIPTION:
2318 : case OBJECT_TABCONSTRAINT:
2319 : case OBJECT_TRANSFORM:
2320 : case OBJECT_TRIGGER:
2321 : case OBJECT_TSCONFIGURATION:
2322 : case OBJECT_TSDICTIONARY:
2323 : case OBJECT_TSPARSER:
2324 : case OBJECT_TSTEMPLATE:
2325 : case OBJECT_USER_MAPPING:
2326 : case OBJECT_VIEW:
2327 0 : elog(ERROR, "unsupported object type: %d", (int) objtype);
2328 : }
2329 :
2330 0 : return "???"; /* keep compiler quiet */
2331 : }
2332 :
2333 : /*
2334 : * Return the ObjectType as a string; as above, but use the spelling
2335 : * in ALTER DEFAULT PRIVILEGES commands instead. Generally this is just
2336 : * the plural.
2337 : */
2338 : static const char *
2339 1 : stringify_adefprivs_objtype(ObjectType objtype)
2340 : {
2341 1 : switch (objtype)
2342 : {
2343 0 : case OBJECT_COLUMN:
2344 0 : return "COLUMNS";
2345 1 : case OBJECT_TABLE:
2346 1 : return "TABLES";
2347 0 : case OBJECT_SEQUENCE:
2348 0 : return "SEQUENCES";
2349 0 : case OBJECT_DATABASE:
2350 0 : return "DATABASES";
2351 0 : case OBJECT_DOMAIN:
2352 0 : return "DOMAINS";
2353 0 : case OBJECT_FDW:
2354 0 : return "FOREIGN DATA WRAPPERS";
2355 0 : case OBJECT_FOREIGN_SERVER:
2356 0 : return "FOREIGN SERVERS";
2357 0 : case OBJECT_FUNCTION:
2358 0 : return "FUNCTIONS";
2359 0 : case OBJECT_LANGUAGE:
2360 0 : return "LANGUAGES";
2361 0 : case OBJECT_LARGEOBJECT:
2362 0 : return "LARGE OBJECTS";
2363 0 : case OBJECT_SCHEMA:
2364 0 : return "SCHEMAS";
2365 0 : case OBJECT_PROCEDURE:
2366 0 : return "PROCEDURES";
2367 0 : case OBJECT_ROUTINE:
2368 0 : return "ROUTINES";
2369 0 : case OBJECT_TABLESPACE:
2370 0 : return "TABLESPACES";
2371 0 : case OBJECT_TYPE:
2372 0 : return "TYPES";
2373 : /* these currently aren't used */
2374 0 : case OBJECT_ACCESS_METHOD:
2375 : case OBJECT_AGGREGATE:
2376 : case OBJECT_AMOP:
2377 : case OBJECT_AMPROC:
2378 : case OBJECT_ATTRIBUTE:
2379 : case OBJECT_CAST:
2380 : case OBJECT_COLLATION:
2381 : case OBJECT_CONVERSION:
2382 : case OBJECT_DEFAULT:
2383 : case OBJECT_DEFACL:
2384 : case OBJECT_DOMCONSTRAINT:
2385 : case OBJECT_EVENT_TRIGGER:
2386 : case OBJECT_EXTENSION:
2387 : case OBJECT_FOREIGN_TABLE:
2388 : case OBJECT_INDEX:
2389 : case OBJECT_MATVIEW:
2390 : case OBJECT_OPCLASS:
2391 : case OBJECT_OPERATOR:
2392 : case OBJECT_OPFAMILY:
2393 : case OBJECT_PARAMETER_ACL:
2394 : case OBJECT_POLICY:
2395 : case OBJECT_PROPGRAPH:
2396 : case OBJECT_PUBLICATION:
2397 : case OBJECT_PUBLICATION_NAMESPACE:
2398 : case OBJECT_PUBLICATION_REL:
2399 : case OBJECT_ROLE:
2400 : case OBJECT_RULE:
2401 : case OBJECT_STATISTIC_EXT:
2402 : case OBJECT_SUBSCRIPTION:
2403 : case OBJECT_TABCONSTRAINT:
2404 : case OBJECT_TRANSFORM:
2405 : case OBJECT_TRIGGER:
2406 : case OBJECT_TSCONFIGURATION:
2407 : case OBJECT_TSDICTIONARY:
2408 : case OBJECT_TSPARSER:
2409 : case OBJECT_TSTEMPLATE:
2410 : case OBJECT_USER_MAPPING:
2411 : case OBJECT_VIEW:
2412 0 : elog(ERROR, "unsupported object type: %d", (int) objtype);
2413 : }
2414 :
2415 0 : return "???"; /* keep compiler quiet */
2416 : }
|