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