Skip to content

Commit d6dd7bf

Browse files
Document cross-version NULL semantics shift in driver READMEs
Upstream commit f30fc99 (2026-01-25) fixed a long-standing C-server bug where NULL cells in columns declared BOOLEAN / DATE / DATETIME / TIMESTAMP were emitted with the column's coerced type (BOOLEAN(0) indistinguishable from FALSE; ISO8601("") indistinguishable from empty string). The Python codec is correct on both sides — it faithfully decodes whatever the server emits — but a user upgrading their cluster across f30fc99 sees the data type of those NULL cells flip from bool/str to NoneType with no driver-side warning. Add a Cross-version semantic shift section to the client README so operators and library users know to audit their `is None` checks when upgrading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 61a4778 commit d6dd7bf

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,27 @@ double-FIN on the parent's socket is a real risk if the guard is
7878
bypassed (e.g. via `__new__` to skip `__init__`). Just create the
7979
pool / connection in the child process you intend to use it from.
8080

81+
## Cross-version semantic shift: NULL in BOOLEAN/DATETIME columns
82+
83+
Upstream dqlite commit `f30fc99` (`query: preserve SQLITE_NULL type
84+
for NULL values`, 2026-01-25) changed the wire encoding of NULL cells
85+
in columns declared `BOOLEAN`, `DATE`, `DATETIME`, or `TIMESTAMP`:
86+
87+
- **Before** `f30fc99`: a NULL cell was emitted with the column's
88+
coerced type (`BOOLEAN` with value `0`, or `ISO8601` with value
89+
`""`) — indistinguishable from a real `FALSE` or empty string.
90+
- **After** `f30fc99`: a NULL cell is emitted with `SQLITE_NULL`
91+
(tag 5) and decodes to `None`.
92+
93+
Python code that uses `if row[0] is None:` against an old-server
94+
cluster will silently miss NULL rows. After a cluster upgrade past
95+
`f30fc99`, the same code starts firing where it previously read
96+
`False` or `""`. There is no driver-level handshake field that
97+
distinguishes the two server versions; check your dqlite cluster
98+
version before relying on `is None` for `BOOLEAN` / `DATETIME`
99+
columns. The Python codec faithfully decodes whatever the server
100+
emits — the server, not the driver, drives this semantics shift.
101+
81102
## Layering
82103

83104
`dqlite-client` is the low-level async wire client. Most applications

0 commit comments

Comments
 (0)