>From c84133168628f0eb919d2a607542a6ad804cf445 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 16 Mar 2015 14:41:13 -0300
Subject: [PATCH 28/29] deparse: support ALTER DEFAULT PRIVILEGES

---
 src/backend/commands/event_trigger.c |  67 +++++++++++++++++++
 src/backend/tcop/deparse_utility.c   | 124 ++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c           |   3 +-
 src/include/commands/event_trigger.h |   1 +
 src/include/tcop/deparse_utility.h   |   8 ++-
 5 files changed, 200 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index c19a6a5..002f999 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1870,6 +1870,52 @@ EventTriggerStashAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 	MemoryContextSwitchTo(oldcxt);
 }
 
+/*
+ * EventTriggerStashAlterDefPrivs
+ * 		Save data about an ALTER DEFAULT PRIVILEGES command being
+ * 		executed
+ */
+void
+EventTriggerStashAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
+{
+	MemoryContext	oldcxt;
+	StashedCommand *stashed;
+
+	if (currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+	stashed = palloc0(sizeof(StashedCommand));
+	stashed->type = SCT_AlterDefaultPrivileges;
+
+	switch (stmt->action->objtype)
+	{
+		case ACL_OBJECT_RELATION:
+			stashed->d.defprivs.objtype = "TABLES";
+			break;
+		case ACL_OBJECT_FUNCTION:
+			stashed->d.defprivs.objtype = "FUNCTIONS";
+			break;
+		case ACL_OBJECT_SEQUENCE:
+			stashed->d.defprivs.objtype = "SEQUENCES";
+			break;
+		case ACL_OBJECT_TYPE:
+			stashed->d.defprivs.objtype = "TYPES";
+			break;
+		default:
+			elog(ERROR, "unexpected object type %d", stmt->action->objtype);
+	}
+
+
+	stashed->in_extension = creating_extension;
+	stashed->parsetree = copyObject(stmt);
+
+	currentEventTriggerState->stash = lappend(currentEventTriggerState->stash,
+											  stashed);
+	MemoryContextSwitchTo(oldcxt);
+}
+
 Datum
 pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 {
@@ -2034,6 +2080,27 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 				/* command */
 				values[i++] = CStringGetTextDatum(command);
 			}
+			else if (cmd->type == SCT_AlterDefaultPrivileges)
+			{
+				/* classid */
+				nulls[i++] = true;
+				/* objid */
+				nulls[i++] = true;
+				/* objsubid */
+				nulls[i++] = true;
+				/* command tag */
+				values[i++] = CStringGetTextDatum(CreateCommandTag(cmd->parsetree));
+				/* object_type */
+				values[i++] = CStringGetTextDatum(cmd->d.defprivs.objtype);
+				/* schema */
+				nulls[i++] = true;
+				/* identity */
+				nulls[i++] = true;
+				/* in_extension */
+				values[i++] = BoolGetDatum(cmd->in_extension);
+				/* command */
+				values[i++] = CStringGetTextDatum(command);
+			}
 			else
 			{
 				Assert(cmd->type == SCT_Grant);
diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index 4a00479..a0f5849 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -5650,6 +5650,124 @@ deparse_AlterOpFamily(StashedCommand *cmd)
 }
 
 static ObjTree *
+deparse_AlterDefaultPrivilegesStmt(StashedCommand *cmd)
+{
+	ObjTree	   *alterStmt;
+	AlterDefaultPrivilegesStmt *stmt = (AlterDefaultPrivilegesStmt *) cmd->parsetree;
+	List	   *roles = NIL;
+	List	   *schemas = NIL;
+	List	   *grantees;
+	List	   *privs;
+	ListCell   *cell;
+	ObjTree	   *tmp;
+	ObjTree	   *grant;
+
+	alterStmt = new_objtree_VA("ALTER DEFAULT PRIVILEGES %{in_schema}s "
+							   "%{for_roles}s %{grant}s", 0);
+
+	/* Scan the parse node to dig out the FOR ROLE and IN SCHEMA clauses */
+	foreach(cell, stmt->options)
+	{
+		DefElem	   *opt = (DefElem *) lfirst(cell);
+		ListCell   *cell2;
+
+		Assert(IsA(opt, DefElem));
+		Assert(IsA(opt->arg, List));
+		if (strcmp(opt->defname, "roles") == 0)
+		{
+			foreach(cell2, (List *) opt->arg)
+			{
+				Value  *val = lfirst(cell2);
+
+				roles = lappend(roles,
+								new_string_object(strVal(val)));
+			}
+		}
+		else if (strcmp(opt->defname, "schemas") == 0)
+		{
+			foreach(cell2, (List *) opt->arg)
+			{
+				Value  *val = lfirst(cell2);
+
+				schemas = lappend(schemas,
+								  new_string_object(strVal(val)));
+			}
+		}
+	}
+
+	/* Add the FOR ROLE clause, if any */
+	tmp = new_objtree_VA("FOR ROLE %{roles:, }I", 0);
+	append_array_object(tmp, "roles", roles);
+	if (roles == NIL)
+		append_bool_object(tmp, "present", false);
+	append_object_object(alterStmt, "for_roles", tmp);
+
+	/* Add the IN SCHEMA clause, if any */
+	tmp = new_objtree_VA("IN SCHEMA %{schemas:, }I", 0);
+	append_array_object(tmp, "schemas", schemas);
+	if (schemas == NIL)
+		append_bool_object(tmp, "present", false);
+	append_object_object(alterStmt, "in_schema", tmp);
+
+	/* Add the GRANT subcommand */
+	if (stmt->action->is_grant)
+		grant = new_objtree_VA("GRANT %{privileges:, }s ON %{target}s "
+							   "TO %{grantees:, }I %{grant_option}s", 0);
+	else
+		grant = new_objtree_VA("REVOKE %{grant_option}s %{privileges:, }s "
+							   "ON %{target}s FROM %{grantees:, }I", 0);
+
+	/* add the GRANT OPTION clause */
+	tmp = new_objtree_VA(stmt->action->is_grant ?
+						   "WITH GRANT OPTION" : "GRANT OPTION FOR",
+						   1, "present", ObjTypeBool,
+						   stmt->action->grant_option);
+	append_object_object(grant, "grant_option", tmp);
+
+	/* add the target object type */
+	append_string_object(grant, "target", cmd->d.defprivs.objtype);
+
+	/* add the grantee list */
+	grantees = NIL;
+	foreach(cell, stmt->action->grantees)
+	{
+		RoleSpec   *spec = (RoleSpec *) lfirst(cell);
+		char	   *role = spec->roletype == ROLESPEC_PUBLIC ? "PUBLIC" :
+			get_rolespec_name((Node *) spec);
+
+		grantees = lappend(grantees, new_string_object(role));
+	}
+	append_array_object(grant, "grantees", grantees);
+
+	/*
+	 * Add the privileges list.  This uses the parser struct, as opposed to the
+	 * InternalGrant format used by GRANT.  There are enough other differences
+	 * that this doesn't seem worth improving.
+	 */
+	if (stmt->action->privileges == NIL)
+		privs = list_make1(new_string_object("ALL PRIVILEGES"));
+	else
+	{
+		privs = NIL;
+
+		foreach(cell, stmt->action->privileges)
+		{
+			AccessPriv *priv = lfirst(cell);
+
+			Assert(priv->cols == NIL);
+			privs = lappend(privs,
+							new_string_object(priv->priv_name));
+		}
+	}
+
+	append_array_object(grant, "privileges", privs);
+
+	append_object_object(alterStmt, "grant", grant);
+
+	return alterStmt;
+}
+
+static ObjTree *
 deparse_AlterTableStmt(StashedCommand *cmd)
 {
 	ObjTree	   *alterTableStmt;
@@ -6419,7 +6537,8 @@ deparse_simple_command(StashedCommand *cmd)
 			break;
 
 		case T_AlterDefaultPrivilegesStmt:
-			elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+			/* handled elsewhere */
+			elog(ERROR, "unexpected command type %s", CreateCommandTag(parsetree));
 			break;
 
 		case T_CreatePolicyStmt:	/* CREATE POLICY */
@@ -6501,6 +6620,9 @@ deparse_utility_command(StashedCommand *cmd)
 		case SCT_AlterOpFamily:
 			tree = deparse_AlterOpFamily(cmd);
 			break;
+		case SCT_AlterDefaultPrivileges:
+			tree = deparse_AlterDefaultPrivilegesStmt(cmd);
+			break;
 		default:
 			elog(ERROR, "unexpected deparse node type %d", cmd->type);
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3bc2387..868ae13 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1462,7 +1462,8 @@ ProcessUtilitySlow(Node *parsetree,
 
 			case T_AlterDefaultPrivilegesStmt:
 				ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
-				/* XXX FIXME WTF? */
+				EventTriggerStashAlterDefPrivs(parsetree);
+				commandStashed = true;
 				break;
 
 			case T_CreatePolicyStmt:	/* CREATE POLICY */
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index fd34001..b8b39f9 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -75,5 +75,6 @@ extern void EventTriggerAlterTableEnd(void);
 extern void EventTriggerStashGrant(InternalGrant *istmt);
 extern void EventTriggerStashAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 							List *operators, List *procedures);
+extern void EventTriggerStashAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt);
 
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index b663ba2..a0ed8e4 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -30,7 +30,8 @@ typedef enum StashedCommandType
 	SCT_Simple,
 	SCT_AlterTable,
 	SCT_Grant,
-	SCT_AlterOpFamily
+	SCT_AlterOpFamily,
+	SCT_AlterDefaultPrivileges
 } StashedCommandType;
 
 /*
@@ -77,6 +78,11 @@ typedef struct StashedCommand
 			List   *operators;
 			List   *procedures;
 		} opfam;
+
+		struct
+		{
+			char   *objtype;
+		} defprivs;
 	} d;
 } StashedCommand;
 
-- 
2.1.4

