Repository navigation
fix(sqlite): type a scalar subquery's json nested in json soundly - #51
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Important
The new "json() of a scalar subquery is NULL" rule fires for every JSON constructor whose first argument is a subquery, not just json(). Constructors that never return NULL get typed | null.
Reviewed changes
Reviewed the single commit, which makes a scalar subquery's JSON unknown when it is nested in other JSON, and types json((select …)) as the shape the subquery builds.
carry/json_factforselect: a scalar subquery's value is nowCarry::Maybe(sounknowninside JSON) unless it is plainly not JSON, because SQLite 3.54.1 drops the subtype.subquery_shape:json((select <constructor>))takes the shape of the subquery's one column, through bothbuilt_textandshape_here. Compounds andVALUESare excluded.- Nullability of
json((select …)): nullable when the subquery may find no row, both at the top level (type_of) and nested (json_fact). - Oracle, tests, docs, driver matrix:
kept.sqlmoves tounknown, a newsubquery_json.sqlcovers thejson()forms, the profile statements wrap their subqueries injson(), and the SQLite docs' rule says to do the same.engine.wasmis rebuilt.
claude-opus-5-5 | 𝕏
SQLite 3.54.1, which Node 26.11's node:sqlite bundles, drops the JSON
subtype of a scalar subquery's value, so json_object('n', (select ...))
nests it as text where 3.53 nests it as JSON. Such a value is now
unknown, and json() around the subquery nests its JSON on every SQLite.
Only json() and jsonb() of a scalar subquery are NULL where it finds no row; json_array and the aggregates read that NULL as a null.
0d353ce to
5f165fe
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Reviewed 5f165fe, which narrows the subquery-nullability rule from the previous review to json() and jsonb().
type_of"function" arm: a constructor's nullability now follows its subquery argument only when the call isjson().jsonb()never reaches this arm, becausebuilt_textdoesn't cover it, but it already becomes nullable throughNulls::IfArgs.json_factshape branch: the same rule is now limited tojsonandjsonb, sojson_arrayandjson_group_arrayof a subquery stay non-null where they are nested.- Tests: added a top-level
json_typedassertion forjson_array,json_group_arrayandjson_group_object, two kept-list rows, and an oracle statement,subquery_null.sql, whose expected types ([string | null],[text?]) match what SQLite returns:[null].
claude-opus-5-5 | 𝕏

SQLite 3.54.1, the SQLite inside Node 26.11's
node:sqlite, drops the JSON subtype of a scalar subquery's value. So JSON built in a subquery and nested in other JSON comes back as text there, where SQLite 3.53 (better-sqlite3) nests it as JSON:Reylo typed
nas the object, which is wrong on 3.54. Neither 3.54.0's nor 3.54.1's release notes mention the change, so both behaviors are out there, depending on the driver's SQLite.unknown(engine/sqlite/src/sqlite.rs,carry): it may be the JSON or its text.json((select …))marks the subquery's text as JSON again on every SQLite, so it now takes the shape the subquery builds, nested or at the top of the select list. It's nullable where the subquery may find no row.The SQLite docs page's "Built JSON inside built JSON" rule now says to wrap the subquery in
json(…). Its new pair was run through.run()onnode:sqlite(SQLite 3.54.1) andbetter-sqlite3(3.53.4).This also fixes
tests/representation.test.ts› "SQLite: JSON a statement builds…", which fails onmainunder Node 26.11 (CI runs Node 24, so it stayed green): the test's profile and the driver matrix's S10j statement now wrap their subqueries injson().Tests: the engine's unit tests (the old expectations moved to the
unknownlist, plusjson()cases and a no-row case); the oracle casesqlite_json(profile wrapped, and a newsubquery_json.sql); the full oracle with--strict-coverage; the wasm fingerprint; the loader tests under Node 26.11.1; and the driver matrix'snode:sqliteandbetter-sqlite3@13jobs under Node 26.11.1, S10j included.