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