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