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