Production moved to Postgres. The app had only ever run on SQLite, so the first run of the new test-postgres CI job surfaced 83 failures across four classes. Most are fixed; this tracks the rest.
test-postgres is continue-on-error: true for now, because gating on a known-red suite means no deploys at all. It goes back to a hard gate the moment this reaches zero — a non-blocking check nobody reads is the same as no check.
Fixed
?placeholders. Raw SQL with?is a syntax error on Postgres, reported against the next token (syntax error at or near "AND"). Fixed in the library:toDialectPlaceholdersin bun-query-builder 0.2.32, quoting-aware so a?inside a literal is left alone.- SQLite-only
strftime/json_extractthroughout the query engine. Fixed in the library:jsonExtract/dateBucket/booleanLiteraljoined theDialectDriverinterface in 0.2.31, implemented for every dialect. The app now carries no dialect knowledge at all. - Booleans written as
1and0. Nine statements, plus two test fixtures. Postgres refuses them; SQLite accepts them. schema.text()producingvarchar(255).textandstringshared a branch in the migration generator's type inference, sotext()was a no-op unless it also carried a large.max(). That is what put JSON blobs in 255-char columns. Fixed in bun-query-builder 0.2.33.- A literal
'owner'meeting an enum column in aUNIONand twoCASEexpressions. Postgres coerces the literal into the enum and refuses it, because the owner is synthesised rather than being aproject_membersrow. Both sides are now explicitlyCAST(... AS TEXT).
Remaining (24 failures)
Integers come back as strings. expect(result.projectId).toBe(248) gets "248". The Postgres driver returns bigint as a string, which is correct for a driver — JS cannot hold every bigint — and wrong for an id the application compares with ===. Needs a decision: coerce at the boundary in the app, or have the query builder normalise integer-typed columns. The second is better if it can be done without lying about large values.
LIMIT must not be negative (3). Somewhere a limit is computed as negative and SQLite silently treated it as unbounded. That is a real bug in either case and worth finding regardless of dialect.
A few value too long for character varying(255) remain on tables other than the ones already widened. Each needs the column checked against what the model says it holds.
Why this is worth the noise
Every one of these passed the whole SQLite suite. They are exactly the bugs a single-dialect test suite cannot see, and the reason the job exists.