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