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