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