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