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