]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- public method name is get_enums()
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Aug 2014 21:42:33 +0000 (17:42 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Aug 2014 21:42:33 +0000 (17:42 -0400)
- return a list of dicts like other methods do
- don't combine 'schema' with 'name', leave them separate
- support '*' argument so that we can retrieve cross-schema
if needed
- remove "conn" argument
- use bound parameters for 'schema' in SQL
- order by schema, name, label
- adapt _load_enums changes to column reflection
- changelog
- module docs for get_enums()
- add drop of enums to --dropfirst

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/testing/plugin/plugin_base.py
test/dialect/postgresql/test_reflection.py

index e4671ed9bac89e48adf519050051bace9759bde8..7762534e87f3d02d4dc1fa75c3e2d317a9005e67 100644 (file)
 .. changelog::
        :version: 1.0.0
 
+    .. change::
+        :tags: postgresql, feature
+        :pullreq: github:126
+
+        Added new method :meth:`.PGInspector.get_enums`, when using the
+        inspector for Postgresql will provide a list of ENUM types.
+        Pull request courtesy Ilya Pekelny.
+
     .. change::
         :tags: mysql, bug
 
index 3356e9d4b8d2f1d1581eaf2aca9c3c7a0dbf1fb6..c2b1d66f43b42e8e39dc8869147861d0b17e0400 100644 (file)
@@ -401,6 +401,23 @@ The value passed to the keyword argument will be simply passed through to the
 underlying CREATE INDEX command, so it *must* be a valid index type for your
 version of PostgreSQL.
 
+Special Reflection Options
+--------------------------
+
+The :class:`.Inspector` used for the Postgresql backend is an instance
+of :class:`.PGInspector`, which offers additional methods::
+
+    from sqlalchemy import create_engine, inspect
+
+    engine = create_engine("postgresql+psycopg2://localhost/test")
+    insp = inspect(engine)  # will be a PGInspector
+
+    print(insp.get_enums())
+
+.. autoclass:: PGInspector
+    :members:
+
+
 """
 from collections import defaultdict
 import re
@@ -1570,16 +1587,31 @@ class PGInspector(reflection.Inspector):
         reflection.Inspector.__init__(self, conn)
 
     def get_table_oid(self, table_name, schema=None):
-        """Return the oid from `table_name` and `schema`."""
+        """Return the OID for the given table name."""
 
         return self.dialect.get_table_oid(self.bind, table_name, schema,
                                           info_cache=self.info_cache)
-    def load_enums(self, conn, schema=None):
-        """Return a enums list.
 
-        A per-database and per-schema enums list."""
+    def get_enums(self, schema=None):
+        """Return a list of ENUM objects.
+
+        Each member is a dictionary containing these fields:
+
+            * name - name of the enum
+            * schema - the schema name for the enum.
+            * visible - boolean, whether or not this enum is visible
+              in the default search path.
+            * labels - a list of string labels that apply to the enum.
+
+        :param schema: schema name.  If None, the default schema
+         (typically 'public') is used.  May also be set to '*' to
+         indicate load enums for all schemas.
+
+        .. versionadded:: 1.0.0
+
+        """
         schema = schema or self.default_schema_name
-        return self.dialect._load_enums(conn, schema)
+        return self.dialect._load_enums(self.bind, schema)
 
 
 class CreateEnumType(schema._CreateDropBase):
@@ -2045,7 +2077,12 @@ class PGDialect(default.DefaultDialect):
         c = connection.execute(s, table_oid=table_oid)
         rows = c.fetchall()
         domains = self._load_domains(connection)
-        enums = self._load_enums(connection)
+        enums = dict(
+            (
+                "%s.%s" % (rec['schema'], rec['name'])
+                if not rec['visible'] else rec['name'], rec) for rec in
+            self._load_enums(connection, schema='*')
+        )
 
         # format columns
         columns = []
@@ -2119,10 +2156,9 @@ class PGDialect(default.DefaultDialect):
             elif attype in enums:
                 enum = enums[attype]
                 coltype = ENUM
-                if "." in attype:
-                    kwargs['schema'], kwargs['name'] = attype.split('.')
-                else:
-                    kwargs['name'] = attype
+                kwargs['name'] = enum['name']
+                if not enum['visible']:
+                    kwargs['schema'] = enum['schema']
                 args = tuple(enum['labels'])
                 break
             elif attype in domains:
@@ -2447,32 +2483,37 @@ class PGDialect(default.DefaultDialect):
                  LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
                  LEFT JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid
             WHERE t.typtype = 'e'
-            AND n.nspname = '%s'
-            ORDER BY "name", e.oid -- e.oid gives us label order
-        """ % schema
+        """
+
+        if schema != '*':
+            SQL_ENUMS += "AND n.nspname = :schema "
+
+        # e.oid gives us label order within an enum
+        SQL_ENUMS += 'ORDER BY "schema", "name", e.oid'
 
         s = sql.text(SQL_ENUMS, typemap={
             'attname': sqltypes.Unicode,
             'label': sqltypes.Unicode})
+
+        if schema != '*':
+            s = s.bindparams(schema=schema)
+
         c = connection.execute(s)
 
-        enums = {}
+        enums = []
+        enum_by_name = {}
         for enum in c.fetchall():
-            if enum['visible']:
-                # 'visible' just means whether or not the enum is in a
-                # schema that's on the search path -- or not overridden by
-                # a schema with higher precedence. If it's not visible,
-                # it will be prefixed with the schema-name when it's used.
-                name = enum['name']
-            else:
-                name = "%s.%s" % (enum['schema'], enum['name'])
-
-            if name in enums:
-                enums[name]['labels'].append(enum['label'])
+            key = (enum['schema'], enum['name'])
+            if key in enum_by_name:
+                enum_by_name[key]['labels'].append(enum['label'])
             else:
-                enums[name] = {
+                enum_by_name[key] = enum_rec = {
+                    'name': enum['name'],
+                    'schema': enum['schema'],
+                    'visible': enum['visible'],
                     'labels': [enum['label']],
                 }
+                enums.append(enum_rec)
 
         return enums
 
index 4c245e9e928060552c72ea139d72ec1994866940..9c63a2e1d03400c631e25cad40c61f7baf312b78 100644 (file)
@@ -315,7 +315,7 @@ def _setup_requirements(argument):
 @post
 def _prep_testing_database(options, file_config):
     from sqlalchemy.testing import config
-    from sqlalchemy import schema, inspect
+    from sqlalchemy import schema, inspect, testing
 
     if options.dropfirst:
         for cfg in config.Config.all_configs():
@@ -358,6 +358,14 @@ def _prep_testing_database(options, file_config):
                                      schema="test_schema")
                     ))
 
+            if testing.against("postgresql"):
+                from sqlalchemy.dialects import postgresql
+                for enum in inspector.get_enums("*"):
+                    e.execute(postgresql.DropEnumType(
+                        postgresql.ENUM(
+                            name=enum['name'],
+                            schema=enum['schema'])))
+
 
 @post
 def _set_table_options(options, file_config):
index 26de239026b449af032cc5ba74b48a4eddf6ecf4..bab41b0f752e9b92f20c5b57979093767887fd4a 100644 (file)
@@ -624,24 +624,69 @@ class ReflectionTest(fixtures.TestBase):
             eq_(fk, fk_ref[fk['name']])
 
     @testing.provide_metadata
-    def test_inspect_enums_custom_schema(self):
+    def test_inspect_enums_schema(self):
         conn = testing.db.connect()
-        enum_type = postgresql.ENUM('sad', 'ok', 'happy', name='mood',
-            metadata=self.metadata, schema='test_schema')
+        enum_type = postgresql.ENUM(
+            'sad', 'ok', 'happy', name='mood',
+            schema='test_schema',
+            metadata=self.metadata)
         enum_type.create(conn)
         inspector = reflection.Inspector.from_engine(conn.engine)
-        eq_(inspector.load_enums(conn, 'test_schema'), {
-            u'test_schema.mood': {'labels': [u'sad', u'ok', u'happy']}})
+        eq_(
+            inspector.get_enums('test_schema'), [{
+                'visible': False,
+                'name': 'mood',
+                'schema': 'test_schema',
+                'labels': ['sad', 'ok', 'happy']
+            }])
 
     @testing.provide_metadata
-    def test_inspect_enums_schema(self):
-        conn = testing.db.connect()
-        enum_type = postgresql.ENUM('cat', 'dog', 'rat', name='pet',
+    def test_inspect_enums(self):
+        enum_type = postgresql.ENUM(
+            'cat', 'dog', 'rat', name='pet', metadata=self.metadata)
+        enum_type.create(testing.db)
+        inspector = reflection.Inspector.from_engine(testing.db)
+        eq_(inspector.get_enums(), [
+            {
+                'visible': True,
+                'labels': ['cat', 'dog', 'rat'],
+                'name': 'pet',
+                'schema': 'public'
+            }])
+
+    @testing.provide_metadata
+    def test_inspect_enums_star(self):
+        enum_type = postgresql.ENUM(
+            'cat', 'dog', 'rat', name='pet', metadata=self.metadata)
+        schema_enum_type = postgresql.ENUM(
+            'sad', 'ok', 'happy', name='mood',
+            schema='test_schema',
             metadata=self.metadata)
-        enum_type.create(conn)
-        inspector = reflection.Inspector.from_engine(conn.engine)
-        eq_(inspector.load_enums(conn), {
-            u'pet': {'labels': [u'cat', u'dog', u'rat']}})
+        enum_type.create(testing.db)
+        schema_enum_type.create(testing.db)
+        inspector = reflection.Inspector.from_engine(testing.db)
+
+        eq_(inspector.get_enums(), [
+            {
+                'visible': True,
+                'labels': ['cat', 'dog', 'rat'],
+                'name': 'pet',
+                'schema': 'public'
+            }])
+
+        eq_(inspector.get_enums('*'), [
+            {
+                'visible': True,
+                'labels': ['cat', 'dog', 'rat'],
+                'name': 'pet',
+                'schema': 'public'
+            },
+            {
+                'visible': False,
+                'name': 'mood',
+                'schema': 'test_schema',
+                'labels': ['sad', 'ok', 'happy']
+            }])
 
 
 class CustomTypeReflectionTest(fixtures.TestBase):