Skip to content

Commit 162156f

Browse files
Pin _is_multi_statement consecutive-semicolon walker behaviour
cursor.py:526-535 walks past consecutive ``;`` and trailing comments to classify SQL as single- vs multi-statement. The walker is the security guard preventing stacked-query smuggling into dqlite's prepare path (which silently drops everything past the first ``;``). Pin the actual current contract: - ``INSERT ...;;`` → multi (conservative) - ``INSERT ...; -- comment\n;`` → multi (conservative) - ``INSERT ...; /* */ ;`` → multi (conservative) - ``INSERT ...;`` + whitespace / comments only → NOT multi The conservative classification on ``;;`` is the safer arm: a false-negative would smuggle a stacked second statement; a false-positive only rejects valid-but-unusual SQL with diagnostic. Companion to the existing leading-semicolon and tracker-desync coverage; this test exercises the trailing-walker arm directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e1f4ad8 commit 162156f

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Pin: ``_is_multi_statement`` consecutive-semicolon walker
2+
correctly handles ``;;`` (empty statement) and ``; <comment> ;``
3+
(only-comment statement) without misclassifying them as
4+
multi-statement.
5+
6+
dqlite's prepare silently drops everything past the first ``;``,
7+
so the classifier is the security guard preventing stacked-query
8+
smuggling. A regression that collapses the walker would either:
9+
- Reject valid SQL terminated with ``;;`` (false positive), OR
10+
- Smuggle multi-statement SQL with comment-only tails (false
11+
negative).
12+
13+
Direct unit-tests of the walker at cursor.py:533-536. Companion
14+
to the existing leading-semicolon and tracker-desync coverage.
15+
"""
16+
17+
from dqlitedbapi.cursor import _is_multi_statement
18+
19+
20+
def test_double_semicolon_at_end_classified_as_multi() -> None:
21+
"""Conservative classification: ``;;`` produces a non-empty tail
22+
after the first ``;`` (the second ``;`` itself), and the walker
23+
treats it as multi-statement. False-positive, but safer than
24+
false-negative since dqlite's prepare path silently drops
25+
everything past the first ``;``. A regression that makes this
26+
return False would also mask real ``; <noise> ; INSERT`` as
27+
single-statement."""
28+
assert _is_multi_statement("INSERT INTO t VALUES(1);;") is True
29+
30+
31+
def test_semicolon_then_comment_then_semicolon_classified_as_multi() -> None:
32+
"""Same conservative posture: a trailing ``;`` after the first
33+
``;`` (with only comments / whitespace between) trips the
34+
classifier. The walker is the security guard — false-positive
35+
here is acceptable and prevents stacked-query smuggling."""
36+
assert _is_multi_statement("INSERT INTO t VALUES(1); -- end\n;") is True
37+
assert _is_multi_statement("INSERT INTO t VALUES(1); /* */ ;") is True
38+
39+
40+
def test_double_semicolon_then_real_second_statement_IS_multi() -> None:
41+
"""Definitive multi-statement: trailing INSERT after ``;;``."""
42+
assert _is_multi_statement("INSERT INTO t VALUES(1);; INSERT INTO t VALUES(2)") is True
43+
44+
45+
def test_trailing_whitespace_after_single_semicolon_is_not_multi() -> None:
46+
"""A single trailing ``;`` followed only by whitespace / comments
47+
must NOT be classified as multi — that's the docstring's
48+
explicit contract."""
49+
assert _is_multi_statement("INSERT INTO t VALUES(1); ") is False
50+
assert _is_multi_statement("INSERT INTO t VALUES(1); -- comment\n") is False
51+
assert _is_multi_statement("INSERT INTO t VALUES(1); /* trailing */") is False
52+
53+
54+
def test_single_statement_is_not_multi() -> None:
55+
assert _is_multi_statement("SELECT 1") is False
56+
assert _is_multi_statement("SELECT 1;") is False
57+
58+
59+
def test_real_multi_statement_classified_correctly() -> None:
60+
assert _is_multi_statement("SELECT 1; SELECT 2") is True
61+
assert _is_multi_statement("INSERT INTO t VALUES(1); SELECT * FROM t") is True

0 commit comments

Comments
 (0)