]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- repair get_foreign_table_names() for PGInsp/dialect level
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 17 Sep 2014 17:11:22 +0000 (13:11 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 17 Sep 2014 17:11:22 +0000 (13:11 -0400)
- repair get_view_names()
- changelog + migration note

doc/build/changelog/changelog_10.rst
doc/build/changelog/migration_10.rst
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/engine/reflection.py
setup.cfg
test/dialect/postgresql/test_reflection.py

index 9c7f207cc71068d2560d1a99b0c2d4d636126d2c..b00dbb0a7762f2f42d3d2b8f91bb9691a401fffa 100644 (file)
     series as well.  For changes that are specific to 1.0 with an emphasis
     on compatibility concerns, see :doc:`/changelog/migration_10`.
 
+    .. change::
+        :tags: feature, postgresql
+        :tickets: 2891
+        :pullreq: github:128
+
+        Support has been added for reflection of materialized views
+        and foreign tables, as well as support for materialized views
+        within :meth:`.Inspector.get_view_names`, and a new method
+        :meth:`.PGInspector.get_foreign_table_names` available on the
+        Postgresql version of :class:`.Inspector`.  Pull request courtesy
+        Rodrigo Menezes.
+
+        .. seealso::
+
+            :ref:`feature_2891`
+
+
     .. change::
         :tags: feature, orm
 
index 6a48b31fa284d441aa1f61aa73270435740b1c17..d967afa35bf5bcfb60adffba455699b70765780d 100644 (file)
@@ -865,6 +865,34 @@ method that returns information on all available ``ENUM`` types::
 
        :meth:`.PGInspector.get_enums`
 
+.. _feature_2891:
+
+Postgresql Dialect reflects Materialized Views, Foreign Tables
+--------------------------------------------------------------
+
+Changes are as follows:
+
+* the :class:`Table` construct with ``autoload=True`` will now match a name
+  that exists in the database as a materialized view or foriegn table.
+
+* :meth:`.Inspector.get_view_names` will return plain and materialized view
+  names.
+
+* :meth:`.Inspector.get_table_names` does **not** change for Postgresql, it
+  continues to return only the names of plain tables.
+
+* A new method :meth:`.PGInspector.get_foreign_table_names` is added which
+  will return the names of tables that are specifically marked as "foreign"
+  in the Postgresql schema tables.
+
+The change to reflection involves adding ``'m'`` and ``'f'`` to the list
+of qualifiers we use when querying ``pg_class.relkind``, but this change
+is new in 1.0.0 to avoid any backwards-incompatible surprises for those
+running 0.9 in production.
+
+:ticket:`2891`
+
+
 MySQL internal "no such table" exceptions not passed to event handlers
 ----------------------------------------------------------------------
 
index df9797658efb30c2c504e454d602adf7859d0c53..b9a0d461bcee2da756f8524463f328a81c6e7119 100644 (file)
@@ -401,6 +401,7 @@ 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
 --------------------------
 
@@ -1679,22 +1680,18 @@ class PGInspector(reflection.Inspector):
         schema = schema or self.default_schema_name
         return self.dialect._load_enums(self.bind, schema)
 
-    def get_foreign_table_names(self, connection, schema=None, **kw):
-        if schema is not None:
-            current_schema = schema
-        else:
-            current_schema = self.default_schema_name
+    def get_foreign_table_names(self, schema=None):
+        """Return a list of FOREIGN TABLE names.
 
-        result = connection.execute(
-            sql.text("SELECT relname FROM pg_class c "
-                     "WHERE relkind = 'f' "
-                     "AND '%s' = (select nspname from pg_namespace n "
-                     "where n.oid = c.relnamespace) " %
-                     current_schema,
-                     typemap={'relname': sqltypes.Unicode}
-                     )
-        )
-        return [row[0] for row in result]
+        Behavior is similar to that of :meth:`.Inspector.get_table_names`,
+        except that the list is limited to those tables tha report a
+        ``relkind`` value of ``f``.
+
+        .. versionadded:: 1.0.0
+
+        """
+        schema = schema or self.default_schema_name
+        return self.dialect._get_foreign_table_names(self.bind, schema)
 
 
 class CreateEnumType(schema._CreateDropBase):
@@ -2094,6 +2091,24 @@ class PGDialect(default.DefaultDialect):
         )
         return [row[0] for row in result]
 
+    @reflection.cache
+    def _get_foreign_table_names(self, connection, schema=None, **kw):
+        if schema is not None:
+            current_schema = schema
+        else:
+            current_schema = self.default_schema_name
+
+        result = connection.execute(
+            sql.text("SELECT relname FROM pg_class c "
+                     "WHERE relkind = 'f' "
+                     "AND '%s' = (select nspname from pg_namespace n "
+                     "where n.oid = c.relnamespace) " %
+                     current_schema,
+                     typemap={'relname': sqltypes.Unicode}
+                     )
+        )
+        return [row[0] for row in result]
+
     @reflection.cache
     def get_view_names(self, connection, schema=None, **kw):
         if schema is not None:
@@ -2103,7 +2118,7 @@ class PGDialect(default.DefaultDialect):
         s = """
         SELECT relname
         FROM pg_class c
-        WHERE relkind IN ('m', v')
+        WHERE relkind IN ('m', 'v')
           AND '%(schema)s' = (select nspname from pg_namespace n
           where n.oid = c.relnamespace)
         """ % dict(schema=current_schema)
index b722905883752d96da368176f4708c2a4e5abfca..cf1f2d3dd1302937353bc988a935b79cd58a240e 100644 (file)
@@ -227,9 +227,6 @@ class Inspector(object):
         :param schema: Optional, retrieve names from a non-default schema.
          For special quoting, use :class:`.quoted_name`.
 
-        .. versionchanged:: 1.0.0 now returns materialized views as well
-        as normal views.
-
         """
 
         return self.dialect.get_view_names(self.bind, schema,
index 51a4e30bf259d1b064ec5baba8bcb038c6c3db38..b70086605dabdc756dc4ab705a4e22c1d4cf8ac9 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -31,7 +31,7 @@ oracle_db_link = test_link
 # CREATE EXTENSION postgres_fdw;
 # GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO public;
 # this can be localhost to create a loopback foreign table
-postgres_test_db_link = localhost
+postgres_test_db_link = localhost
 
 
 [db]
index 4bc65869424a75beeb962ba9acc8d42cbc7282b0..b8b9be3dec912009ee6db0d33587c8b128428638 100644 (file)
@@ -59,9 +59,15 @@ class ForeignTableReflectionTest(fixtures.TablesTest, AssertsExecutionResults):
     def test_get_foreign_table_names(self):
         inspector = inspect(testing.db)
         with testing.db.connect() as conn:
-            ft_names = inspector.get_foreign_table_names(conn)
+            ft_names = inspector.get_foreign_table_names()
             eq_(ft_names, ['test_foreigntable'])
 
+    def test_get_table_names_no_foreign(self):
+        inspector = inspect(testing.db)
+        with testing.db.connect() as conn:
+            names = inspector.get_table_names()
+            eq_(names, ['testtable'])
+
 
 class MaterialiedViewReflectionTest(
         fixtures.TablesTest, AssertsExecutionResults):
@@ -85,15 +91,24 @@ class MaterialiedViewReflectionTest(
                 {"id": 89, "data": 'd1'}
             )
 
-        view = sa.DDL(
+        materialized_view = sa.DDL(
             "CREATE MATERIALIZED VIEW test_mview AS "
             "SELECT * FROM testtable")
 
-        sa.event.listen(testtable, 'after_create', view)
+        plain_view = sa.DDL(
+            "CREATE VIEW test_regview AS "
+            "SELECT * FROM testtable")
+
+        sa.event.listen(testtable, 'after_create', plain_view)
+        sa.event.listen(testtable, 'after_create', materialized_view)
         sa.event.listen(
             testtable, 'before_drop',
             sa.DDL("DROP MATERIALIZED VIEW test_mview")
         )
+        sa.event.listen(
+            testtable, 'before_drop',
+            sa.DDL("DROP VIEW test_regview")
+        )
 
     def test_mview_is_reflected(self):
         metadata = MetaData(testing.db)
@@ -109,6 +124,10 @@ class MaterialiedViewReflectionTest(
             [(89, 'd1',)]
         )
 
+    def test_get_view_names(self):
+        insp = inspect(testing.db)
+        eq_(set(insp.get_view_names()), set(['test_mview', 'test_regview']))
+
 
 class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
     """Test PostgreSQL domains"""