>From 21c30e970b1d22482660dfcc154767f3b5c03ae4 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 6 Mar 2015 17:16:29 -0300
Subject: [PATCH 2/3] deparse/core: get_object_address support default ACLs

---
 src/backend/catalog/objectaddress.c          | 125 +++++++++++++++++++++++++--
 src/backend/commands/event_trigger.c         |   1 +
 src/include/nodes/parsenodes.h               |   1 +
 src/test/regress/expected/event_trigger.out  |   5 ++
 src/test/regress/expected/object_address.out |  17 +++-
 src/test/regress/sql/event_trigger.sql       |   4 +
 src/test/regress/sql/object_address.sql      |   7 +-
 7 files changed, 149 insertions(+), 11 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 5553ec7..32eea58 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -522,7 +522,7 @@ ObjectTypeMap[] =
 	/* OCLASS_USER_MAPPING */
 	{ "user mapping", OBJECT_USER_MAPPING },
 	/* OCLASS_DEFACL */
-	{ "default acl", -1 },		/* unmapped */
+	{ "default acl", OBJECT_DEFACL },
 	/* OCLASS_EXTENSION */
 	{ "extension", OBJECT_EXTENSION },
 	/* OCLASS_EVENT_TRIGGER */
@@ -557,6 +557,8 @@ static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
 						List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_usermapping(List *objname,
 							   List *objargs, bool missing_ok);
+static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
+						  bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
 static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -775,6 +777,10 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				address = get_object_address_usermapping(objname, objargs,
 														 missing_ok);
 				break;
+			case OBJECT_DEFACL:
+				address = get_object_address_defacl(objname, objargs,
+													missing_ok);
+				break;
 			default:
 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 				/* placate compiler, in case it thinks elog might return */
@@ -1437,6 +1443,95 @@ get_object_address_usermapping(List *objname, List *objargs, bool missing_ok)
 }
 
 /*
+ * Find the ObjectAddress for a default ACL.
+ */
+static ObjectAddress
+get_object_address_defacl(List *objname, List *objargs, bool missing_ok)
+{
+	HeapTuple	tp;
+	Oid			userid;
+	Oid			schemaid;
+	char	   *username;
+	char	   *schema;
+	char		objtyp;
+	char	   *stuff;
+	ObjectAddress address;
+
+	ObjectAddressSet(address, DefaultAclRelationId, InvalidOid);
+
+	/*
+	 * Figure out the textual attributes first so that they can be used for
+	 * error reporting.
+	 */
+	username = strVal(linitial(objname));
+	if (list_length(objargs) >= 2)
+		schema = (char *) strVal(llast(objargs));
+	else
+		schema = NULL;
+
+	objtyp = ((char *) strVal(linitial(objargs)))[0];
+	switch (objtyp)
+	{
+		case DEFACLOBJ_RELATION:
+			stuff = "tables";
+			break;
+		case DEFACLOBJ_SEQUENCE:
+			stuff = "sequences";
+			break;
+		case DEFACLOBJ_FUNCTION:
+			stuff = "functions";
+			break;
+		case DEFACLOBJ_TYPE:
+			stuff = "types";
+			break;
+		default:
+			elog(ERROR, "invalid defacl type %c", objtyp);
+	}
+
+	tp = SearchSysCache1(AUTHNAME,
+						 CStringGetDatum(username));
+	if (!HeapTupleIsValid(tp))
+		goto not_found;
+
+	userid = HeapTupleGetOid(tp);
+	ReleaseSysCache(tp);
+
+	if (schema)
+	{
+		schemaid = get_namespace_oid(schema, true);
+		if (schemaid == InvalidOid)
+			goto not_found;
+	}
+	else
+		schemaid = InvalidOid;
+
+	tp = SearchSysCache3(DEFACLROLENSPOBJ,
+						 ObjectIdGetDatum(userid),
+						 ObjectIdGetDatum(schemaid),
+						 CharGetDatum(objtyp));
+	if (!HeapTupleIsValid(tp))
+		goto not_found;
+
+	address.objectId = HeapTupleGetOid(tp);
+	ReleaseSysCache(tp);
+
+	return address;
+
+not_found:
+	if (schema)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist",
+						username, schema, stuff)));
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("default ACL for user \"%s\" on %s does not exist",
+						username, stuff)));
+	return address;
+}
+
+/*
  * Convert an array of TEXT into a List of string Values, as emitted by the
  * parser, which is what get_object_address uses as input.
  */
@@ -1599,6 +1694,13 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("argument list length must be exactly %d", 2)));
 			break;
+		case OBJECT_DEFACL:
+			if ((list_length(args) < 1) ||
+				(list_length(args) > 2))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("argument list length must be between %d and %d", 1, 2)));
+			break;
 		default:
 			break;
 	}
@@ -4013,10 +4115,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 				SysScanDesc rcscan;
 				HeapTuple	tup;
 				Form_pg_default_acl defacl;
-
-				/* no objname support */
-				if (objname)
-					*objname = NIL;
+				char	   *schema;
+				char	   *username;
 
 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
@@ -4036,19 +4136,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
+				username = GetUserNameFromId(defacl->defaclrole);
 				appendStringInfo(&buffer,
 								 "for role %s",
-					quote_identifier(GetUserNameFromId(defacl->defaclrole)));
+								 quote_identifier(username));
 
 				if (OidIsValid(defacl->defaclnamespace))
 				{
-					char	   *schema;
-
 					schema = get_namespace_name(defacl->defaclnamespace);
 					appendStringInfo(&buffer,
 									 " in schema %s",
 									 quote_identifier(schema));
 				}
+				else
+					schema = NULL;
 
 				switch (defacl->defaclobjtype)
 				{
@@ -4070,6 +4171,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 						break;
 				}
 
+				if (objname)
+				{
+					*objname = list_make1(username);
+					*objargs = list_make1(psprintf("%c", defacl->defaclobjtype));
+					if (schema)
+						*objargs = lappend(*objargs, schema);
+				}
+
 				systable_endscan(rcscan);
 				heap_close(defaclrel, AccessShareLock);
 				break;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4e446bd..3fec57e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1065,6 +1065,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_COLUMN:
 		case OBJECT_COLLATION:
 		case OBJECT_CONVERSION:
+		case OBJECT_DEFACL:
 		case OBJECT_DEFAULT:
 		case OBJECT_DOMAIN:
 		case OBJECT_DOMCONSTRAINT:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index dbde840..36c5d43 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1219,6 +1219,7 @@ typedef enum ObjectType
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
 	OBJECT_DEFAULT,
+	OBJECT_DEFACL,
 	OBJECT_DOMAIN,
 	OBJECT_DOMCONSTRAINT,
 	OBJECT_EVENT_TRIGGER,
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 4ff7f35..47dc023 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -122,6 +122,10 @@ NOTICE:  test_event_trigger: ddl_command_end CREATE SERVER
 -- regress_event_trigger_end should fire here
 create user mapping for regression_bob server useless_server;
 NOTICE:  test_event_trigger: ddl_command_end CREATE USER MAPPING
+-- regress_event_trigger_end should fire here
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
+NOTICE:  test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
@@ -141,6 +145,7 @@ ERROR:  event trigger "regress_event_trigger" does not exist
 drop role regression_bob;
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
 DETAIL:  owner of event trigger regress_event_trigger3
+owner of default privileges on new relations belonging to role regression_bob
 owner of user mapping for regression_bob on server useless_server
 -- cleanup before next test
 -- these are all OK; the second one should emit a NOTICE
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index e72abda..e7bb06a 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -30,6 +30,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
 ERROR:  unrecognized object type "stone"
@@ -77,7 +79,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy'), ('user mapping')
+		('policy'), ('user mapping'), ('default acl')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -255,6 +257,12 @@ WARNING:  error for user mapping,{addr_nsp,zwei},{}: argument list length must b
 WARNING:  error for user mapping,{addr_nsp,zwei},{integer}: user mapping for user "addr_nsp" in server "integer" does not exist
 WARNING:  error for user mapping,{eins,zwei,drei},{}: argument list length must be exactly 1
 WARNING:  error for user mapping,{eins,zwei,drei},{integer}: user mapping for user "eins" in server "integer" does not exist
+WARNING:  error for default acl,{eins},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{eins},{integer}: invalid defacl type i
+WARNING:  error for default acl,{addr_nsp,zwei},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{addr_nsp,zwei},{integer}: invalid defacl type i
+WARNING:  error for default acl,{eins,zwei,drei},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{eins,zwei,drei},{integer}: invalid defacl type i
 -- these object types cannot be qualified names
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -341,6 +349,8 @@ WITH objects (type, name, args) AS (VALUES
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
 				('user mapping', '{regtest_addr_user}', '{integer}'),
+				('default acl', '{regtest_addr_user}', '{r,public}'),
+				('default acl', '{regtest_addr_user}', '{r}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -355,6 +365,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
 	ORDER BY addr1.classid, addr1.objid;
            type            |   schema   |       name        |                               identity                               | ?column? 
 ---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
+ default acl               |            |                   | for role regtest_addr_user in schema public on tables                | t
+ default acl               |            |                   | for role regtest_addr_user on tables                                 | t
  type                      | pg_catalog | _int4             | integer[]                                                            | t
  type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype                                                 | t
  type                      | addr_nsp   | genenum           | addr_nsp.genenum                                                     | t
@@ -391,11 +403,12 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
  text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
  text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
-(36 rows)
+(38 rows)
 
 ---
 --- Cleanup resources
 ---
 DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 DROP SCHEMA addr_nsp CASCADE;
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index f89fa71..2569a6c 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -123,6 +123,10 @@ create server useless_server foreign data wrapper useless;
 -- regress_event_trigger_end should fire here
 create user mapping for regression_bob server useless_server;
 
+-- regress_event_trigger_end should fire here
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
+
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index b714b52..f4d940c 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -34,6 +34,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
@@ -73,7 +75,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy'), ('user mapping')
+		('policy'), ('user mapping'), ('default acl')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -156,6 +158,8 @@ WITH objects (type, name, args) AS (VALUES
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
 				('user mapping', '{regtest_addr_user}', '{integer}'),
+				('default acl', '{regtest_addr_user}', '{r,public}'),
+				('default acl', '{regtest_addr_user}', '{r}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -176,4 +180,5 @@ DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 
 DROP SCHEMA addr_nsp CASCADE;
 
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;
-- 
2.1.4

