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