Support user mappings in get_object_address

Since commit 72dd233d3e we were trying to obtain object addressing
information in sql_drop event triggers, but that caused failures when
the drops involved user mappings.  This addition enables that to work
again.  Naturally, pg_get_object_address can work with these objects
now, too.

I toyed with the idea of removing DropUserMappingStmt as a node and
using DropStmt instead in the DropUserMappingStmt grammar production,
but that didn't go very well: for one thing the messages thrown by the
specific code are specialized (you get "server not found" if you specify
the wrong server, instead of a generic "user mapping for ... not found"
which you'd get it we were to merge this with RemoveObjects --- unless
we added even more special cases).  For another thing, it would require
to pass RoleSpec nodes through the objname/objargs representation used
by RemoveObjects, which works in isolation, but gets messy when
pg_get_object_address is involved.  So I dropped this part for now.

Reviewed by Stephen Frost.
This commit is contained in:
Alvaro Herrera 2015-03-11 17:01:13 -03:00
parent 1ce7a57ca6
commit 890192e99a
7 changed files with 109 additions and 13 deletions

View File

@ -520,7 +520,7 @@ ObjectTypeMap[] =
/* OCLASS_FOREIGN_SERVER */
{ "server", OBJECT_FOREIGN_SERVER },
/* OCLASS_USER_MAPPING */
{ "user mapping", -1 }, /* unmapped */
{ "user mapping", OBJECT_USER_MAPPING },
/* OCLASS_DEFACL */
{ "default acl", -1 }, /* unmapped */
/* OCLASS_EXTENSION */
@ -555,6 +555,8 @@ static ObjectAddress get_object_address_type(ObjectType objtype,
List *objname, bool missing_ok);
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 const ObjectPropertyType *get_object_property_data(Oid class_id);
static void getRelationDescription(StringInfo buffer, Oid relid);
@ -769,6 +771,10 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
address.objectId = get_ts_config_oid(objname, missing_ok);
address.objectSubId = 0;
break;
case OBJECT_USER_MAPPING:
address = get_object_address_usermapping(objname, objargs,
missing_ok);
break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@ -1372,6 +1378,75 @@ get_object_address_opcf(ObjectType objtype,
return address;
}
/*
* Find the ObjectAddress for a user mapping.
*/
static ObjectAddress
get_object_address_usermapping(List *objname, List *objargs, bool missing_ok)
{
ObjectAddress address;
Oid userid;
char *username;
char *servername;
ForeignServer *server;
HeapTuple tp;
ObjectAddressSet(address, UserMappingRelationId, InvalidOid);
/* fetch string names from input lists, for error messages */
username = strVal(linitial(objname));
servername = strVal(linitial(objargs));
/* look up pg_authid OID of mapped user; InvalidOid if PUBLIC */
if (strcmp(username, "public") == 0)
userid = InvalidOid;
else
{
tp = SearchSysCache1(AUTHNAME,
CStringGetDatum(username));
if (!HeapTupleIsValid(tp))
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("user mapping for user \"%s\" in server \"%s\" does not exist",
username, servername)));
return address;
}
userid = HeapTupleGetOid(tp);
ReleaseSysCache(tp);
}
/* Now look up the pg_user_mapping tuple */
server = GetForeignServerByName(servername, true);
if (!server)
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("server \"%s\" does not exist", servername)));
return address;
}
tp = SearchSysCache2(USERMAPPINGUSERSERVER,
ObjectIdGetDatum(userid),
ObjectIdGetDatum(server->serverid));
if (!HeapTupleIsValid(tp))
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("user mapping for user \"%s\" in server \"%s\" does not exist",
username, servername)));
return address;
}
address.objectId = HeapTupleGetOid(tp);
ReleaseSysCache(tp);
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.
@ -1523,6 +1598,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
case OBJECT_CAST:
case OBJECT_USER_MAPPING:
if (list_length(args) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),

View File

@ -1092,6 +1092,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
case OBJECT_VIEW:
return true;
}

View File

@ -1268,6 +1268,7 @@ typedef enum ObjectType
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
OBJECT_VIEW
} ObjectType;

View File

@ -110,6 +110,12 @@ revoke all on table event_trigger_fire1 from public;
NOTICE: test_event_trigger: ddl_command_end REVOKE
drop table event_trigger_fire1;
NOTICE: test_event_trigger: ddl_command_end DROP TABLE
create foreign data wrapper useless;
NOTICE: test_event_trigger: ddl_command_end CREATE FOREIGN DATA WRAPPER
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 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"
@ -125,10 +131,11 @@ alter event trigger regress_event_trigger rename to regress_event_trigger3;
-- should fail, doesn't exist any more
drop event trigger regress_event_trigger;
ERROR: event trigger "regress_event_trigger" does not exist
-- should fail, regression_bob owns regress_event_trigger2/3
-- should fail, regression_bob owns some objects
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 user mapping for regression_bob on server useless_server
-- cleanup before next test
-- these are all OK; the second one should emit a NOTICE
drop event trigger if exists regress_event_trigger2;

View File

@ -28,6 +28,8 @@ CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0);
CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
CREATE POLICY genpol ON addr_nsp.gentable;
CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
-- test some error cases
SELECT pg_get_object_address('stone', '{}', '{}');
ERROR: unrecognized object type "stone"
@ -42,8 +44,7 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
('operator of access method'), ('function of access method'),
('user mapping')
('operator of access method'), ('function of access method')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@ -61,7 +62,6 @@ WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for operator of access method: unsupported object type "operator of access method"
WARNING: error for function of access method: unsupported object type "function of access method"
WARNING: error for user mapping: unsupported object type "user mapping"
DO $$
DECLARE
objtype text;
@ -77,7 +77,7 @@ BEGIN
('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
('text search parser'), ('text search dictionary'),
('text search template'), ('text search configuration'),
('policy')
('policy'), ('user mapping')
LOOP
FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
LOOP
@ -249,6 +249,12 @@ WARNING: error for policy,{addr_nsp,zwei},{}: relation "addr_nsp" does not exis
WARNING: error for policy,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist
WARNING: error for policy,{eins,zwei,drei},{}: schema "eins" does not exist
WARNING: error for policy,{eins,zwei,drei},{integer}: schema "eins" does not exist
WARNING: error for user mapping,{eins},{}: argument list length must be exactly 1
WARNING: error for user mapping,{eins},{integer}: user mapping for user "eins" in server "integer" does not exist
WARNING: error for user mapping,{addr_nsp,zwei},{}: argument list length must be exactly 1
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
-- these object types cannot be qualified names
SELECT pg_get_object_address('language', '{one}', '{}');
ERROR: language "one" does not exist
@ -334,7 +340,7 @@ WITH objects (type, name, args) AS (VALUES
-- tablespace
('foreign-data wrapper', '{addr_fdw}', '{}'),
('server', '{addr_fserv}', '{}'),
-- user mapping
('user mapping', '{regtest_addr_user}', '{integer}'),
-- extension
-- event trigger
('policy', '{addr_nsp, gentable, genpol}', '{}')
@ -365,6 +371,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
foreign table | addr_nsp | genftable | addr_nsp.genftable | t
role | | regtest_addr_user | regtest_addr_user | t
server | | addr_fserv | addr_fserv | t
user mapping | | | regtest_addr_user on server integer | t
foreign-data wrapper | | addr_fdw | addr_fdw | t
default value | | | for addr_nsp.gentable.b | t
cast | | | (bigint AS integer) | t
@ -384,7 +391,7 @@ 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
(35 rows)
(36 rows)
---
--- Cleanup resources

View File

@ -107,6 +107,9 @@ grant all on table event_trigger_fire1 to public;
comment on table event_trigger_fire1 is 'here is a comment';
revoke all on table event_trigger_fire1 from public;
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 owner to non-superuser should fail
alter event trigger regress_event_trigger owner to regression_bob;
@ -124,7 +127,7 @@ alter event trigger regress_event_trigger rename to regress_event_trigger3;
-- should fail, doesn't exist any more
drop event trigger regress_event_trigger;
-- should fail, regression_bob owns regress_event_trigger2/3
-- should fail, regression_bob owns some objects
drop role regression_bob;
-- cleanup before next test

View File

@ -32,6 +32,8 @@ CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0);
CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
CREATE POLICY genpol ON addr_nsp.gentable;
CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
-- test some error cases
SELECT pg_get_object_address('stone', '{}', '{}');
@ -45,8 +47,7 @@ DECLARE
BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'),
('operator of access method'), ('function of access method'),
('user mapping')
('operator of access method'), ('function of access method')
LOOP
BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}');
@ -72,7 +73,7 @@ BEGIN
('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
('text search parser'), ('text search dictionary'),
('text search template'), ('text search configuration'),
('policy')
('policy'), ('user mapping')
LOOP
FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
LOOP
@ -154,7 +155,7 @@ WITH objects (type, name, args) AS (VALUES
-- tablespace
('foreign-data wrapper', '{addr_fdw}', '{}'),
('server', '{addr_fserv}', '{}'),
-- user mapping
('user mapping', '{regtest_addr_user}', '{integer}'),
-- extension
-- event trigger
('policy', '{addr_nsp, gentable, genpol}', '{}')