]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add reflection support for Postgresql partitioned tables
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 1 Oct 2018 20:23:33 +0000 (16:23 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 1 Oct 2018 20:24:55 +0000 (16:24 -0400)
Added rudimental support for reflection of Postgresql
partitioned tables, e.g. that relkind='p' is added to reflection
queries that return table information.

Fixes: #4237
Change-Id: I66fd10b002e4ed21ea13b13a7e35a85f66bdea75

doc/build/changelog/migration_13.rst
doc/build/changelog/unreleased_13/4237.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/postgresql/base.py
test/dialect/postgresql/test_reflection.py

index 500062686921bfe818089bb21bc896bb4f376942..b7e76b9557a056b018c19435017e9d6d6253b4db 100644 (file)
@@ -409,6 +409,42 @@ Key Behavioral Changes - Core
 Dialect Improvements and Changes - PostgreSQL
 =============================================
 
+.. _change_4237:
+
+Added basic reflection support for Postgresql paritioned tables
+---------------------------------------------------------------
+
+SQLAlchemy can render the "PARTITION BY" sequnce within a Postgresql
+CREATE TABLE statement using the flag ``postgresql_partition_by``, added in
+version 1.2.6.    However, the ``'p'`` type was not part of the reflection
+queries used until now.
+
+Given a schema such as::
+
+    dv = Table(
+        'data_values', metadata,
+        Column('modulus', Integer, nullable=False),
+        Column('data', String(30)),
+        postgresql_partition_by='range(modulus)')
+
+    sa.event.listen(
+        dv,
+        "after_create",
+        sa.DDL(
+            "CREATE TABLE data_values_4_10 PARTITION OF data_values "
+            "FOR VALUES FROM (4) TO (10)")
+    )
+
+The two table names ``'data_values'`` and ``'data_values_4_10'`` will come
+back from :meth:`.Inspector.get_table_names` and additionally the columns
+will come back from ``Inspector.get_columns('data_values')`` as well
+as ``Inspector.get_columns('data_values_4_10')``.   This also extends to the
+use of ``Table(..., autoload=True)`` with these tables.
+
+
+:ticket:`4237`
+
+
 Dialect Improvements and Changes - MySQL
 =============================================
 
diff --git a/doc/build/changelog/unreleased_13/4237.rst b/doc/build/changelog/unreleased_13/4237.rst
new file mode 100644 (file)
index 0000000..a023e59
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+   :tags: feature, postgresql
+   :tickets: 4237
+
+   Added rudimental support for reflection of Postgresql
+   partitioned tables, e.g. that relkind='p' is added to reflection
+   queries that return table information.
+
+   .. seealso::
+
+        :ref:`change_4237`
index ea2c5187092da6b3ad4c9e7a0641cc1624440229..11fcc41d5988a11ef8fef8ac2e6b8c7b76a1f722 100644 (file)
@@ -2460,7 +2460,8 @@ class PGDialect(default.DefaultDialect):
             FROM pg_catalog.pg_class c
             LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
             WHERE (%s)
-            AND c.relname = :table_name AND c.relkind in ('r', 'v', 'm', 'f')
+            AND c.relname = :table_name AND c.relkind in
+            ('r', 'v', 'm', 'f', 'p')
         """ % schema_where_clause
         # Since we're binding to unicode, table_name and schema_name must be
         # unicode.
@@ -2491,7 +2492,7 @@ class PGDialect(default.DefaultDialect):
         result = connection.execute(
             sql.text("SELECT c.relname FROM pg_class c "
                      "JOIN pg_namespace n ON n.oid = c.relnamespace "
-                     "WHERE n.nspname = :schema AND c.relkind = 'r'"
+                     "WHERE n.nspname = :schema AND c.relkind in ('r', 'p')"
                      ).columns(relname=sqltypes.Unicode),
             schema=schema if schema is not None else self.default_schema_name)
         return [name for name, in result]
index 70bc26e0b61f17db3cf784ad85f6ae406f84d746..b30cb506bc6d6b036c11e1a1b6a34cfab373b7a0 100644 (file)
@@ -74,6 +74,51 @@ class ForeignTableReflectionTest(fixtures.TablesTest, AssertsExecutionResults):
             eq_(names, ['testtable'])
 
 
+class PartitionedReflectionTest(
+        fixtures.TablesTest, AssertsExecutionResults):
+    # partitioned table reflection, issue #4237
+
+    __only_on__ = 'postgresql >= 10'
+    __backend__ = True
+
+    @classmethod
+    def define_tables(cls, metadata):
+        # the actual function isn't reflected yet
+        dv = Table(
+            'data_values', metadata,
+            Column('modulus', Integer, nullable=False),
+            Column('data', String(30)),
+            postgresql_partition_by='range(modulus)')
+
+        # looks like this is reflected prior to #4237
+        sa.event.listen(
+            dv,
+            "after_create",
+            sa.DDL(
+                "CREATE TABLE data_values_4_10 PARTITION OF data_values "
+                "FOR VALUES FROM (4) TO (10)")
+        )
+
+    def test_get_tablenames(self):
+        assert {'data_values', 'data_values_4_10'}.issubset(
+            inspect(testing.db).get_table_names()
+        )
+
+    def test_reflect_cols(self):
+        cols = inspect(testing.db).get_columns('data_values')
+        eq_(
+            [c['name'] for c in cols],
+            ['modulus', 'data']
+        )
+
+    def test_reflect_cols_from_partition(self):
+        cols = inspect(testing.db).get_columns('data_values_4_10')
+        eq_(
+            [c['name'] for c in cols],
+            ['modulus', 'data']
+        )
+
+
 class MaterializedViewReflectionTest(
         fixtures.TablesTest, AssertsExecutionResults):
     """Test reflection on materialized views"""