From 256efbb2b8ffa69e3e08bc987161a8e1b9649dc9 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 28 Mar 2022 13:46:24 -0400 Subject: [PATCH] fix quotes regexp for SQLite CHECK constraints Fixed bug where the name of CHECK constraints under SQLite would not be reflected if the name were created using quotes, as is the case when the name uses mixed case or special characters. Fixes: #5463 Change-Id: Ic3b1e0a0385fb9e727b0880e90815ea2814df313 (cherry picked from commit cb52b934000047278dbb63d0cfffdb4eae1f669c) --- doc/build/changelog/unreleased_14/5463.rst | 8 ++++++++ lib/sqlalchemy/dialects/sqlite/base.py | 12 ++++++++---- lib/sqlalchemy/testing/suite/test_reflection.py | 6 ++++-- 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 doc/build/changelog/unreleased_14/5463.rst diff --git a/doc/build/changelog/unreleased_14/5463.rst b/doc/build/changelog/unreleased_14/5463.rst new file mode 100644 index 0000000000..5de6182acf --- /dev/null +++ b/doc/build/changelog/unreleased_14/5463.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, sqlite, reflection + :tickets: 5463 + + Fixed bug where the name of CHECK constraints under SQLite would not be + reflected if the name were created using quotes, as is the case when the + name uses mixed case or special characters. + diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 7ba9700d70..49e4b5c195 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -2449,17 +2449,21 @@ class SQLiteDialect(default.DefaultDialect): if not table_data: return [] - CHECK_PATTERN = r"(?:CONSTRAINT (\w+) +)?" r"CHECK *\( *(.+) *\),? *" + CHECK_PATTERN = r"(?:CONSTRAINT (.+) +)?" r"CHECK *\( *(.+) *\),? *" check_constraints = [] # NOTE: we aren't using re.S here because we actually are # taking advantage of each CHECK constraint being all on one # line in the table definition in order to delineate. This # necessarily makes assumptions as to how the CREATE TABLE # was emitted. + for match in re.finditer(CHECK_PATTERN, table_data, re.I): - check_constraints.append( - {"sqltext": match.group(2), "name": match.group(1)} - ) + name = match.group(1) + + if name: + name = re.sub(r'^"|"$', "", name) + + check_constraints.append({"sqltext": match.group(2), "name": name}) return check_constraints diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index a1f2e3bc94..459a4d8211 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -1152,7 +1152,9 @@ class ComponentReflectionTestExtra(fixtures.TestBase): metadata, Column("a", Integer()), sa.CheckConstraint("a > 1 AND a < 5", name="cc1"), - sa.CheckConstraint("a = 1 OR (a > 2 AND a < 5)", name="cc2"), + sa.CheckConstraint( + "a = 1 OR (a > 2 AND a < 5)", name="UsesCasing" + ), schema=schema, ) @@ -1179,8 +1181,8 @@ class ComponentReflectionTestExtra(fixtures.TestBase): eq_( reflected, [ + {"name": "UsesCasing", "sqltext": "a = 1 or a > 2 and a < 5"}, {"name": "cc1", "sqltext": "a > 1 and a < 5"}, - {"name": "cc2", "sqltext": "a = 1 or a > 2 and a < 5"}, ], ) -- 2.47.2