]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Improve identity column reflection
authorFederico Caselli <cfederico87@gmail.com>
Mon, 3 Mar 2025 22:35:48 +0000 (23:35 +0100)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 7 Mar 2025 00:26:19 +0000 (19:26 -0500)
Add SQL typing to reflection query used to retrieve a the structure
of IDENTITY columns, adding explicit JSON typing to the query to suit
unusual PostgreSQL driver configurations that don't support JSON natively.

Fixed issue affecting PostgreSQL 17.3 and greater where reflection of
domains with "NOT NULL" as part of their definition would include an
invalid constraint entry in the data returned by
:meth:`_postgresql.PGInspector.get_domains` corresponding to an additional
"NOT NULL" constraint that isn't a CHECK constraint; the existing
``"nullable"`` entry in the dictionary already indicates if the domain
includes a "not null" constraint.   Note that such domains also cannot be
reflected on PostgreSQL 17.0 through 17.2 due to a bug on the PostgreSQL
side; if encountering errors in reflection of domains which include NOT
NULL, upgrade to PostgreSQL server 17.3 or greater.

Fixes: #11751
Change-Id: I8e69de51601dca3257186e38c6f699fbfd9014c6

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

diff --git a/doc/build/changelog/unreleased_20/11751.rst b/doc/build/changelog/unreleased_20/11751.rst
new file mode 100644 (file)
index 0000000..3686f4f
--- /dev/null
@@ -0,0 +1,21 @@
+.. change::
+    :tags: bug, postgresql
+    :tickets: 11751
+
+    Add SQL typing to reflection query used to retrieve a the structure
+    of IDENTITY columns, adding explicit JSON typing to the query to suit
+    unusual PostgreSQL driver configurations that don't support JSON natively.
+
+.. change::
+    :tags: bug, postgresql
+
+    Fixed issue affecting PostgreSQL 17.3 and greater where reflection of
+    domains with "NOT NULL" as part of their definition would include an
+    invalid constraint entry in the data returned by
+    :meth:`_postgresql.PGInspector.get_domains` corresponding to an additional
+    "NOT NULL" constraint that isn't a CHECK constraint; the existing
+    ``"nullable"`` entry in the dictionary already indicates if the domain
+    includes a "not null" constraint.   Note that such domains also cannot be
+    reflected on PostgreSQL 17.0 through 17.2 due to a bug on the PostgreSQL
+    side; if encountering errors in reflection of domains which include NOT
+    NULL, upgrade to PostgreSQL server 17.3 or greater.
index 83bd99d7f0a0ad04fa786e16a848738cb072855a..133052ff0b182b6ed12af18358e83f34ac547699 100644 (file)
@@ -3601,6 +3601,7 @@ class PGDialect(default.DefaultDialect):
                         pg_catalog.pg_sequence.c.seqcache,
                         "cycle",
                         pg_catalog.pg_sequence.c.seqcycle,
+                        type_=sqltypes.JSON(),
                     )
                 )
                 .select_from(pg_catalog.pg_sequence)
@@ -5010,11 +5011,12 @@ class PGDialect(default.DefaultDialect):
                     key=lambda t: t[0],
                 )
                 for name, def_ in sorted_constraints:
-                    # constraint is in the form "CHECK (expression)".
+                    # constraint is in the form "CHECK (expression)"
+                    # or "NOT NULL". Ignore the "NOT NULL" and
                     # remove "CHECK (" and the tailing ")".
-                    check = def_[7:-1]
-                    constraints.append({"name": name, "check": check})
-
+                    if def_.casefold().startswith("check"):
+                        check = def_[7:-1]
+                        constraints.append({"name": name, "check": check})
             domain_rec: ReflectedDomain = {
                 "name": domain["name"],
                 "schema": domain["schema"],
index 510c8aa33c517f6ad2f65e76a8f7c757da048e2b..4d889c6775fd7329b57b78d54c2b03b36fd1895c 100644 (file)
@@ -432,7 +432,7 @@ class DomainReflectionTest(fixtures.TestBase, AssertsExecutionResults):
     @testing.fixture
     def testtable(self, connection, testdomain):
         connection.exec_driver_sql(
-            "CREATE TABLE testtable (question integer, answer " "testdomain)"
+            "CREATE TABLE testtable (question integer, answer testdomain)"
         )
         yield
         connection.exec_driver_sql("DROP TABLE testtable")