]> 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:31:31 +0000 (09:31 -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

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 cbc750066c1f1596f38a78b4c229c87b825e3708..e8e79b230e5acc4b82b8b71a1940f021058cdce1 100644 (file)
@@ -3485,12 +3485,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 b6cedd55fc7ff48c46c4224007f1d137f863bd6c..cc231933c528b0820ec35a5b5fe3586ad6221c1a 100644 (file)
@@ -1524,17 +1524,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)
@@ -1547,6 +1564,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)",
             },
         )