]> granicus.if.org Git - postgresql/commitdiff
Support user mappings in get_object_address
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2015 20:01:13 +0000 (17:01 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2015 20:04:27 +0000 (17:04 -0300)
Since commit 72dd233d3ef 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.

src/backend/catalog/objectaddress.c
src/backend/commands/event_trigger.c
src/include/nodes/parsenodes.h
src/test/regress/expected/event_trigger.out
src/test/regress/expected/object_address.out
src/test/regress/sql/event_trigger.sql
src/test/regress/sql/object_address.sql

index 541912bac5e2800534c525fc7b4991d9b039814f..67c14020e5c9e1736f1a5d1715555aa78bd3de4b 100644 (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),
index f573c9ce706e4e47be150ef5b3a1e9707697091c..4e446bd25cdbedfa4d067d56de6a4e55b60ccfc6 100644 (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;
        }
index c226b039cf1c023ebec09d87e083833b13b80726..1279aca882cf422ccaeb0078f80ebbbd879445f7 100644 (file)
@@ -1268,6 +1268,7 @@ typedef enum ObjectType
        OBJECT_TSPARSER,
        OBJECT_TSTEMPLATE,
        OBJECT_TYPE,
+       OBJECT_USER_MAPPING,
        OBJECT_VIEW
 } ObjectType;
 
index 7f7dd820ff27d8db67808af85fc85072af05a718..b87d5034369ece2d47c687bcf247442985ff0563 100644 (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;
index dcf1b46ecd74c5a4b8bea00d2dd6299d3121ffd0..e72abda90aa1cac73e148ff7ba9dd7d57c89703b 100644 (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
index bfe0433a574df51e478c38f8600f0d4235b68aa6..bcfeb3a869311770b62bb306a7451472a13ad361 100644 (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
index 9fc27d8f6e61b6cd9ec9324d9bc75ef0fa353bcd..b714b529c8de91361be38ec97e2cb44b582718d2 100644 (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}', '{}')