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