)
# Notes:
- # * The pattern currently matches any character for the name of the constraint,
- # including newline and tab characters, as long as none of the SQLite's table constraints
- # keywords are encountered seperated by spaces.
- # This prevents the pattern from matching subsequent constraints as part of the name.
- # * Because check constraints can contain special characters and newline and tab characters,
- # the pattern matches any character untill either the beginning of the next constraint
- # statement using a non-capturing non-consuming group (allowing the next one to match),
- # or the end of the table definition e.g. newline and closing ')'.
+ # * The pattern currently matches any character for the name of the
+ # constraint, including newline characters (re.S flag) as long as
+ # none of the SQLite's table constraints keywords are encountered
+ # by a negative lookahead.
+ # This prevents the pattern from matching subsequent constraints
+ # as part of the name.
+ # This is only done for those keywords if seperated by spaces, to
+ # support constraint names that contains them e.g. "check_value".
+ #
+ # * Because check constraint definitions can also contain newline
+ # or tab characters, the pattern matches any character untill either
+ # the beginning of the next constraint statement using a
+ # non-capturing and non-consuming group, allowing the next one
+ # to match, or the end of the table definition
+ # e.g. newline and closing ')'.
CHECK_PATTERN = r"(?:CONSTRAINT ((?:(?! PRIMARY | FOREIGN KEY| UNIQUE | CHECK ).)+) )?CHECK \((.+?)\)(?:, *\n\t?(?=CONSTRAINT|CHECK)|\n\))"
cks = []
- for match in re.finditer(CHECK_PATTERN, table_data or "", re.I|re.S):
+ for match in re.finditer(CHECK_PATTERN, table_data or "", re.I | re.S):
name = match.group(1)
if name:
Table("q", meta, Column("id", Integer), PrimaryKeyConstraint("id"))
# intentional new line
- Table("r", meta, Column("id", Integer), Column("value", Integer), Column("prefix", String), PrimaryKeyConstraint("id"),
+ Table("r", meta,
+ Column("id", Integer),
+ Column("value", Integer),
+ Column("prefix", String),
+ PrimaryKeyConstraint("id"),
CheckConstraint("id > 0"),
# Constraint definition with newline and tab characters
- CheckConstraint("((value > 0) AND \n\t(value < 100) AND \n\t(value != 50))", name='ck_r_value_multiline'),
- # Constraint name with special characters and 'check' in the name
+ CheckConstraint(
+ """((value > 0) AND \n\t(value < 100) AND \n\t
+ (value != 50))""",
+ name='ck_r_value_multiline'),
+ # Constraint name with special chars and 'check' in the name
CheckConstraint("value IS NOT NULL", name="^check-r* #\n\t"),
# Constraint definition with special characters.
CheckConstraint("prefix NOT GLOB '*[^-. /#,]*'")
- )
+ )
meta.create_all(conn)
{"sqltext": "q > 1 AND q < 6", "name": None},
],
)
+ print(inspector.get_check_constraints("r"))
eq_(
inspector.get_check_constraints("r"),
[
{"sqltext": "value IS NOT NULL", "name": "^check-r* #\n\t"},
- {"sqltext": "((value > 0) AND \n\t(value < 100) AND \n\t(value != 50))", "name": "ck_r_value_multiline"},
+ # Triple-quote multi-line definition should have added a
+ # newline and whitespace:
+ {"sqltext": "((value > 0) AND \n\t(value < 100) AND \n\t\n"
+ " (value != 50))",
+ "name": "ck_r_value_multiline"},
{"sqltext": "id > 0", "name": None},
{"sqltext": "prefix NOT GLOB '*[^-. /#,]*'", "name": None},
],
else:
assert False
+
class SavepointTest(fixtures.TablesTest):
"""test that savepoints work when we use the correct event setup"""