summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlvaro Herrera2015-03-11 22:23:47 +0000
committerAlvaro Herrera2015-03-11 22:23:47 +0000
commit4464303405f1f886d63f8316386621cd7436c5d6 (patch)
tree5e9b0ce63f073d9a4cca047330bcf6a145df30f8
parentd4d7777548ed3ea2ca579003e37f9df4d0e0ab9e (diff)
Support default ACLs in get_object_address
In the spirit of 890192e99af, this time add support for the things living in the pg_default_acl catalog. These are not really "objects", but they show up as such in event triggers. There is no "DROP DEFAULT PRIVILEGES" or similar command, so it doesn't look like the new representation given would be useful anywhere else, so I didn't try to use it outside objectaddress.c. (That might be a bug in itself, but that would be material for another commit.) Reviewed by Stephen Frost.
-rw-r--r--src/backend/catalog/objectaddress.c137
-rw-r--r--src/backend/commands/event_trigger.c1
-rw-r--r--src/include/nodes/parsenodes.h1
-rw-r--r--src/test/regress/expected/event_trigger.out4
-rw-r--r--src/test/regress/expected/object_address.out17
-rw-r--r--src/test/regress/sql/event_trigger.sql2
-rw-r--r--src/test/regress/sql/object_address.sql7
7 files changed, 158 insertions, 11 deletions
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 67c14020e5c..142bc689e95 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 */
@@ -1448,6 +1454,113 @@ 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 objtype;
+ char *objtype_str;
+ ObjectAddress address;
+
+ ObjectAddressSet(address, DefaultAclRelationId, InvalidOid);
+
+ /*
+ * First figure out the textual attributes so that they can be used for
+ * error reporting.
+ */
+ username = strVal(linitial(objname));
+ if (list_length(objname) >= 2)
+ schema = (char *) strVal(lsecond(objname));
+ else
+ schema = NULL;
+
+ /*
+ * Decode defaclobjtype. Only first char is considered; the rest of the
+ * string, if any, is blissfully ignored.
+ */
+ objtype = ((char *) strVal(linitial(objargs)))[0];
+ switch (objtype)
+ {
+ case DEFACLOBJ_RELATION:
+ objtype_str = "tables";
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ objtype_str = "sequences";
+ break;
+ case DEFACLOBJ_FUNCTION:
+ objtype_str = "functions";
+ break;
+ case DEFACLOBJ_TYPE:
+ objtype_str = "types";
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized default ACL object type %c", objtype),
+ errhint("Valid object types are 'r', 'S', 'f', and 'T'.")));
+ }
+
+ /*
+ * Look up user ID. Behave as "default ACL not found" if the user doesn't
+ * exist.
+ */
+ tp = SearchSysCache1(AUTHNAME,
+ CStringGetDatum(username));
+ if (!HeapTupleIsValid(tp))
+ goto not_found;
+ userid = HeapTupleGetOid(tp);
+ ReleaseSysCache(tp);
+
+ /*
+ * If a schema name was given, look up its OID. If it doesn't exist,
+ * behave as "default ACL not found".
+ */
+ if (schema)
+ {
+ schemaid = get_namespace_oid(schema, true);
+ if (schemaid == InvalidOid)
+ goto not_found;
+ }
+ else
+ schemaid = InvalidOid;
+
+ /* Finally, look up the pg_default_acl object */
+ tp = SearchSysCache3(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(schemaid),
+ CharGetDatum(objtype));
+ if (!HeapTupleIsValid(tp))
+ goto not_found;
+
+ address.objectId = HeapTupleGetOid(tp);
+ ReleaseSysCache(tp);
+
+ return address;
+
+not_found:
+ if (!missing_ok)
+ {
+ if (schema)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist",
+ username, schema, objtype_str)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("default ACL for user \"%s\" on %s does not exist",
+ username, objtype_str)));
+ }
+ 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 +1712,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_OPFAMILY:
case OBJECT_CAST:
case OBJECT_USER_MAPPING:
+ case OBJECT_DEFACL:
if (list_length(args) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -4024,10 +4138,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);
@@ -4047,19 +4159,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)
{
@@ -4081,6 +4194,14 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ if (objname)
+ {
+ *objname = list_make1(username);
+ if (schema)
+ *objname = lappend(*objname, schema);
+ *objargs = list_make1(psprintf("%c", defacl->defaclobjtype));
+ }
+
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 4e446bd25cd..3fec57ea237 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 1279aca882c..38ed661122d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1239,6 +1239,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 b87d5034369..1dace02782f 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -116,6 +116,9 @@ create server useless_server foreign data wrapper useless;
NOTICE: test_event_trigger: ddl_command_end CREATE SERVER
create user mapping for regression_bob server useless_server;
NOTICE: test_event_trigger: ddl_command_end CREATE USER MAPPING
+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"
@@ -135,6 +138,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 e72abda90aa..3bcbcd8b65f 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 exactly 1
+WARNING: error for default acl,{eins},{integer}: unrecognized default ACL object type i
+WARNING: error for default acl,{addr_nsp,zwei},{}: argument list length must be exactly 1
+WARNING: error for default acl,{addr_nsp,zwei},{integer}: unrecognized default ACL object type i
+WARNING: error for default acl,{eins,zwei,drei},{}: argument list length must be exactly 1
+WARNING: error for default acl,{eins,zwei,drei},{integer}: unrecognized default ACL object 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,public}', '{r}'),
+ ('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 bcfeb3a8693..1b7346409c8 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -110,6 +110,8 @@ drop table event_trigger_fire1;
create foreign data wrapper useless;
create server useless_server foreign data wrapper useless;
create user mapping for regression_bob server useless_server;
+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 b714b529c8d..a49f03fdf7d 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,public}', '{r}'),
+ ('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;