Skip to content

Commit 6ce43c0

Browse files
Strip leading UTF-8 BOM in client transaction-tracker classifier helper
Symmetric with the dbapi-side fix. Python's str.strip() does not consider U+FEFF whitespace, so a BOM-prefixed BEGIN / COMMIT / ROLLBACK / SAVEPOINT / RELEASE silently desynced the transaction tracker from the server. SQLite's sqlite3_prepare_v2 skips a leading BOM before tokenisation, so the fix restores SQLite parity. Add a leading lstrip(U+FEFF) before the existing strip(); a parity test in the dbapi tree pins both copies.
1 parent 38c5a90 commit 6ce43c0

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

src/dqliteclient/connection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,15 @@ def _strip_leading_comments(sql: str) -> str:
243243
step with the server. This helper is duplicated from the dbapi
244244
cursor's identical helper rather than introducing an inter-package
245245
import — the parser is small and stable.
246+
247+
Also strips a leading UTF-8 BOM (``\\ufeff``) for parity with
248+
SQLite's ``sqlite3_prepare_v2``, which silently skips it. Python's
249+
``str.strip()`` does NOT consider ``\\ufeff`` whitespace, so a SQL
250+
file imported via ``encoding='utf-8'`` (instead of ``utf-8-sig``)
251+
or written by PowerShell ``Set-Content`` / Notepad would otherwise
252+
desync the savepoint / transaction tracker.
246253
"""
247-
s = sql.strip()
254+
s = sql.lstrip("").strip()
248255
while True:
249256
if s.startswith("--"):
250257
newline = s.find("\n")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Pin: client-layer ``_strip_leading_comments`` strips leading
2+
UTF-8 BOM. See dbapi-side test for full rationale.
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from dqliteclient.connection import _starts_with_tx_verb, _strip_leading_comments
8+
9+
_BOM = ""
10+
11+
12+
def test_strip_leading_comments_strips_lone_bom() -> None:
13+
assert _strip_leading_comments(f"{_BOM}BEGIN") == "BEGIN"
14+
15+
16+
def test_strip_leading_comments_strips_bom_then_whitespace() -> None:
17+
assert _strip_leading_comments(f"{_BOM} COMMIT") == "COMMIT"
18+
19+
20+
def test_strip_leading_comments_strips_bom_then_line_comment() -> None:
21+
assert _strip_leading_comments(f"{_BOM}-- hi\nROLLBACK") == "ROLLBACK"
22+
23+
24+
def test_strip_leading_comments_strips_bom_then_block_comment() -> None:
25+
assert _strip_leading_comments(f"{_BOM}/* hi */ SAVEPOINT a") == "SAVEPOINT a"
26+
27+
28+
def test_starts_with_tx_verb_recognises_bom_prefixed_begin() -> None:
29+
assert _starts_with_tx_verb(f"{_BOM}BEGIN") is True
30+
31+
32+
def test_starts_with_tx_verb_recognises_bom_prefixed_commit() -> None:
33+
assert _starts_with_tx_verb(f"{_BOM}COMMIT") is True

0 commit comments

Comments
 (0)