Skip to content

Commit 447b3f9

Browse files
Pin trigger preamble paren and quote-styled identifier branches
_scan_for_trigger_begin has dedicated branches for paren tracking (WHEN clauses), double-quoted / bracketed / backtick identifiers, and line / block comments between TRIGGER and BEGIN. Existing tests covered the negative cases (string-literal containing CREATE TRIGGER, nested CREATE TRIGGER) but never fed a preamble where those constructs appear legitimately, so a refactor that drops paren_depth tracking or one of the quote handlers would silently regress. Pin the positive cases: WHEN with paren-nested BEGIN-flag, double- quoted/bracketed/backtick table names with doubled-character escapes, and -- / /* */ comments between ON-clause and BEGIN. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 18c3316 commit 447b3f9

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
r"""Pin: ``_scan_for_trigger_begin`` correctly handles paren-depth and
2+
quote-styled identifiers inside the trigger preamble (between
3+
``TRIGGER`` and ``BEGIN``).
4+
5+
The scanner has dedicated branches for ``(`` / ``)`` paren tracking,
6+
``"quoted"`` / ``[bracketed]`` / ``\`backtick\``` identifiers, and
7+
``--`` / ``/* */`` comments inside the preamble. Existing tests
8+
exercise:
9+
10+
- TEMP / TEMPORARY trigger
11+
- INSTEAD OF trigger
12+
- string-literal-with-CREATE-TRIGGER negative case
13+
- nested CREATE TRIGGER
14+
15+
But never feed a preamble containing those legitimately. Without
16+
paren tracking a ``WHEN (BEGIN_FLAG = 1)`` clause would FALSE-match
17+
the standalone ``BEGIN``; without quote-styled-identifier handling
18+
a column name like ``"BEGIN"`` (quoted reserved word) would do the
19+
same.
20+
"""
21+
22+
from __future__ import annotations
23+
24+
from dqliteclient.connection import _split_top_level_statements
25+
26+
27+
class TestPreambleParenTracking:
28+
def test_when_clause_with_begin_token_in_paren_does_not_split(self) -> None:
29+
# If paren_depth tracking were missing, the BEGIN token inside
30+
# WHEN (...) would be matched as the trigger body opener and
31+
# the body delimiters would be misaligned.
32+
sql = (
33+
"CREATE TRIGGER aud AFTER UPDATE ON x "
34+
"FOR EACH ROW WHEN (NEW.a = 'BEGIN' OR OLD.b > 0) BEGIN\n"
35+
" UPDATE y SET v=1;\n"
36+
"END;"
37+
)
38+
pieces = _split_top_level_statements(sql)
39+
assert len(pieces) == 1, f"got {len(pieces)} pieces: {pieces!r}"
40+
41+
def test_nested_when_clauses(self) -> None:
42+
sql = (
43+
"CREATE TRIGGER aud AFTER UPDATE ON x "
44+
"FOR EACH ROW WHEN ((NEW.a > 0) AND (NEW.b < 10)) BEGIN\n"
45+
" UPDATE y SET v=1;\n"
46+
"END;"
47+
)
48+
pieces = _split_top_level_statements(sql)
49+
assert len(pieces) == 1
50+
51+
52+
class TestPreambleQuotedIdentifiers:
53+
def test_double_quoted_table_name_in_preamble(self) -> None:
54+
sql = (
55+
'CREATE TRIGGER aud AFTER INSERT ON "my table" BEGIN\n'
56+
" UPDATE y SET v=1;\n"
57+
"END;"
58+
)
59+
pieces = _split_top_level_statements(sql)
60+
assert len(pieces) == 1, f"got {len(pieces)} pieces: {pieces!r}"
61+
62+
def test_double_quoted_with_doubled_quote_escape(self) -> None:
63+
# SQLite identifier quoting: "" inside "..." is a literal "
64+
sql = (
65+
'CREATE TRIGGER aud AFTER INSERT ON "weird""name" BEGIN\n'
66+
" UPDATE y SET v=1;\n"
67+
"END;"
68+
)
69+
pieces = _split_top_level_statements(sql)
70+
assert len(pieces) == 1
71+
72+
def test_bracketed_table_name_in_preamble(self) -> None:
73+
sql = (
74+
"CREATE TRIGGER aud AFTER INSERT ON [my table] BEGIN\n"
75+
" UPDATE y SET v=1;\n"
76+
"END;"
77+
)
78+
pieces = _split_top_level_statements(sql)
79+
assert len(pieces) == 1
80+
81+
def test_backtick_table_name_in_preamble(self) -> None:
82+
sql = (
83+
"CREATE TRIGGER aud AFTER INSERT ON `my table` BEGIN\n"
84+
" UPDATE y SET v=1;\n"
85+
"END;"
86+
)
87+
pieces = _split_top_level_statements(sql)
88+
assert len(pieces) == 1
89+
90+
def test_backtick_with_doubled_backtick_escape(self) -> None:
91+
sql = (
92+
"CREATE TRIGGER aud AFTER INSERT ON `weird``name` BEGIN\n"
93+
" UPDATE y SET v=1;\n"
94+
"END;"
95+
)
96+
pieces = _split_top_level_statements(sql)
97+
assert len(pieces) == 1
98+
99+
100+
class TestPreambleComments:
101+
def test_line_comment_between_trigger_and_begin(self) -> None:
102+
sql = (
103+
"CREATE TRIGGER aud AFTER INSERT ON x -- migration v3\n"
104+
"BEGIN\n"
105+
" UPDATE y SET v=1;\n"
106+
"END;"
107+
)
108+
pieces = _split_top_level_statements(sql)
109+
assert len(pieces) == 1
110+
111+
def test_block_comment_between_trigger_and_begin(self) -> None:
112+
sql = (
113+
"CREATE TRIGGER aud AFTER INSERT ON x /* notes */ BEGIN\n"
114+
" UPDATE y SET v=1;\n"
115+
"END;"
116+
)
117+
pieces = _split_top_level_statements(sql)
118+
assert len(pieces) == 1

0 commit comments

Comments
 (0)