Found on main @ a258137, verified by execution on live Postgres 17 and sqlite.
where({ id: [3, 2] }) renders id IN (...) on the select builder and "id" = ? on the write builders, so the same input means two different things depending on which statement it is attached to.
Repro
// rows: id 1,2,3 (tenant 1), id 4 (tenant 999)
await db.selectFrom('t').select('id').where({ id: [3, 2] }).get()
// -> [2, 3] IN semantics
await db.deleteFrom('t').where({ id: [3, 2] }).execute()
// -> deletes nothingSame on Postgres — the delete matches no rows and reports success. Nothing errors on either dialect.
Why it matters
This is the opposite direction to #1101 — the write is too narrow rather than too wide, so it destroys nothing. But it fails silently, and the failure is invisible in exactly the case people write it for: a bulk delete of a set of ids that reports success and removes nothing.
The select builder learned IN semantics for the empty-collection case in #1013/#1083; the write builders were not updated then either, which is the same pattern as #1101.
Suggested fix
Render an array value as IN (...) in the object form on both write builders, matching client.ts:3029-3033 / :3417-3428 on the select side. Sharing one renderer between the three would stop them diverging again — the divergence, not the individual branch, is what keeps producing these.
Note the empty-array case needs deciding at the same time: where({ id: [] }) must not become a predicate that matches everything on a DELETE.
Found by an automated sweep. The sweep additionally claimed the array is consumed as the entire positional bind list on sqlite, dropping other predicates — I could not reproduce that; in my run the statement neither threw nor deleted anything. Only the divergence above is confirmed.