README, §Write metadata: "Adapters report driver metadata alongside the rows: on a successful Result, result.meta?.rowCount carries the affected-row count and result.meta?.lastInsertRowid the generated id … so an INSERT without RETURNING is no longer a black box."
Two shapes fall outside that in the node:sqlite adapter.
Reproduction (DatabaseSync(':memory:'), table t (id integer primary key, name text))
| statement |
returned |
expected |
insert into t (name) values (?) |
{ rows: [], meta: { rowCount: 1, lastInsertRowid: 1 } } |
✅ |
update t set name = ? where id = ? |
{ rows: [], meta: { rowCount: 1 } } |
✅ |
with x as (select 1 as v) insert into t (name) select 'w' from x |
{ rows: [], meta: {} } |
{ rowCount: 1, lastInsertRowid: … } |
insert into t (name) values ('r') returning id |
[{ id: 3 }] — no meta at all |
rows and { rowCount, lastInsertRowid } |
Root cause
src/adapters/node-sqlite.ts:
writeMeta keys off readLeadingKeyword(sql), and a CTE-led DML statement leads with with, which is in RESULT_KEYWORDS, not DML_KEYWORDS — so no rowCount. (statement.columns() correctly routes it to run(), it is only the meta classification that misses.)
- The
.all() branch returns the row array directly, so a row-returning write (RETURNING) never carries meta. Every other adapter returns { rows, meta } for both shapes.
Suggested fix: classify the leading keyword after skipping a WITH … prefix (the CTE case is the clear-cut one — run() already returns changes/lastInsertRowid there, they are just discarded).
The RETURNING case needs a decision: StatementSync.all() returns only rows, so the numbers would have to come from a follow-up select changes(), last_insert_rowid() on the same connection. If that extra round trip isn't wanted, the README's promise should be narrowed to writes without RETURNING instead.
Found in an audit of master @ dc1afdb (v0.1.8). Related history: #203 / PR #208.
README, §Write metadata: "Adapters report driver metadata alongside the rows: on a successful
Result,result.meta?.rowCountcarries the affected-row count andresult.meta?.lastInsertRowidthe generated id … so an INSERT withoutRETURNINGis no longer a black box."Two shapes fall outside that in the
node:sqliteadapter.Reproduction (
DatabaseSync(':memory:'), tablet (id integer primary key, name text))insert into t (name) values (?){ rows: [], meta: { rowCount: 1, lastInsertRowid: 1 } }update t set name = ? where id = ?{ rows: [], meta: { rowCount: 1 } }with x as (select 1 as v) insert into t (name) select 'w' from x{ rows: [], meta: {} }{ rowCount: 1, lastInsertRowid: … }insert into t (name) values ('r') returning id[{ id: 3 }]— no meta at all{ rowCount, lastInsertRowid }Root cause
src/adapters/node-sqlite.ts:writeMetakeys offreadLeadingKeyword(sql), and a CTE-led DML statement leads withwith, which is inRESULT_KEYWORDS, notDML_KEYWORDS— so norowCount. (statement.columns()correctly routes it torun(), it is only the meta classification that misses.).all()branch returns the row array directly, so a row-returning write (RETURNING) never carries meta. Every other adapter returns{ rows, meta }for both shapes.Suggested fix: classify the leading keyword after skipping a
WITH …prefix (the CTE case is the clear-cut one —run()already returnschanges/lastInsertRowidthere, they are just discarded).The
RETURNINGcase needs a decision:StatementSync.all()returns only rows, so the numbers would have to come from a follow-upselect changes(), last_insert_rowid()on the same connection. If that extra round trip isn't wanted, the README's promise should be narrowed to writes withoutRETURNINGinstead.Found in an audit of
master@ dc1afdb (v0.1.8). Related history: #203 / PR #208.