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