1 /*-------------------------------------------------------------------------
2 *
3 * event_trigger.c
4 * PostgreSQL EVENT TRIGGER support code.
5 *
6 * Portions Copyright (c) 1996-2017, 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 {"PUBLICATION", true},
110 {"ROLE", false},
111 {"RULE", true},
112 {"SCHEMA", true},
113 {"SEQUENCE", true},
114 {"SERVER", true},
115 {"STATISTICS", true},
116 {"SUBSCRIPTION", true},
117 {"TABLE", true},
118 {"TABLESPACE", false},
119 {"TRANSFORM", true},
120 {"TRIGGER", true},
121 {"TEXT SEARCH CONFIGURATION", true},
122 {"TEXT SEARCH DICTIONARY", true},
123 {"TEXT SEARCH PARSER", true},
124 {"TEXT SEARCH TEMPLATE", true},
125 {"TYPE", true},
126 {"USER MAPPING", true},
127 {"VIEW", true},
128 {NULL, false}
129 };
130
131 /* Support for dropped objects */
132 typedef struct SQLDropObject
133 {
134 ObjectAddress address;
135 const char *schemaname;
136 const char *objname;
137 const char *objidentity;
138 const char *objecttype;
139 List *addrnames;
140 List *addrargs;
141 bool original;
142 bool normal;
143 bool istemp;
144 slist_node next;
145 } SQLDropObject;
146
147 static void AlterEventTriggerOwner_internal(Relation rel,
148 HeapTuple tup,
149 Oid newOwnerId);
150 static event_trigger_command_tag_check_result check_ddl_tag(const char *tag);
151 static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag(
152 const char *tag);
153 static void error_duplicate_filter_variable(const char *defname);
154 static Datum filter_list_to_array(List *filterlist);
155 static Oid insert_event_trigger_tuple(char *trigname, char *eventname,
156 Oid evtOwner, Oid funcoid, List *tags);
157 static void validate_ddl_tags(const char *filtervar, List *taglist);
158 static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
159 static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
160 static const char *stringify_grantobjtype(GrantObjectType objtype);
161 static const char *stringify_adefprivs_objtype(GrantObjectType objtype);
162
163 /*
164 * Create an event trigger.
165 */
166 Oid
CreateEventTrigger(CreateEventTrigStmt * stmt)167 CreateEventTrigger(CreateEventTrigStmt *stmt)
168 {
169 HeapTuple tuple;
170 Oid funcoid;
171 Oid funcrettype;
172 Oid fargtypes[1]; /* dummy */
173 Oid evtowner = GetUserId();
174 ListCell *lc;
175 List *tags = NULL;
176
177 /*
178 * It would be nice to allow database owners or even regular users to do
179 * this, but there are obvious privilege escalation risks which would have
180 * to somehow be plugged first.
181 */
182 if (!superuser())
183 ereport(ERROR,
184 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
185 errmsg("permission denied to create event trigger \"%s\"",
186 stmt->trigname),
187 errhint("Must be superuser to create an event trigger.")));
188
189 /* Validate event name. */
190 if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
191 strcmp(stmt->eventname, "ddl_command_end") != 0 &&
192 strcmp(stmt->eventname, "sql_drop") != 0 &&
193 strcmp(stmt->eventname, "table_rewrite") != 0)
194 ereport(ERROR,
195 (errcode(ERRCODE_SYNTAX_ERROR),
196 errmsg("unrecognized event name \"%s\"",
197 stmt->eventname)));
198
199 /* Validate filter conditions. */
200 foreach(lc, stmt->whenclause)
201 {
202 DefElem *def = (DefElem *) lfirst(lc);
203
204 if (strcmp(def->defname, "tag") == 0)
205 {
206 if (tags != NULL)
207 error_duplicate_filter_variable(def->defname);
208 tags = (List *) def->arg;
209 }
210 else
211 ereport(ERROR,
212 (errcode(ERRCODE_SYNTAX_ERROR),
213 errmsg("unrecognized filter variable \"%s\"", def->defname)));
214 }
215
216 /* Validate tag list, if any. */
217 if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
218 strcmp(stmt->eventname, "ddl_command_end") == 0 ||
219 strcmp(stmt->eventname, "sql_drop") == 0)
220 && tags != NULL)
221 validate_ddl_tags("tag", tags);
222 else if (strcmp(stmt->eventname, "table_rewrite") == 0
223 && tags != NULL)
224 validate_table_rewrite_tags("tag", tags);
225
226 /*
227 * Give user a nice error message if an event trigger of the same name
228 * already exists.
229 */
230 tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
231 if (HeapTupleIsValid(tuple))
232 ereport(ERROR,
233 (errcode(ERRCODE_DUPLICATE_OBJECT),
234 errmsg("event trigger \"%s\" already exists",
235 stmt->trigname)));
236
237 /* Find and validate the trigger function. */
238 funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
239 funcrettype = get_func_rettype(funcoid);
240 if (funcrettype != EVTTRIGGEROID)
241 ereport(ERROR,
242 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
243 errmsg("function %s must return type %s",
244 NameListToString(stmt->funcname), "event_trigger")));
245
246 /* Insert catalog entries. */
247 return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
248 evtowner, funcoid, tags);
249 }
250
251 /*
252 * Validate DDL command tags.
253 */
254 static void
validate_ddl_tags(const char * filtervar,List * taglist)255 validate_ddl_tags(const char *filtervar, List *taglist)
256 {
257 ListCell *lc;
258
259 foreach(lc, taglist)
260 {
261 const char *tag = strVal(lfirst(lc));
262 event_trigger_command_tag_check_result result;
263
264 result = check_ddl_tag(tag);
265 if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
266 ereport(ERROR,
267 (errcode(ERRCODE_SYNTAX_ERROR),
268 errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
269 tag, filtervar)));
270 if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
271 ereport(ERROR,
272 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
273 /* translator: %s represents an SQL statement name */
274 errmsg("event triggers are not supported for %s",
275 tag)));
276 }
277 }
278
279 static event_trigger_command_tag_check_result
check_ddl_tag(const char * tag)280 check_ddl_tag(const char *tag)
281 {
282 const char *obtypename;
283 event_trigger_support_data *etsd;
284
285 /*
286 * Handle some idiosyncratic special cases.
287 */
288 if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
289 pg_strcasecmp(tag, "SELECT INTO") == 0 ||
290 pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
291 pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
292 pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
293 pg_strcasecmp(tag, "COMMENT") == 0 ||
294 pg_strcasecmp(tag, "GRANT") == 0 ||
295 pg_strcasecmp(tag, "REVOKE") == 0 ||
296 pg_strcasecmp(tag, "DROP OWNED") == 0 ||
297 pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0 ||
298 pg_strcasecmp(tag, "SECURITY LABEL") == 0)
299 return EVENT_TRIGGER_COMMAND_TAG_OK;
300
301 /*
302 * Otherwise, command should be CREATE, ALTER, or DROP.
303 */
304 if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
305 obtypename = tag + 7;
306 else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
307 obtypename = tag + 6;
308 else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
309 obtypename = tag + 5;
310 else
311 return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
312
313 /*
314 * ...and the object type should be something recognizable.
315 */
316 for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
317 if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
318 break;
319 if (etsd->obtypename == NULL)
320 return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
321 if (!etsd->supported)
322 return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
323 return EVENT_TRIGGER_COMMAND_TAG_OK;
324 }
325
326 /*
327 * Validate DDL command tags for event table_rewrite.
328 */
329 static void
validate_table_rewrite_tags(const char * filtervar,List * taglist)330 validate_table_rewrite_tags(const char *filtervar, List *taglist)
331 {
332 ListCell *lc;
333
334 foreach(lc, taglist)
335 {
336 const char *tag = strVal(lfirst(lc));
337 event_trigger_command_tag_check_result result;
338
339 result = check_table_rewrite_ddl_tag(tag);
340 if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
341 ereport(ERROR,
342 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
343 /* translator: %s represents an SQL statement name */
344 errmsg("event triggers are not supported for %s",
345 tag)));
346 }
347 }
348
349 static event_trigger_command_tag_check_result
check_table_rewrite_ddl_tag(const char * tag)350 check_table_rewrite_ddl_tag(const char *tag)
351 {
352 if (pg_strcasecmp(tag, "ALTER TABLE") == 0 ||
353 pg_strcasecmp(tag, "ALTER TYPE") == 0)
354 return EVENT_TRIGGER_COMMAND_TAG_OK;
355
356 return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
357 }
358
359 /*
360 * Complain about a duplicate filter variable.
361 */
362 static void
error_duplicate_filter_variable(const char * defname)363 error_duplicate_filter_variable(const char *defname)
364 {
365 ereport(ERROR,
366 (errcode(ERRCODE_SYNTAX_ERROR),
367 errmsg("filter variable \"%s\" specified more than once",
368 defname)));
369 }
370
371 /*
372 * Insert the new pg_event_trigger row and record dependencies.
373 */
374 static Oid
insert_event_trigger_tuple(char * trigname,char * eventname,Oid evtOwner,Oid funcoid,List * taglist)375 insert_event_trigger_tuple(char *trigname, char *eventname, Oid evtOwner,
376 Oid funcoid, List *taglist)
377 {
378 Relation tgrel;
379 Oid trigoid;
380 HeapTuple tuple;
381 Datum values[Natts_pg_trigger];
382 bool nulls[Natts_pg_trigger];
383 NameData evtnamedata,
384 evteventdata;
385 ObjectAddress myself,
386 referenced;
387
388 /* Open pg_event_trigger. */
389 tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
390
391 /* Build the new pg_trigger tuple. */
392 memset(nulls, false, sizeof(nulls));
393 namestrcpy(&evtnamedata, trigname);
394 values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(&evtnamedata);
395 namestrcpy(&evteventdata, eventname);
396 values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(&evteventdata);
397 values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
398 values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
399 values[Anum_pg_event_trigger_evtenabled - 1] =
400 CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
401 if (taglist == NIL)
402 nulls[Anum_pg_event_trigger_evttags - 1] = true;
403 else
404 values[Anum_pg_event_trigger_evttags - 1] =
405 filter_list_to_array(taglist);
406
407 /* Insert heap tuple. */
408 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
409 trigoid = CatalogTupleInsert(tgrel, tuple);
410 heap_freetuple(tuple);
411
412 /* Depend on owner. */
413 recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
414
415 /* Depend on event trigger function. */
416 myself.classId = EventTriggerRelationId;
417 myself.objectId = trigoid;
418 myself.objectSubId = 0;
419 referenced.classId = ProcedureRelationId;
420 referenced.objectId = funcoid;
421 referenced.objectSubId = 0;
422 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
423
424 /* Depend on extension, if any. */
425 recordDependencyOnCurrentExtension(&myself, false);
426
427 /* Post creation hook for new event trigger */
428 InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);
429
430 /* Close pg_event_trigger. */
431 heap_close(tgrel, RowExclusiveLock);
432
433 return trigoid;
434 }
435
436 /*
437 * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented
438 * by a DefElem whose value is a List of String nodes; in the catalog, we
439 * store the list of strings as a text array. This function transforms the
440 * former representation into the latter one.
441 *
442 * For cleanliness, we store command tags in the catalog as text. It's
443 * possible (although not currently anticipated) that we might have
444 * a case-sensitive filter variable in the future, in which case this would
445 * need some further adjustment.
446 */
447 static Datum
filter_list_to_array(List * filterlist)448 filter_list_to_array(List *filterlist)
449 {
450 ListCell *lc;
451 Datum *data;
452 int i = 0,
453 l = list_length(filterlist);
454
455 data = (Datum *) palloc(l * sizeof(Datum));
456
457 foreach(lc, filterlist)
458 {
459 const char *value = strVal(lfirst(lc));
460 char *result,
461 *p;
462
463 result = pstrdup(value);
464 for (p = result; *p; p++)
465 *p = pg_ascii_toupper((unsigned char) *p);
466 data[i++] = PointerGetDatum(cstring_to_text(result));
467 pfree(result);
468 }
469
470 return PointerGetDatum(construct_array(data, l, TEXTOID, -1, false, 'i'));
471 }
472
473 /*
474 * Guts of event trigger deletion.
475 */
476 void
RemoveEventTriggerById(Oid trigOid)477 RemoveEventTriggerById(Oid trigOid)
478 {
479 Relation tgrel;
480 HeapTuple tup;
481
482 tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
483
484 tup = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
485 if (!HeapTupleIsValid(tup))
486 elog(ERROR, "cache lookup failed for event trigger %u", trigOid);
487
488 CatalogTupleDelete(tgrel, &tup->t_self);
489
490 ReleaseSysCache(tup);
491
492 heap_close(tgrel, RowExclusiveLock);
493 }
494
495 /*
496 * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
497 */
498 Oid
AlterEventTrigger(AlterEventTrigStmt * stmt)499 AlterEventTrigger(AlterEventTrigStmt *stmt)
500 {
501 Relation tgrel;
502 HeapTuple tup;
503 Oid trigoid;
504 Form_pg_event_trigger evtForm;
505 char tgenabled = stmt->tgenabled;
506
507 tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
508
509 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
510 CStringGetDatum(stmt->trigname));
511 if (!HeapTupleIsValid(tup))
512 ereport(ERROR,
513 (errcode(ERRCODE_UNDEFINED_OBJECT),
514 errmsg("event trigger \"%s\" does not exist",
515 stmt->trigname)));
516
517 trigoid = HeapTupleGetOid(tup);
518
519 if (!pg_event_trigger_ownercheck(trigoid, GetUserId()))
520 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
521 stmt->trigname);
522
523 /* tuple is a copy, so we can modify it below */
524 evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
525 evtForm->evtenabled = tgenabled;
526
527 CatalogTupleUpdate(tgrel, &tup->t_self, tup);
528
529 InvokeObjectPostAlterHook(EventTriggerRelationId,
530 trigoid, 0);
531
532 /* clean up */
533 heap_freetuple(tup);
534 heap_close(tgrel, RowExclusiveLock);
535
536 return trigoid;
537 }
538
539 /*
540 * Change event trigger's owner -- by name
541 */
542 ObjectAddress
AlterEventTriggerOwner(const char * name,Oid newOwnerId)543 AlterEventTriggerOwner(const char *name, Oid newOwnerId)
544 {
545 Oid evtOid;
546 HeapTuple tup;
547 Relation rel;
548 ObjectAddress address;
549
550 rel = heap_open(EventTriggerRelationId, RowExclusiveLock);
551
552 tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name));
553
554 if (!HeapTupleIsValid(tup))
555 ereport(ERROR,
556 (errcode(ERRCODE_UNDEFINED_OBJECT),
557 errmsg("event trigger \"%s\" does not exist", name)));
558
559 evtOid = HeapTupleGetOid(tup);
560
561 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
562
563 ObjectAddressSet(address, EventTriggerRelationId, evtOid);
564
565 heap_freetuple(tup);
566
567 heap_close(rel, RowExclusiveLock);
568
569 return address;
570 }
571
572 /*
573 * Change event trigger owner, by OID
574 */
575 void
AlterEventTriggerOwner_oid(Oid trigOid,Oid newOwnerId)576 AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId)
577 {
578 HeapTuple tup;
579 Relation rel;
580
581 rel = heap_open(EventTriggerRelationId, RowExclusiveLock);
582
583 tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid));
584
585 if (!HeapTupleIsValid(tup))
586 ereport(ERROR,
587 (errcode(ERRCODE_UNDEFINED_OBJECT),
588 errmsg("event trigger with OID %u does not exist", trigOid)));
589
590 AlterEventTriggerOwner_internal(rel, tup, newOwnerId);
591
592 heap_freetuple(tup);
593
594 heap_close(rel, RowExclusiveLock);
595 }
596
597 /*
598 * Internal workhorse for changing an event trigger's owner
599 */
600 static void
AlterEventTriggerOwner_internal(Relation rel,HeapTuple tup,Oid newOwnerId)601 AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
602 {
603 Form_pg_event_trigger form;
604
605 form = (Form_pg_event_trigger) GETSTRUCT(tup);
606
607 if (form->evtowner == newOwnerId)
608 return;
609
610 if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
611 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
612 NameStr(form->evtname));
613
614 /* New owner must be a superuser */
615 if (!superuser_arg(newOwnerId))
616 ereport(ERROR,
617 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
618 errmsg("permission denied to change owner of event trigger \"%s\"",
619 NameStr(form->evtname)),
620 errhint("The owner of an event trigger must be a superuser.")));
621
622 form->evtowner = newOwnerId;
623 CatalogTupleUpdate(rel, &tup->t_self, 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 triggers, 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_PUBLICATION:
1129 case OBJECT_PUBLICATION_REL:
1130 case OBJECT_RULE:
1131 case OBJECT_SCHEMA:
1132 case OBJECT_SEQUENCE:
1133 case OBJECT_SUBSCRIPTION:
1134 case OBJECT_STATISTIC_EXT:
1135 case OBJECT_TABCONSTRAINT:
1136 case OBJECT_TABLE:
1137 case OBJECT_TRANSFORM:
1138 case OBJECT_TRIGGER:
1139 case OBJECT_TSCONFIGURATION:
1140 case OBJECT_TSDICTIONARY:
1141 case OBJECT_TSPARSER:
1142 case OBJECT_TSTEMPLATE:
1143 case OBJECT_TYPE:
1144 case OBJECT_USER_MAPPING:
1145 case OBJECT_VIEW:
1146 return true;
1147
1148 /*
1149 * There's intentionally no default: case here; we want the
1150 * compiler to warn if a new ObjectType hasn't been handled above.
1151 */
1152 }
1153
1154 /* Shouldn't get here, but if we do, say "no support" */
1155 return false;
1156 }
1157
1158 /*
1159 * Do event triggers support this object class?
1160 */
1161 bool
EventTriggerSupportsObjectClass(ObjectClass objclass)1162 EventTriggerSupportsObjectClass(ObjectClass objclass)
1163 {
1164 switch (objclass)
1165 {
1166 case OCLASS_DATABASE:
1167 case OCLASS_TBLSPACE:
1168 case OCLASS_ROLE:
1169 /* no support for global objects */
1170 return false;
1171 case OCLASS_EVENT_TRIGGER:
1172 /* no support for event triggers on event triggers */
1173 return false;
1174 case OCLASS_CLASS:
1175 case OCLASS_PROC:
1176 case OCLASS_TYPE:
1177 case OCLASS_CAST:
1178 case OCLASS_COLLATION:
1179 case OCLASS_CONSTRAINT:
1180 case OCLASS_CONVERSION:
1181 case OCLASS_DEFAULT:
1182 case OCLASS_LANGUAGE:
1183 case OCLASS_LARGEOBJECT:
1184 case OCLASS_OPERATOR:
1185 case OCLASS_OPCLASS:
1186 case OCLASS_OPFAMILY:
1187 case OCLASS_AM:
1188 case OCLASS_AMOP:
1189 case OCLASS_AMPROC:
1190 case OCLASS_REWRITE:
1191 case OCLASS_TRIGGER:
1192 case OCLASS_SCHEMA:
1193 case OCLASS_STATISTIC_EXT:
1194 case OCLASS_TSPARSER:
1195 case OCLASS_TSDICT:
1196 case OCLASS_TSTEMPLATE:
1197 case OCLASS_TSCONFIG:
1198 case OCLASS_FDW:
1199 case OCLASS_FOREIGN_SERVER:
1200 case OCLASS_USER_MAPPING:
1201 case OCLASS_DEFACL:
1202 case OCLASS_EXTENSION:
1203 case OCLASS_POLICY:
1204 case OCLASS_PUBLICATION:
1205 case OCLASS_PUBLICATION_REL:
1206 case OCLASS_SUBSCRIPTION:
1207 case OCLASS_TRANSFORM:
1208 return true;
1209
1210 /*
1211 * There's intentionally no default: case here; we want the
1212 * compiler to warn if a new OCLASS hasn't been handled above.
1213 */
1214 }
1215
1216 /* Shouldn't get here, but if we do, say "no support" */
1217 return false;
1218 }
1219
1220 bool
EventTriggerSupportsGrantObjectType(GrantObjectType objtype)1221 EventTriggerSupportsGrantObjectType(GrantObjectType objtype)
1222 {
1223 switch (objtype)
1224 {
1225 case ACL_OBJECT_DATABASE:
1226 case ACL_OBJECT_TABLESPACE:
1227 /* no support for global objects */
1228 return false;
1229
1230 case ACL_OBJECT_COLUMN:
1231 case ACL_OBJECT_RELATION:
1232 case ACL_OBJECT_SEQUENCE:
1233 case ACL_OBJECT_DOMAIN:
1234 case ACL_OBJECT_FDW:
1235 case ACL_OBJECT_FOREIGN_SERVER:
1236 case ACL_OBJECT_FUNCTION:
1237 case ACL_OBJECT_LANGUAGE:
1238 case ACL_OBJECT_LARGEOBJECT:
1239 case ACL_OBJECT_NAMESPACE:
1240 case ACL_OBJECT_TYPE:
1241 return true;
1242
1243 /*
1244 * There's intentionally no default: case here; we want the
1245 * compiler to warn if a new ACL class hasn't been handled above.
1246 */
1247 }
1248
1249 /* Shouldn't get here, but if we do, say "no support" */
1250 return false;
1251 }
1252
1253 /*
1254 * Prepare event trigger state for a new complete query to run, if necessary;
1255 * returns whether this was done. If it was, EventTriggerEndCompleteQuery must
1256 * be called when the query is done, regardless of whether it succeeds or fails
1257 * -- so use of a PG_TRY block is mandatory.
1258 */
1259 bool
EventTriggerBeginCompleteQuery(void)1260 EventTriggerBeginCompleteQuery(void)
1261 {
1262 EventTriggerQueryState *state;
1263 MemoryContext cxt;
1264
1265 /*
1266 * Currently, sql_drop, table_rewrite, ddl_command_end events are the only
1267 * reason to have event trigger state at all; so if there are none, don't
1268 * install one.
1269 */
1270 if (!trackDroppedObjectsNeeded())
1271 return false;
1272
1273 cxt = AllocSetContextCreate(TopMemoryContext,
1274 "event trigger state",
1275 ALLOCSET_DEFAULT_SIZES);
1276 state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
1277 state->cxt = cxt;
1278 slist_init(&(state->SQLDropList));
1279 state->in_sql_drop = false;
1280 state->table_rewrite_oid = InvalidOid;
1281
1282 state->commandCollectionInhibited = currentEventTriggerState ?
1283 currentEventTriggerState->commandCollectionInhibited : false;
1284 state->currentCommand = NULL;
1285 state->commandList = NIL;
1286 state->previous = currentEventTriggerState;
1287 currentEventTriggerState = state;
1288
1289 return true;
1290 }
1291
1292 /*
1293 * Query completed (or errored out) -- clean up local state, return to previous
1294 * one.
1295 *
1296 * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
1297 * returned false previously.
1298 *
1299 * Note: this might be called in the PG_CATCH block of a failing transaction,
1300 * so be wary of running anything unnecessary. (In particular, it's probably
1301 * unwise to try to allocate memory.)
1302 */
1303 void
EventTriggerEndCompleteQuery(void)1304 EventTriggerEndCompleteQuery(void)
1305 {
1306 EventTriggerQueryState *prevstate;
1307
1308 prevstate = currentEventTriggerState->previous;
1309
1310 /* this avoids the need for retail pfree of SQLDropList items: */
1311 MemoryContextDelete(currentEventTriggerState->cxt);
1312
1313 currentEventTriggerState = prevstate;
1314 }
1315
1316 /*
1317 * Do we need to keep close track of objects being dropped?
1318 *
1319 * This is useful because there is a cost to running with them enabled.
1320 */
1321 bool
trackDroppedObjectsNeeded(void)1322 trackDroppedObjectsNeeded(void)
1323 {
1324 /*
1325 * true if any sql_drop, table_rewrite, ddl_command_end event trigger
1326 * exists
1327 */
1328 return list_length(EventCacheLookup(EVT_SQLDrop)) > 0 ||
1329 list_length(EventCacheLookup(EVT_TableRewrite)) > 0 ||
1330 list_length(EventCacheLookup(EVT_DDLCommandEnd)) > 0;
1331 }
1332
1333 /*
1334 * Support for dropped objects information on event trigger functions.
1335 *
1336 * We keep the list of objects dropped by the current command in current
1337 * state's SQLDropList (comprising SQLDropObject items). Each time a new
1338 * command is to start, a clean EventTriggerQueryState is created; commands
1339 * that drop objects do the dependency.c dance to drop objects, which
1340 * populates the current state's SQLDropList; when the event triggers are
1341 * invoked they can consume the list via pg_event_trigger_dropped_objects().
1342 * When the command finishes, the EventTriggerQueryState is cleared, and
1343 * the one from the previous command is restored (when no command is in
1344 * execution, the current state is NULL).
1345 *
1346 * All this lets us support the case that an event trigger function drops
1347 * objects "reentrantly".
1348 */
1349
1350 /*
1351 * Register one object as being dropped by the current command.
1352 */
1353 void
EventTriggerSQLDropAddObject(const ObjectAddress * object,bool original,bool normal)1354 EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
1355 {
1356 SQLDropObject *obj;
1357 MemoryContext oldcxt;
1358
1359 if (!currentEventTriggerState)
1360 return;
1361
1362 Assert(EventTriggerSupportsObjectClass(getObjectClass(object)));
1363
1364 /* don't report temp schemas except my own */
1365 if (object->classId == NamespaceRelationId &&
1366 (isAnyTempNamespace(object->objectId) &&
1367 !isTempNamespace(object->objectId)))
1368 return;
1369
1370 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1371
1372 obj = palloc0(sizeof(SQLDropObject));
1373 obj->address = *object;
1374 obj->original = original;
1375 obj->normal = normal;
1376
1377 /*
1378 * Obtain schema names from the object's catalog tuple, if one exists;
1379 * this lets us skip objects in temp schemas. We trust that
1380 * ObjectProperty contains all object classes that can be
1381 * schema-qualified.
1382 */
1383 if (is_objectclass_supported(object->classId))
1384 {
1385 Relation catalog;
1386 HeapTuple tuple;
1387
1388 catalog = heap_open(obj->address.classId, AccessShareLock);
1389 tuple = get_catalog_object_by_oid(catalog, obj->address.objectId);
1390
1391 if (tuple)
1392 {
1393 AttrNumber attnum;
1394 Datum datum;
1395 bool isnull;
1396
1397 attnum = get_object_attnum_namespace(obj->address.classId);
1398 if (attnum != InvalidAttrNumber)
1399 {
1400 datum = heap_getattr(tuple, attnum,
1401 RelationGetDescr(catalog), &isnull);
1402 if (!isnull)
1403 {
1404 Oid namespaceId;
1405
1406 namespaceId = DatumGetObjectId(datum);
1407 /* temp objects are only reported if they are my own */
1408 if (isTempNamespace(namespaceId))
1409 {
1410 obj->schemaname = "pg_temp";
1411 obj->istemp = true;
1412 }
1413 else if (isAnyTempNamespace(namespaceId))
1414 {
1415 pfree(obj);
1416 heap_close(catalog, AccessShareLock);
1417 MemoryContextSwitchTo(oldcxt);
1418 return;
1419 }
1420 else
1421 {
1422 obj->schemaname = get_namespace_name(namespaceId);
1423 obj->istemp = false;
1424 }
1425 }
1426 }
1427
1428 if (get_object_namensp_unique(obj->address.classId) &&
1429 obj->address.objectSubId == 0)
1430 {
1431 attnum = get_object_attnum_name(obj->address.classId);
1432 if (attnum != InvalidAttrNumber)
1433 {
1434 datum = heap_getattr(tuple, attnum,
1435 RelationGetDescr(catalog), &isnull);
1436 if (!isnull)
1437 obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
1438 }
1439 }
1440 }
1441
1442 heap_close(catalog, AccessShareLock);
1443 }
1444 else
1445 {
1446 if (object->classId == NamespaceRelationId &&
1447 isTempNamespace(object->objectId))
1448 obj->istemp = true;
1449 }
1450
1451 /* object identity, objname and objargs */
1452 obj->objidentity =
1453 getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
1454
1455 /* object type */
1456 obj->objecttype = getObjectTypeDescription(&obj->address);
1457
1458 slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
1459
1460 MemoryContextSwitchTo(oldcxt);
1461 }
1462
1463 /*
1464 * pg_event_trigger_dropped_objects
1465 *
1466 * Make the list of dropped objects available to the user function run by the
1467 * Event Trigger.
1468 */
1469 Datum
pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)1470 pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
1471 {
1472 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1473 TupleDesc tupdesc;
1474 Tuplestorestate *tupstore;
1475 MemoryContext per_query_ctx;
1476 MemoryContext oldcontext;
1477 slist_iter iter;
1478
1479 /*
1480 * Protect this function from being called out of context
1481 */
1482 if (!currentEventTriggerState ||
1483 !currentEventTriggerState->in_sql_drop)
1484 ereport(ERROR,
1485 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1486 errmsg("%s can only be called in a sql_drop event trigger function",
1487 "pg_event_trigger_dropped_objects()")));
1488
1489 /* check to see if caller supports us returning a tuplestore */
1490 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1491 ereport(ERROR,
1492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1493 errmsg("set-valued function called in context that cannot accept a set")));
1494 if (!(rsinfo->allowedModes & SFRM_Materialize))
1495 ereport(ERROR,
1496 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1497 errmsg("materialize mode required, but it is not allowed in this context")));
1498
1499 /* Build a tuple descriptor for our result type */
1500 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1501 elog(ERROR, "return type must be a row type");
1502
1503 /* Build tuplestore to hold the result rows */
1504 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1505 oldcontext = MemoryContextSwitchTo(per_query_ctx);
1506
1507 tupstore = tuplestore_begin_heap(true, false, work_mem);
1508 rsinfo->returnMode = SFRM_Materialize;
1509 rsinfo->setResult = tupstore;
1510 rsinfo->setDesc = tupdesc;
1511
1512 MemoryContextSwitchTo(oldcontext);
1513
1514 slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
1515 {
1516 SQLDropObject *obj;
1517 int i = 0;
1518 Datum values[12];
1519 bool nulls[12];
1520
1521 obj = slist_container(SQLDropObject, next, iter.cur);
1522
1523 MemSet(values, 0, sizeof(values));
1524 MemSet(nulls, 0, sizeof(nulls));
1525
1526 /* classid */
1527 values[i++] = ObjectIdGetDatum(obj->address.classId);
1528
1529 /* objid */
1530 values[i++] = ObjectIdGetDatum(obj->address.objectId);
1531
1532 /* objsubid */
1533 values[i++] = Int32GetDatum(obj->address.objectSubId);
1534
1535 /* original */
1536 values[i++] = BoolGetDatum(obj->original);
1537
1538 /* normal */
1539 values[i++] = BoolGetDatum(obj->normal);
1540
1541 /* is_temporary */
1542 values[i++] = BoolGetDatum(obj->istemp);
1543
1544 /* object_type */
1545 values[i++] = CStringGetTextDatum(obj->objecttype);
1546
1547 /* schema_name */
1548 if (obj->schemaname)
1549 values[i++] = CStringGetTextDatum(obj->schemaname);
1550 else
1551 nulls[i++] = true;
1552
1553 /* object_name */
1554 if (obj->objname)
1555 values[i++] = CStringGetTextDatum(obj->objname);
1556 else
1557 nulls[i++] = true;
1558
1559 /* object_identity */
1560 if (obj->objidentity)
1561 values[i++] = CStringGetTextDatum(obj->objidentity);
1562 else
1563 nulls[i++] = true;
1564
1565 /* address_names and address_args */
1566 if (obj->addrnames)
1567 {
1568 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
1569
1570 if (obj->addrargs)
1571 values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
1572 else
1573 values[i++] = PointerGetDatum(construct_empty_array(TEXTOID));
1574 }
1575 else
1576 {
1577 nulls[i++] = true;
1578 nulls[i++] = true;
1579 }
1580
1581 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1582 }
1583
1584 /* clean up and return the tuplestore */
1585 tuplestore_donestoring(tupstore);
1586
1587 return (Datum) 0;
1588 }
1589
1590 /*
1591 * pg_event_trigger_table_rewrite_oid
1592 *
1593 * Make the Oid of the table going to be rewritten available to the user
1594 * function run by the Event Trigger.
1595 */
1596 Datum
pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)1597 pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS)
1598 {
1599 /*
1600 * Protect this function from being called out of context
1601 */
1602 if (!currentEventTriggerState ||
1603 currentEventTriggerState->table_rewrite_oid == InvalidOid)
1604 ereport(ERROR,
1605 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1606 errmsg("%s can only be called in a table_rewrite event trigger function",
1607 "pg_event_trigger_table_rewrite_oid()")));
1608
1609 PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid);
1610 }
1611
1612 /*
1613 * pg_event_trigger_table_rewrite_reason
1614 *
1615 * Make the rewrite reason available to the user.
1616 */
1617 Datum
pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)1618 pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS)
1619 {
1620 /*
1621 * Protect this function from being called out of context
1622 */
1623 if (!currentEventTriggerState ||
1624 currentEventTriggerState->table_rewrite_reason == 0)
1625 ereport(ERROR,
1626 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
1627 errmsg("%s can only be called in a table_rewrite event trigger function",
1628 "pg_event_trigger_table_rewrite_reason()")));
1629
1630 PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason);
1631 }
1632
1633 /*-------------------------------------------------------------------------
1634 * Support for DDL command deparsing
1635 *
1636 * The routines below enable an event trigger function to obtain a list of
1637 * DDL commands as they are executed. There are three main pieces to this
1638 * feature:
1639 *
1640 * 1) Within ProcessUtilitySlow, or some sub-routine thereof, each DDL command
1641 * adds a struct CollectedCommand representation of itself to the command list,
1642 * using the routines below.
1643 *
1644 * 2) Some time after that, ddl_command_end fires and the command list is made
1645 * available to the event trigger function via pg_event_trigger_ddl_commands();
1646 * the complete command details are exposed as a column of type pg_ddl_command.
1647 *
1648 * 3) An extension can install a function capable of taking a value of type
1649 * pg_ddl_command and transform it into some external, user-visible and/or
1650 * -modifiable representation.
1651 *-------------------------------------------------------------------------
1652 */
1653
1654 /*
1655 * Inhibit DDL command collection.
1656 */
1657 void
EventTriggerInhibitCommandCollection(void)1658 EventTriggerInhibitCommandCollection(void)
1659 {
1660 if (!currentEventTriggerState)
1661 return;
1662
1663 currentEventTriggerState->commandCollectionInhibited = true;
1664 }
1665
1666 /*
1667 * Re-establish DDL command collection.
1668 */
1669 void
EventTriggerUndoInhibitCommandCollection(void)1670 EventTriggerUndoInhibitCommandCollection(void)
1671 {
1672 if (!currentEventTriggerState)
1673 return;
1674
1675 currentEventTriggerState->commandCollectionInhibited = false;
1676 }
1677
1678 /*
1679 * EventTriggerCollectSimpleCommand
1680 * Save data about a simple DDL command that was just executed
1681 *
1682 * address identifies the object being operated on. secondaryObject is an
1683 * object address that was related in some way to the executed command; its
1684 * meaning is command-specific.
1685 *
1686 * For instance, for an ALTER obj SET SCHEMA command, objtype is the type of
1687 * object being moved, objectId is its OID, and secondaryOid is the OID of the
1688 * old schema. (The destination schema OID can be obtained by catalog lookup
1689 * of the object.)
1690 */
1691 void
EventTriggerCollectSimpleCommand(ObjectAddress address,ObjectAddress secondaryObject,Node * parsetree)1692 EventTriggerCollectSimpleCommand(ObjectAddress address,
1693 ObjectAddress secondaryObject,
1694 Node *parsetree)
1695 {
1696 MemoryContext oldcxt;
1697 CollectedCommand *command;
1698
1699 /* ignore if event trigger context not set, or collection disabled */
1700 if (!currentEventTriggerState ||
1701 currentEventTriggerState->commandCollectionInhibited)
1702 return;
1703
1704 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1705
1706 command = palloc(sizeof(CollectedCommand));
1707
1708 command->type = SCT_Simple;
1709 command->in_extension = creating_extension;
1710
1711 command->d.simple.address = address;
1712 command->d.simple.secondaryObject = secondaryObject;
1713 command->parsetree = copyObject(parsetree);
1714
1715 currentEventTriggerState->commandList = lappend(currentEventTriggerState->commandList,
1716 command);
1717
1718 MemoryContextSwitchTo(oldcxt);
1719 }
1720
1721 /*
1722 * EventTriggerAlterTableStart
1723 * Prepare to receive data on an ALTER TABLE command about to be executed
1724 *
1725 * Note we don't collect the command immediately; instead we keep it in
1726 * currentCommand, and only when we're done processing the subcommands we will
1727 * add it to the command list.
1728 */
1729 void
EventTriggerAlterTableStart(Node * parsetree)1730 EventTriggerAlterTableStart(Node *parsetree)
1731 {
1732 MemoryContext oldcxt;
1733 CollectedCommand *command;
1734
1735 /* ignore if event trigger context not set, or collection disabled */
1736 if (!currentEventTriggerState ||
1737 currentEventTriggerState->commandCollectionInhibited)
1738 return;
1739
1740 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1741
1742 command = palloc(sizeof(CollectedCommand));
1743
1744 command->type = SCT_AlterTable;
1745 command->in_extension = creating_extension;
1746
1747 command->d.alterTable.classId = RelationRelationId;
1748 command->d.alterTable.objectId = InvalidOid;
1749 command->d.alterTable.subcmds = NIL;
1750 command->parsetree = copyObject(parsetree);
1751
1752 command->parent = currentEventTriggerState->currentCommand;
1753 currentEventTriggerState->currentCommand = command;
1754
1755 MemoryContextSwitchTo(oldcxt);
1756 }
1757
1758 /*
1759 * Remember the OID of the object being affected by an ALTER TABLE.
1760 *
1761 * This is needed because in some cases we don't know the OID until later.
1762 */
1763 void
EventTriggerAlterTableRelid(Oid objectId)1764 EventTriggerAlterTableRelid(Oid objectId)
1765 {
1766 if (!currentEventTriggerState ||
1767 currentEventTriggerState->commandCollectionInhibited)
1768 return;
1769
1770 currentEventTriggerState->currentCommand->d.alterTable.objectId = objectId;
1771 }
1772
1773 /*
1774 * EventTriggerCollectAlterTableSubcmd
1775 * Save data about a single part of an ALTER TABLE.
1776 *
1777 * Several different commands go through this path, but apart from ALTER TABLE
1778 * itself, they are all concerned with AlterTableCmd nodes that are generated
1779 * internally, so that's all that this code needs to handle at the moment.
1780 */
1781 void
EventTriggerCollectAlterTableSubcmd(Node * subcmd,ObjectAddress address)1782 EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
1783 {
1784 MemoryContext oldcxt;
1785 CollectedATSubcmd *newsub;
1786
1787 /* ignore if event trigger context not set, or collection disabled */
1788 if (!currentEventTriggerState ||
1789 currentEventTriggerState->commandCollectionInhibited)
1790 return;
1791
1792 Assert(IsA(subcmd, AlterTableCmd));
1793 Assert(currentEventTriggerState->currentCommand != NULL);
1794 Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
1795
1796 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1797
1798 newsub = palloc(sizeof(CollectedATSubcmd));
1799 newsub->address = address;
1800 newsub->parsetree = copyObject(subcmd);
1801
1802 currentEventTriggerState->currentCommand->d.alterTable.subcmds =
1803 lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
1804
1805 MemoryContextSwitchTo(oldcxt);
1806 }
1807
1808 /*
1809 * EventTriggerAlterTableEnd
1810 * Finish up saving an ALTER TABLE command, and add it to command list.
1811 *
1812 * FIXME this API isn't considering the possibility that an xact/subxact is
1813 * aborted partway through. Probably it's best to add an
1814 * AtEOSubXact_EventTriggers() to fix this.
1815 */
1816 void
EventTriggerAlterTableEnd(void)1817 EventTriggerAlterTableEnd(void)
1818 {
1819 CollectedCommand *parent;
1820
1821 /* ignore if event trigger context not set, or collection disabled */
1822 if (!currentEventTriggerState ||
1823 currentEventTriggerState->commandCollectionInhibited)
1824 return;
1825
1826 parent = currentEventTriggerState->currentCommand->parent;
1827
1828 /* If no subcommands, don't collect */
1829 if (list_length(currentEventTriggerState->currentCommand->d.alterTable.subcmds) != 0)
1830 {
1831 MemoryContext oldcxt;
1832
1833 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1834
1835 currentEventTriggerState->commandList =
1836 lappend(currentEventTriggerState->commandList,
1837 currentEventTriggerState->currentCommand);
1838
1839 MemoryContextSwitchTo(oldcxt);
1840 }
1841 else
1842 pfree(currentEventTriggerState->currentCommand);
1843
1844 currentEventTriggerState->currentCommand = parent;
1845 }
1846
1847 /*
1848 * EventTriggerCollectGrant
1849 * Save data about a GRANT/REVOKE command being executed
1850 *
1851 * This function creates a copy of the InternalGrant, as the original might
1852 * not have the right lifetime.
1853 */
1854 void
EventTriggerCollectGrant(InternalGrant * istmt)1855 EventTriggerCollectGrant(InternalGrant *istmt)
1856 {
1857 MemoryContext oldcxt;
1858 CollectedCommand *command;
1859 InternalGrant *icopy;
1860 ListCell *cell;
1861
1862 /* ignore if event trigger context not set, or collection disabled */
1863 if (!currentEventTriggerState ||
1864 currentEventTriggerState->commandCollectionInhibited)
1865 return;
1866
1867 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1868
1869 /*
1870 * This is tedious, but necessary.
1871 */
1872 icopy = palloc(sizeof(InternalGrant));
1873 memcpy(icopy, istmt, sizeof(InternalGrant));
1874 icopy->objects = list_copy(istmt->objects);
1875 icopy->grantees = list_copy(istmt->grantees);
1876 icopy->col_privs = NIL;
1877 foreach(cell, istmt->col_privs)
1878 icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
1879
1880 /* Now collect it, using the copied InternalGrant */
1881 command = palloc(sizeof(CollectedCommand));
1882 command->type = SCT_Grant;
1883 command->in_extension = creating_extension;
1884 command->d.grant.istmt = icopy;
1885 command->parsetree = NULL;
1886
1887 currentEventTriggerState->commandList =
1888 lappend(currentEventTriggerState->commandList, command);
1889
1890 MemoryContextSwitchTo(oldcxt);
1891 }
1892
1893 /*
1894 * EventTriggerCollectAlterOpFam
1895 * Save data about an ALTER OPERATOR FAMILY ADD/DROP command being
1896 * executed
1897 */
1898 void
EventTriggerCollectAlterOpFam(AlterOpFamilyStmt * stmt,Oid opfamoid,List * operators,List * procedures)1899 EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
1900 List *operators, List *procedures)
1901 {
1902 MemoryContext oldcxt;
1903 CollectedCommand *command;
1904
1905 /* ignore if event trigger context not set, or collection disabled */
1906 if (!currentEventTriggerState ||
1907 currentEventTriggerState->commandCollectionInhibited)
1908 return;
1909
1910 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1911
1912 command = palloc(sizeof(CollectedCommand));
1913 command->type = SCT_AlterOpFamily;
1914 command->in_extension = creating_extension;
1915 ObjectAddressSet(command->d.opfam.address,
1916 OperatorFamilyRelationId, opfamoid);
1917 command->d.opfam.operators = operators;
1918 command->d.opfam.procedures = procedures;
1919 command->parsetree = (Node *) copyObject(stmt);
1920
1921 currentEventTriggerState->commandList =
1922 lappend(currentEventTriggerState->commandList, command);
1923
1924 MemoryContextSwitchTo(oldcxt);
1925 }
1926
1927 /*
1928 * EventTriggerCollectCreateOpClass
1929 * Save data about a CREATE OPERATOR CLASS command being executed
1930 */
1931 void
EventTriggerCollectCreateOpClass(CreateOpClassStmt * stmt,Oid opcoid,List * operators,List * procedures)1932 EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
1933 List *operators, List *procedures)
1934 {
1935 MemoryContext oldcxt;
1936 CollectedCommand *command;
1937
1938 /* ignore if event trigger context not set, or collection disabled */
1939 if (!currentEventTriggerState ||
1940 currentEventTriggerState->commandCollectionInhibited)
1941 return;
1942
1943 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1944
1945 command = palloc0(sizeof(CollectedCommand));
1946 command->type = SCT_CreateOpClass;
1947 command->in_extension = creating_extension;
1948 ObjectAddressSet(command->d.createopc.address,
1949 OperatorClassRelationId, opcoid);
1950 command->d.createopc.operators = operators;
1951 command->d.createopc.procedures = procedures;
1952 command->parsetree = (Node *) copyObject(stmt);
1953
1954 currentEventTriggerState->commandList =
1955 lappend(currentEventTriggerState->commandList, command);
1956
1957 MemoryContextSwitchTo(oldcxt);
1958 }
1959
1960 /*
1961 * EventTriggerCollectAlterTSConfig
1962 * Save data about an ALTER TEXT SEARCH CONFIGURATION command being
1963 * executed
1964 */
1965 void
EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt * stmt,Oid cfgId,Oid * dictIds,int ndicts)1966 EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
1967 Oid *dictIds, int ndicts)
1968 {
1969 MemoryContext oldcxt;
1970 CollectedCommand *command;
1971
1972 /* ignore if event trigger context not set, or collection disabled */
1973 if (!currentEventTriggerState ||
1974 currentEventTriggerState->commandCollectionInhibited)
1975 return;
1976
1977 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
1978
1979 command = palloc0(sizeof(CollectedCommand));
1980 command->type = SCT_AlterTSConfig;
1981 command->in_extension = creating_extension;
1982 ObjectAddressSet(command->d.atscfg.address,
1983 TSConfigRelationId, cfgId);
1984 command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
1985 memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
1986 command->d.atscfg.ndicts = ndicts;
1987 command->parsetree = (Node *) copyObject(stmt);
1988
1989 currentEventTriggerState->commandList =
1990 lappend(currentEventTriggerState->commandList, command);
1991
1992 MemoryContextSwitchTo(oldcxt);
1993 }
1994
1995 /*
1996 * EventTriggerCollectAlterDefPrivs
1997 * Save data about an ALTER DEFAULT PRIVILEGES command being
1998 * executed
1999 */
2000 void
EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt * stmt)2001 EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
2002 {
2003 MemoryContext oldcxt;
2004 CollectedCommand *command;
2005
2006 /* ignore if event trigger context not set, or collection disabled */
2007 if (!currentEventTriggerState ||
2008 currentEventTriggerState->commandCollectionInhibited)
2009 return;
2010
2011 oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
2012
2013 command = palloc0(sizeof(CollectedCommand));
2014 command->type = SCT_AlterDefaultPrivileges;
2015 command->d.defprivs.objtype = stmt->action->objtype;
2016 command->in_extension = creating_extension;
2017 command->parsetree = (Node *) copyObject(stmt);
2018
2019 currentEventTriggerState->commandList =
2020 lappend(currentEventTriggerState->commandList, command);
2021 MemoryContextSwitchTo(oldcxt);
2022 }
2023
2024 /*
2025 * In a ddl_command_end event trigger, this function reports the DDL commands
2026 * being run.
2027 */
2028 Datum
pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)2029 pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
2030 {
2031 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2032 TupleDesc tupdesc;
2033 Tuplestorestate *tupstore;
2034 MemoryContext per_query_ctx;
2035 MemoryContext oldcontext;
2036 ListCell *lc;
2037
2038 /*
2039 * Protect this function from being called out of context
2040 */
2041 if (!currentEventTriggerState)
2042 ereport(ERROR,
2043 (errcode(ERRCODE_E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED),
2044 errmsg("%s can only be called in an event trigger function",
2045 "pg_event_trigger_ddl_commands()")));
2046
2047 /* check to see if caller supports us returning a tuplestore */
2048 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
2049 ereport(ERROR,
2050 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2051 errmsg("set-valued function called in context that cannot accept a set")));
2052 if (!(rsinfo->allowedModes & SFRM_Materialize))
2053 ereport(ERROR,
2054 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2055 errmsg("materialize mode required, but it is not allowed in this context")));
2056
2057 /* Build a tuple descriptor for our result type */
2058 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2059 elog(ERROR, "return type must be a row type");
2060
2061 /* Build tuplestore to hold the result rows */
2062 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
2063 oldcontext = MemoryContextSwitchTo(per_query_ctx);
2064
2065 tupstore = tuplestore_begin_heap(true, false, work_mem);
2066 rsinfo->returnMode = SFRM_Materialize;
2067 rsinfo->setResult = tupstore;
2068 rsinfo->setDesc = tupdesc;
2069
2070 MemoryContextSwitchTo(oldcontext);
2071
2072 foreach(lc, currentEventTriggerState->commandList)
2073 {
2074 CollectedCommand *cmd = lfirst(lc);
2075 Datum values[9];
2076 bool nulls[9];
2077 ObjectAddress addr;
2078 int i = 0;
2079
2080 /*
2081 * For IF NOT EXISTS commands that attempt to create an existing
2082 * object, the returned OID is Invalid. Don't return anything.
2083 *
2084 * One might think that a viable alternative would be to look up the
2085 * Oid of the existing object and run the deparse with that. But
2086 * since the parse tree might be different from the one that created
2087 * the object in the first place, we might not end up in a consistent
2088 * state anyway.
2089 */
2090 if (cmd->type == SCT_Simple &&
2091 !OidIsValid(cmd->d.simple.address.objectId))
2092 continue;
2093
2094 MemSet(nulls, 0, sizeof(nulls));
2095
2096 switch (cmd->type)
2097 {
2098 case SCT_Simple:
2099 case SCT_AlterTable:
2100 case SCT_AlterOpFamily:
2101 case SCT_CreateOpClass:
2102 case SCT_AlterTSConfig:
2103 {
2104 char *identity;
2105 char *type;
2106 char *schema = NULL;
2107
2108 if (cmd->type == SCT_Simple)
2109 addr = cmd->d.simple.address;
2110 else if (cmd->type == SCT_AlterTable)
2111 ObjectAddressSet(addr,
2112 cmd->d.alterTable.classId,
2113 cmd->d.alterTable.objectId);
2114 else if (cmd->type == SCT_AlterOpFamily)
2115 addr = cmd->d.opfam.address;
2116 else if (cmd->type == SCT_CreateOpClass)
2117 addr = cmd->d.createopc.address;
2118 else if (cmd->type == SCT_AlterTSConfig)
2119 addr = cmd->d.atscfg.address;
2120
2121 type = getObjectTypeDescription(&addr);
2122 identity = getObjectIdentity(&addr);
2123
2124 /*
2125 * Obtain schema name, if any ("pg_temp" if a temp
2126 * object). If the object class is not in the supported
2127 * list here, we assume it's a schema-less object type,
2128 * and thus "schema" remains set to NULL.
2129 */
2130 if (is_objectclass_supported(addr.classId))
2131 {
2132 AttrNumber nspAttnum;
2133
2134 nspAttnum = get_object_attnum_namespace(addr.classId);
2135 if (nspAttnum != InvalidAttrNumber)
2136 {
2137 Relation catalog;
2138 HeapTuple objtup;
2139 Oid schema_oid;
2140 bool isnull;
2141
2142 catalog = heap_open(addr.classId, AccessShareLock);
2143 objtup = get_catalog_object_by_oid(catalog,
2144 addr.objectId);
2145 if (!HeapTupleIsValid(objtup))
2146 elog(ERROR, "cache lookup failed for object %u/%u",
2147 addr.classId, addr.objectId);
2148 schema_oid =
2149 heap_getattr(objtup, nspAttnum,
2150 RelationGetDescr(catalog), &isnull);
2151 if (isnull)
2152 elog(ERROR,
2153 "invalid null namespace in object %u/%u/%d",
2154 addr.classId, addr.objectId, addr.objectSubId);
2155 /* XXX not quite get_namespace_name_or_temp */
2156 if (isAnyTempNamespace(schema_oid))
2157 schema = pstrdup("pg_temp");
2158 else
2159 schema = get_namespace_name(schema_oid);
2160
2161 heap_close(catalog, AccessShareLock);
2162 }
2163 }
2164
2165 /* classid */
2166 values[i++] = ObjectIdGetDatum(addr.classId);
2167 /* objid */
2168 values[i++] = ObjectIdGetDatum(addr.objectId);
2169 /* objsubid */
2170 values[i++] = Int32GetDatum(addr.objectSubId);
2171 /* command tag */
2172 values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
2173 /* object_type */
2174 values[i++] = CStringGetTextDatum(type);
2175 /* schema */
2176 if (schema == NULL)
2177 nulls[i++] = true;
2178 else
2179 values[i++] = CStringGetTextDatum(schema);
2180 /* identity */
2181 values[i++] = CStringGetTextDatum(identity);
2182 /* in_extension */
2183 values[i++] = BoolGetDatum(cmd->in_extension);
2184 /* command */
2185 values[i++] = PointerGetDatum(cmd);
2186 }
2187 break;
2188
2189 case SCT_AlterDefaultPrivileges:
2190 /* classid */
2191 nulls[i++] = true;
2192 /* objid */
2193 nulls[i++] = true;
2194 /* objsubid */
2195 nulls[i++] = true;
2196 /* command tag */
2197 values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
2198 /* object_type */
2199 values[i++] = CStringGetTextDatum(stringify_adefprivs_objtype(
2200 cmd->d.defprivs.objtype));
2201 /* schema */
2202 nulls[i++] = true;
2203 /* identity */
2204 nulls[i++] = true;
2205 /* in_extension */
2206 values[i++] = BoolGetDatum(cmd->in_extension);
2207 /* command */
2208 values[i++] = PointerGetDatum(cmd);
2209 break;
2210
2211 case SCT_Grant:
2212 /* classid */
2213 nulls[i++] = true;
2214 /* objid */
2215 nulls[i++] = true;
2216 /* objsubid */
2217 nulls[i++] = true;
2218 /* command tag */
2219 values[i++] = CStringGetTextDatum(cmd->d.grant.istmt->is_grant ?
2220 "GRANT" : "REVOKE");
2221 /* object_type */
2222 values[i++] = CStringGetTextDatum(stringify_grantobjtype(
2223 cmd->d.grant.istmt->objtype));
2224 /* schema */
2225 nulls[i++] = true;
2226 /* identity */
2227 nulls[i++] = true;
2228 /* in_extension */
2229 values[i++] = BoolGetDatum(cmd->in_extension);
2230 /* command */
2231 values[i++] = PointerGetDatum(cmd);
2232 break;
2233 }
2234
2235 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2236 }
2237
2238 /* clean up and return the tuplestore */
2239 tuplestore_donestoring(tupstore);
2240
2241 PG_RETURN_VOID();
2242 }
2243
2244 /*
2245 * Return the GrantObjectType as a string, as it would appear in GRANT and
2246 * REVOKE commands.
2247 */
2248 static const char *
stringify_grantobjtype(GrantObjectType objtype)2249 stringify_grantobjtype(GrantObjectType objtype)
2250 {
2251 switch (objtype)
2252 {
2253 case ACL_OBJECT_COLUMN:
2254 return "COLUMN";
2255 case ACL_OBJECT_RELATION:
2256 return "TABLE";
2257 case ACL_OBJECT_SEQUENCE:
2258 return "SEQUENCE";
2259 case ACL_OBJECT_DATABASE:
2260 return "DATABASE";
2261 case ACL_OBJECT_DOMAIN:
2262 return "DOMAIN";
2263 case ACL_OBJECT_FDW:
2264 return "FOREIGN DATA WRAPPER";
2265 case ACL_OBJECT_FOREIGN_SERVER:
2266 return "FOREIGN SERVER";
2267 case ACL_OBJECT_FUNCTION:
2268 return "FUNCTION";
2269 case ACL_OBJECT_LANGUAGE:
2270 return "LANGUAGE";
2271 case ACL_OBJECT_LARGEOBJECT:
2272 return "LARGE OBJECT";
2273 case ACL_OBJECT_NAMESPACE:
2274 return "SCHEMA";
2275 case ACL_OBJECT_TABLESPACE:
2276 return "TABLESPACE";
2277 case ACL_OBJECT_TYPE:
2278 return "TYPE";
2279 }
2280
2281 elog(ERROR, "unrecognized grant object type: %d", (int) objtype);
2282 return "???"; /* keep compiler quiet */
2283 }
2284
2285 /*
2286 * Return the GrantObjectType as a string; as above, but use the spelling
2287 * in ALTER DEFAULT PRIVILEGES commands instead. Generally this is just
2288 * the plural.
2289 */
2290 static const char *
stringify_adefprivs_objtype(GrantObjectType objtype)2291 stringify_adefprivs_objtype(GrantObjectType objtype)
2292 {
2293 switch (objtype)
2294 {
2295 case ACL_OBJECT_COLUMN:
2296 return "COLUMNS";
2297 case ACL_OBJECT_RELATION:
2298 return "TABLES";
2299 case ACL_OBJECT_SEQUENCE:
2300 return "SEQUENCES";
2301 case ACL_OBJECT_DATABASE:
2302 return "DATABASES";
2303 case ACL_OBJECT_DOMAIN:
2304 return "DOMAINS";
2305 case ACL_OBJECT_FDW:
2306 return "FOREIGN DATA WRAPPERS";
2307 case ACL_OBJECT_FOREIGN_SERVER:
2308 return "FOREIGN SERVERS";
2309 case ACL_OBJECT_FUNCTION:
2310 return "FUNCTIONS";
2311 case ACL_OBJECT_LANGUAGE:
2312 return "LANGUAGES";
2313 case ACL_OBJECT_LARGEOBJECT:
2314 return "LARGE OBJECTS";
2315 case ACL_OBJECT_NAMESPACE:
2316 return "SCHEMAS";
2317 case ACL_OBJECT_TABLESPACE:
2318 return "TABLESPACES";
2319 case ACL_OBJECT_TYPE:
2320 return "TYPES";
2321 }
2322
2323 elog(ERROR, "unrecognized grant object type: %d", (int) objtype);
2324 return "???"; /* keep compiler quiet */
2325 }
2326