]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Improve regex parsing of CHECK constraints for PostgreSQL.
authorGord Thompson <gord@gordthompson.com>
Thu, 19 Dec 2019 17:20:39 +0000 (12:20 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 18 Jan 2020 14:32:35 +0000 (09:32 -0500)
Fixed issue where the PostgreSQL dialect would fail to parse a reflected
CHECK constraint that was a boolean-valued function (as opposed to a
boolean-valued expression).

Fixes: #5039
Closes: #5044
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5044
Pull-request-sha: b6903c656422abf658c4cc88b8cd03291d3a50f8

Change-Id: I7d39b104a8ce346cb593d541c1b4e5eab88867f9
(cherry picked from commit d8ac1e9e6bfc931d2f14f9846d6924106f56b7e6)

doc/build/changelog/unreleased_13/5039.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_13/5039.rst b/doc/build/changelog/unreleased_13/5039.rst
new file mode 100644 (file)
index 0000000..2bf3717
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, postgresql
+    :tickets: 5039
+
+    Fixed issue where the PostgreSQL dialect would fail to parse a reflected
+    CHECK constraint that was a boolean-valued function (as opposed to a
+    boolean-valued expression).
index 01a757a5f14ffd3600f0bc96de8decf80cacbb94..1252e7e692c27047c25ebda2789ddc239b0f0c6a 100644 (file)
@@ -3483,12 +3483,13 @@ class PGDialect(default.DefaultDialect):
             # "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)
+            # "CHECK (some_boolean_function(a))"
+            m = re.match(r"^CHECK *\((.+)\)( NOT VALID)?$", src)
             if not m:
                 util.warn("Could not parse CHECK constraint text: %r" % src)
                 sqltext = ""
             else:
-                sqltext = m.group(1)
+                sqltext = re.sub(r'^\((.+)\)$', r'\1', m.group(1))
             entry = {"name": name, "sqltext": sqltext}
             if m and m.group(2):
                 entry["dialect_options"] = {"not_valid": True}
index 28761514147683f3426580d9e619126c433e19f0..9452e8de81dfa7928f7febb437765ed2a4b0973d 100644 (file)
@@ -1530,17 +1530,34 @@ class ReflectionTest(fixtures.TestBase):
     def test_reflect_check_constraint(self):
         meta = self.metadata
 
-        cc_table = Table(
+        udf_create = """\
+            CREATE OR REPLACE FUNCTION is_positive(
+                x integer DEFAULT '-1'::integer)
+                RETURNS boolean
+                LANGUAGE 'plpgsql'
+                COST 100
+                VOLATILE
+            AS $BODY$BEGIN
+                RETURN x > 0;
+            END;$BODY$;
+        """
+        sa.event.listen(meta, "before_create",
+                        sa.DDL(udf_create))
+        sa.event.listen(meta, "after_drop",
+                        sa.DDL("DROP FUNCTION is_positive(integer)"))
+
+        Table(
             "pgsql_cc",
             meta,
             Column("a", Integer()),
             CheckConstraint("a > 1 AND a < 5", name="cc1"),
             CheckConstraint("a = 1 OR (a > 2 AND a < 5)", name="cc2"),
+            CheckConstraint("is_positive(a)", name="cc3"),
         )
 
-        cc_table.create()
+        meta.create_all()
 
-        reflected = Table("pgsql_cc", MetaData(testing.db), autoload=True)
+        reflected = Table("pgsql_cc", MetaData(), autoload_with=testing.db)
 
         check_constraints = dict(
             (uc.name, uc.sqltext.text)
@@ -1553,6 +1570,7 @@ class ReflectionTest(fixtures.TestBase):
             {
                 u"cc1": u"(a > 1) AND (a < 5)",
                 u"cc2": u"(a = 1) OR ((a > 2) AND (a < 5))",
+                u"cc3": u"is_positive(a)",
             },
         )