Permalink
Browse files

Change user mappings to be listed under their servers in YAML output.

 * docs/foreign.rst: Add new automethods.
 * pyrseas/database.py (Database.link_refs): Link user mappings to
   their servers.  (Database.from_map): Remove mapping of input user
   mappings.  (Database.to_map): Remove mapping of user mappings.
 * pyrseas/dbobject/foreign.py (ForeignDataWrapperDict.from_map): Add
   newdb argument to downstream call.  (ForeignServer.to_map): New
   method to output user mappings as part of their servers.
   (ForeignServer.from_map): Handle user mappings as this level.
   (ForeignServerDict.link_refs): New method to link mappings and
   servers.  (ForeignServer.extern_key): Change output format.
   (Foreign.UserMappingDict.query): Add join to
   pg_foreign_data_wrapper and change sort order.
   (UserMappingDict.from_map): Change arguments and reorganize for new
   input.  (UserMappingDict.diff_map): Change key tuples.
 * tests/dbobject/test_foreign.py: Adjust tests to new input and
   output.
  • Loading branch information...
1 parent 0a2bdb4 commit 0b3df87104742ae915fce9e9c39a66cece8eed27 @jmafc jmafc committed Mar 9, 2012
Showing with 71 additions and 48 deletions.
  1. +4 −0 docs/foreign.rst
  2. +1 −2 pyrseas/database.py
  3. +52 −28 pyrseas/dbobject/foreign.py
  4. +14 −18 tests/dbobject/test_foreign.py
View
@@ -58,6 +58,8 @@ foreign server
.. automethod:: ForeignServer.identifier
+.. automethod:: ForeignServer.to_map
+
.. automethod:: ForeignServer.create
Foreign Server Dictionary
@@ -73,6 +75,8 @@ that represents the collection of foreign servers in a database.
.. automethod:: ForeignServerDict.to_map
+.. automethod:: ForeignServerDict.link_refs
+
.. automethod:: ForeignServerDict.diff_map
User Mapping
View
@@ -95,6 +95,7 @@ def _link_refs(self, db):
db.tables.link_refs(db.columns, db.constraints, db.indexes,
db.rules, db.triggers)
db.fdwrappers.link_refs(db.servers)
+ db.servers.link_refs(db.usermaps)
db.ftables.link_refs(db.columns)
db.types.link_refs(db.columns, db.constraints, db.functions)
@@ -165,7 +166,6 @@ def from_map(self, input_map):
self.ndb.schemas.from_map(input_schemas, self.ndb)
self.ndb.casts.from_map(input_casts, self.ndb)
self.ndb.fdwrappers.from_map(input_fdws, self.ndb)
- self.ndb.usermaps.from_map(input_ums, self.ndb)
self._link_refs(self.ndb)
def to_map(self):
@@ -178,7 +178,6 @@ def to_map(self):
dbmap = self.db.languages.to_map()
dbmap.update(self.db.casts.to_map())
dbmap.update(self.db.fdwrappers.to_map())
- dbmap.update(self.db.usermaps.to_map())
dbmap.update(self.db.schemas.to_map())
return dbmap
@@ -120,7 +120,7 @@ def from_map(self, inwrappers, newdb):
setattr(wrapper, key, inwrapper[key])
else:
raise KeyError("Expected typed object, found '%s'" % key)
- newdb.servers.from_map(wrapper, inservs)
+ newdb.servers.from_map(wrapper, inservs, newdb)
def link_refs(self, dbservers):
"""Connect servers to their respective foreign data wrappers
@@ -200,6 +200,21 @@ def identifier(self):
"""
return quote_id(self.name)
+ def to_map(self):
+ """Convert servers and subsidiary objects to a YAML-suitable format
+
+ :return: dictionary
+ """
+ key = self.extern_key()
+ server = {key: self._base_map()}
+ if hasattr(self, 'usermaps'):
+ umaps = {}
+ for umap in self.usermaps.keys():
+ umaps.update(self.usermaps[umap].to_map())
+ server[key]['user mappings'] = umaps
+ del server[key]['usermaps']
+ return server
+
def create(self):
"""Return SQL statements to CREATE the server
@@ -234,11 +249,12 @@ class ForeignServerDict(DbObjectDict):
JOIN pg_foreign_data_wrapper w ON (srvfdw = w.oid)
ORDER BY fdwname, srvname"""
- def from_map(self, wrapper, inservers):
+ def from_map(self, wrapper, inservers, newdb):
"""Initialize the dictionary of servers by examining the input map
:param wrapper: associated foreign data wrapper
:param inservers: input YAML map defining the foreign servers
+ :param newdb: collection of dictionaries defining the database
"""
for key in inservers.keys():
if not key.startswith('server '):
@@ -250,6 +266,8 @@ def from_map(self, wrapper, inservers):
if inserv:
for attr, val in inserv.items():
setattr(serv, attr, val)
+ if 'user mappings' in inserv:
+ newdb.usermaps.from_map(serv, inserv['user mappings'])
if 'oldname' in inserv:
serv.oldname = inserv['oldname']
del inserv['oldname']
@@ -269,6 +287,19 @@ def to_map(self):
servers.update(self[srv].to_map())
return servers
+ def link_refs(self, dbusermaps):
+ """Connect user mappings to their respective servers
+
+ :param dbusermaps: dictionary of user mappings
+ """
+ for (fdw, srv, usr) in dbusermaps.keys():
+ dbusermap = dbusermaps[(fdw, srv, usr)]
+ assert self[(fdw, srv)]
+ server = self[(fdw, srv)]
+ if not hasattr(server, 'usermaps'):
+ server.usermaps = {}
+ server.usermaps.update({usr: dbusermap})
+
def diff_map(self, inservers):
"""Generate SQL to transform existing foreign servers
@@ -313,15 +344,14 @@ class UserMapping(DbObject):
objtype = "USER MAPPING"
- keylist = ['username', 'server']
+ keylist = ['wrapper', 'server', 'username']
def extern_key(self):
"""Return the key to be used in external maps for this user mapping
:return: string
"""
- return '%s for %s server %s' % (self.objtype.lower(), self.username,
- self.server)
+ return self.username
def identifier(self):
"""Return a full identifier for a user mapping object
@@ -352,38 +382,32 @@ class UserMappingDict(DbObjectDict):
cls = UserMapping
query = \
- """SELECT CASE umuser WHEN 0 THEN 'PUBLIC' ELSE
- pg_get_userbyid(umuser) END AS username, srvname AS server,
+ """SELECT fdwname AS wrapper, srvname AS server,
+ CASE umuser WHEN 0 THEN 'PUBLIC' ELSE
+ pg_get_userbyid(umuser) END AS username,
umoptions AS options
FROM pg_user_mapping u
JOIN pg_foreign_server s ON (umserver = s.oid)
- ORDER BY umuser, srvname"""
+ JOIN pg_foreign_data_wrapper w ON (srvfdw = w.oid)
+ ORDER BY fdwname, srvname, umuser"""
- def from_map(self, inusermaps, newdb):
+ def from_map(self, server, inusermaps):
"""Initialize the dictionary of mappings by examining the input map
+ :param server: foreign server associated with mappings
:param inusermaps: input YAML map defining the user mappings
- :param newdb: collection of dictionaries defining the database
"""
for key in inusermaps.keys():
- if not key.startswith('user mapping for '):
- raise KeyError("Unrecognized object type: %s" % key)
- ump = key[17:]
- if ' server ' not in ump:
- raise KeyError("Unrecognized object type: %s" % key)
- pos = ump.find(' server ')
- usr = ump[:pos]
- if usr.lower() == 'public':
- usr = 'PUBLIC'
- srv = ump[pos + 8:]
- self[(usr, srv)] = usermap = UserMapping(username=usr, server=srv)
+ usermap = UserMapping(wrapper=server.wrapper, server=server.name,
+ username=key)
inusermap = inusermaps[key]
if inusermap:
for attr, val in inusermap.items():
setattr(usermap, attr, val)
if 'oldname' in inusermap:
usermap.oldname = inusermap['oldname']
del inusermap['oldname']
+ self[(server.wrapper, server.name, key)] = usermap
def to_map(self):
"""Convert the user mapping dictionary to a regular dictionary
@@ -410,11 +434,11 @@ def diff_map(self, inusermaps):
"""
stmts = []
# check input user mappings
- for (usr, srv) in inusermaps.keys():
- inump = inusermaps[(usr, srv)]
+ for (fdw, srv, usr) in inusermaps.keys():
+ inump = inusermaps[(fdw, srv, usr)]
# does it exist in the database?
- if (usr, srv) in self:
- stmts.append(self[(usr, srv)].diff_map(inump))
+ if (fdw, srv, usr) in self:
+ stmts.append(self[(fdw, srv, usr)].diff_map(inump))
else:
# check for possible RENAME
if hasattr(inump, 'oldname'):
@@ -430,10 +454,10 @@ def diff_map(self, inusermaps):
# create new user mapping
stmts.append(inump.create())
# check database user mappings
- for (usr, srv) in self.keys():
+ for (fdw, srv, usr) in self.keys():
# if missing, drop it
- if (usr, srv) not in inusermaps:
- stmts.append(self[(usr, srv)].drop())
+ if (fdw, srv, usr) not in inusermaps:
+ stmts.append(self[(fdw, srv, usr)].drop())
return stmts
@@ -215,17 +215,18 @@ def test_map_user_mapping(self):
self.db.execute(CREATE_FDW_STMT)
self.db.execute(CREATE_FS_STMT)
dbmap = self.db.execute_and_map(CREATE_UM_STMT)
- self.assertEqual(dbmap['user mapping for PUBLIC server fs1'], {})
+ self.assertEqual(dbmap['foreign data wrapper fdw1']['server fs1']
+ ['user mappings'], {'PUBLIC': {}})
def test_map_user_mapping_options(self):
"Map a user mapping with options"
self.db.execute(CREATE_FDW_STMT)
self.db.execute(CREATE_FS_STMT)
- dbmap = self.db.execute_and_map(
- "CREATE USER MAPPING FOR PUBLIC SERVER fs1 "
- "OPTIONS (user 'john', password 'doe')")
- self.assertEqual(dbmap['user mapping for PUBLIC server fs1'], {
- 'options': ['user=john', 'password=doe']})
+ dbmap = self.db.execute_and_map(CREATE_UM_STMT +
+ " OPTIONS (user 'john', password 'doe')")
+ self.assertEqual(dbmap['foreign data wrapper fdw1']['server fs1'],
+ {'user mappings': {'PUBLIC': {
+ 'options': ['user=john', 'password=doe']}}})
class UserMappingToSqlTestCase(PyrseasTestCase):
@@ -236,7 +237,8 @@ def test_create_user_mapping(self):
self.db.execute(CREATE_FDW_STMT)
self.db.execute_commit(CREATE_FS_STMT)
inmap = self.std_map()
- inmap.update({'user mapping for PUBLIC server fs1': {}})
+ inmap.update({'foreign data wrapper fdw1': {'server fs1': {
+ 'user mappings': {'PUBLIC': {}}}}})
dbsql = self.db.process_map(inmap)
self.assertEqual(fix_indent(dbsql[0]), CREATE_UM_STMT)
@@ -245,18 +247,12 @@ def test_create_user_mapping_options(self):
self.db.execute(CREATE_FDW_STMT)
self.db.execute_commit(CREATE_FS_STMT)
inmap = self.std_map()
- inmap.update({'user mapping for PUBLIC server fs1': {
- 'options': ['user=john', 'password=doe']}})
+ inmap.update({'foreign data wrapper fdw1': {'server fs1': {
+ 'user mappings': {'PUBLIC': {
+ 'options': ['user=john', 'password=doe']}}}}})
dbsql = self.db.process_map(inmap)
- self.assertEqual(fix_indent(dbsql[0]),
- "CREATE USER MAPPING FOR PUBLIC SERVER fs1 "
- "OPTIONS (user 'john', password 'doe')")
-
- def test_bad_map_user_mapping(self):
- "Error creating a user mapping with a bad map"
- inmap = self.std_map()
- inmap.update({'PUBLIC server fs1': {}})
- self.assertRaises(KeyError, self.db.process_map, inmap)
+ self.assertEqual(fix_indent(dbsql[0]), CREATE_UM_STMT +
+ " OPTIONS (user 'john', password 'doe')")
def test_drop_user_mapping(self):
"Drop an existing user mapping"

0 comments on commit 0b3df87

Please sign in to comment.