]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
PGDialect.get_check_constraints: Handle "NOT VALID"
authorBill Finn <bill@angaza.com>
Tue, 27 Aug 2019 16:21:57 +0000 (12:21 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 27 Aug 2019 16:56:11 +0000 (12:56 -0400)
Added support for reflection of CHECK constraints that include the special
PostgreSQL qualifier "NOT VALID", which can be present for CHECK
constraints that were added to an exsiting table with the directive that
they not be applied to existing data in the table. The PostgreSQL
dictionary for CHECK constraints as returned by
:meth:`.Inspector.get_check_constraints` may include an additional entry
``dialect_options`` which within will contain an entry ``"not_valid":
True`` if this symbol is detected.   Pull request courtesy Bill Finn.

Fixes: #4824
Closes: #4825
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4825
Pull-request-sha: a0e1ab133c2d46521a74e55423ac2ba866682dae

Change-Id: I78365f50055c95474c92124b85df66c5c80c00c8
(cherry picked from commit 3980a9a455d08c5073cabc3c2b77de46fa36d7b4)

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

diff --git a/doc/build/changelog/unreleased_13/4824.rst b/doc/build/changelog/unreleased_13/4824.rst
new file mode 100644 (file)
index 0000000..0a624b3
--- /dev/null
@@ -0,0 +1,12 @@
+.. change::
+    :tags: usecase, postgresql
+    :tickets: 4824
+
+    Added support for reflection of CHECK constraints that include the special
+    PostgreSQL qualifier "NOT VALID", which can be present for CHECK
+    constraints that were added to an exsiting table with the directive that
+    they not be applied to existing data in the table. The PostgreSQL
+    dictionary for CHECK constraints as returned by
+    :meth:`.Inspector.get_check_constraints` may include an additional entry
+    ``dialect_options`` which within will contain an entry ``"not_valid":
+    True`` if this symbol is detected.   Pull request courtesy Bill Finn.
index 6611b3e5bd25b37be6a08a40eeda1dbd12011457..32db3a679902041f6d1563a33f290c5061fabc93 100644 (file)
@@ -3439,20 +3439,24 @@ class PGDialect(default.DefaultDialect):
 
         c = connection.execute(sql.text(CHECK_SQL), table_oid=table_oid)
 
-        # samples:
-        # "CHECK (((a > 1) AND (a < 5)))"
-        # "CHECK (((a = 1) OR ((a > 2) AND (a < 5))))"
-        def match_cons(src):
-            m = re.match(r"^CHECK *\(\((.+)\)\)$", src)
+        ret = []
+        for name, src in c:
+            # samples:
+            # "CHECK (((a > 1) AND (a < 5)))"
+            # "CHECK (((a = 1) OR ((a > 2) AND (a < 5))))"
+            # "CHECK (((a > 1) AND (a < 5))) NOT VALID"
+            m = re.match(r"^CHECK *\(\((.+)\)\)( NOT VALID)?$", src)
             if not m:
                 util.warn("Could not parse CHECK constraint text: %r" % src)
-                return ""
-            return m.group(1)
+                sqltext = ""
+            else:
+                sqltext = m.group(1)
+            entry = {"name": name, "sqltext": sqltext}
+            if m and m.group(2):
+                entry["dialect_options"] = {"not_valid": True}
 
-        return [
-            {"name": name, "sqltext": match_cons(src)}
-            for name, src in c.fetchall()
-        ]
+            ret.append(entry)
+        return ret
 
     def _load_enums(self, connection, schema=None):
         schema = schema or self.default_schema_name
index dcd1d03135377867006f459096c364728afda607..054be0f1fafb28ce978c2aff0e0cafe32010e16d 100644 (file)
@@ -552,6 +552,12 @@ class Inspector(object):
         sqltext
           the check constraint's SQL expression
 
+        dialect_options
+          may or may not be present; a dictionary with additional
+          dialect-specific options for this CHECK constraint
+
+          .. versionadded:: 1.3.8
+
         :param table_name: string name of the table.  For special quoting,
          use :class:`.quoted_name`.
 
index 456b9e1734753505ae51289336959c9e8a3a804d..eff3225a2a34ee814e4f412e1da354989524dd99 100644 (file)
@@ -1546,9 +1546,10 @@ class ReflectionTest(fixtures.TestBase):
         )
 
     def test_reflect_check_warning(self):
+        rows = [("some name", "NOTCHECK foobar")]
         conn = mock.Mock(
-            execute=lambda *arg, **kw: mock.Mock(
-                fetchall=lambda: [("some name", "NOTCHECK foobar")]
+            execute=lambda *arg, **kw: mock.MagicMock(
+                fetchall=lambda: rows, __iter__=lambda self: iter(rows)
             )
         )
         with mock.patch.object(
@@ -1559,6 +1560,30 @@ class ReflectionTest(fixtures.TestBase):
             ):
                 testing.db.dialect.get_check_constraints(conn, "foo")
 
+    def test_reflect_with_not_valid_check_constraint(self):
+        rows = [("some name", "CHECK ((a IS NOT NULL)) NOT VALID")]
+        conn = mock.Mock(
+            execute=lambda *arg, **kw: mock.MagicMock(
+                fetchall=lambda: rows, __iter__=lambda self: iter(rows)
+            )
+        )
+        with mock.patch.object(
+            testing.db.dialect, "get_table_oid", lambda *arg, **kw: 1
+        ):
+            check_constraints = testing.db.dialect.get_check_constraints(
+                conn, "foo"
+            )
+            eq_(
+                check_constraints,
+                [
+                    {
+                        "name": "some name",
+                        "sqltext": "a IS NOT NULL",
+                        "dialect_options": {"not_valid": True},
+                    }
+                ],
+            )
+
 
 class CustomTypeReflectionTest(fixtures.TestBase):
     class CustomType(object):