From: Gord Thompson Date: Thu, 19 Dec 2019 17:20:39 +0000 (-0500) Subject: Improve regex parsing of CHECK constraints for PostgreSQL. X-Git-Tag: rel_1_3_13~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f00b584d540749592730202efa72b07b3b511830;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Improve regex parsing of CHECK constraints for PostgreSQL. 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) --- diff --git a/doc/build/changelog/unreleased_13/5039.rst b/doc/build/changelog/unreleased_13/5039.rst new file mode 100644 index 0000000000..2bf3717612 --- /dev/null +++ b/doc/build/changelog/unreleased_13/5039.rst @@ -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). diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 01a757a5f1..1252e7e692 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -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} diff --git a/test/dialect/postgresql/test_reflection.py b/test/dialect/postgresql/test_reflection.py index 2876151414..9452e8de81 100644 --- a/test/dialect/postgresql/test_reflection.py +++ b/test/dialect/postgresql/test_reflection.py @@ -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)", }, )