Uh oh!
There was an error while loading. Please reload this page.
fix(driver-sql): recognise knex's canonical postgres / sqlite client spellings in the dialect getters - #11783
Conversation
…ent spellings in the dialect getters
`SqlDriverConfig` is `Knex.Config & {…}`, so every client name knex accepts is
declared valid, while `isPostgres` / `isSqlite` / `isMysql` each enforced two
literals. `postgres` is knex's canonical name for the dialect whose registered
aliases are `pg` and `postgresql` (`sqlite` likewise for `sqlite3`), so those
canonical spellings answered false and silently dropped every dialect branch.
Measured consequence: `nowColumnDefault` emitted a bare CURRENT_TIMESTAMP
default on a DATE column, resolving the calendar day in the server's timezone.
The getters, `DIALECT_CONNECT_TIMEOUT`, `POSTGRES_WIRE_CLIENTS` and
`withUtcSession` now read one identity set per dialect family instead of four
hand-written lists. Wire recognition and the timeout table EXTEND the emission
sets; nothing derives emission identity from their union, which keeps the open
redshift/cockroachdb support-scope question unanswered by this refactor.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VK8rFDtg8eREaxBGX99Csn📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 64dd1d785ca2a3926a6e2e42f5acca9b3cc7b129 && git checkout 64dd1d785ca2a3926a6e2e42f5acca9b3cc7b129
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8b41bcd85160ac1c475d26b2968d74f84954bbfa ad0ef81bfdd5e6ffa426693fe58c21f076ba9739 && git checkout -B drift-repro 8b41bcd85160ac1c475d26b2968d74f84954bbfa && git merge --no-ff ad0ef81bfdd5e6ffa426693fe58c21f076ba9739
node scripts/docs-audit/affected-docs.mjs --json 8b41bcd85160ac1c475d26b2968d74f84954bbfa
|
Uh oh!
There was an error while loading. Please reload this page.
Part of #11550 — the narrow half of that card. The other half is a live
support-scope decision and is out of scope here: see #11756, which remains open.
What was wrong
SqlDriverConfigisKnex.Config & {…}, so the declared surface is everyclient name knex accepts. The three identity getters enforced two literals
each:
knex's own table (
knex/lib/constants.js, knex 3.3.0 — the version this repopins) registers
pgandpostgresqlas aliases of the canonicalpostgres, andsqliteas an alias of the canonicalsqlite3. Soclient: 'postgres'— knex's primary name for the dialect — answeredisPostgres === false, and every Postgres branch in the driver silentlystopped applying on a configuration knex considers valid.
Re-measured on the pinned knex before fixing anything:
pg,postgresandpostgresqleach construct a client withdialect === 'postgresql'anddriverName === 'pg'. One dialect, three spellings.Why it is not cosmetic — reproduced
The card's own example, reproduced on
origin/main@2a6122bd9dwith the newsuite in place (full output in the reverse-verification section):
That is
nowColumnDefault('date')underclient: 'postgres'falling through toknex.fn.now()— a bareCURRENT_TIMESTAMPdefault on aDATEcolumn, whichresolves the calendar day in the server's timezone. It is the exact defect
that method's Postgres branch exists to remove ("measured: a UTC-12 server
records YESTERDAY", per its own docblock). Nothing threw; the column was just
created wrong.
nowColumnDefault('time')anddialectName(which feeds theschema-drift differ, where
'unknown'disables every dialect-aware comparison)degraded the same way.
The change
One identity set per dialect family, and every client-keyed table in the file
now reads from it instead of carrying its own literals:
isPostgrespg,postgresqlpostgresisSqlitesqlite3,better-sqlite3sqliteisMysqlmysql,mysql2DIALECT_CONNECT_TIMEOUTPOSTGRES_WIRE_CLIENTSPOSTGRES_EMIT_CLIENTS+cockroachdb,redshiftwithUtcSessionclient !== 'mysql' && client !== 'mysql2'MYSQL_EMIT_CLIENTSWhat an already-recognised spelling resolves to is unchanged —
pg,postgresql,sqlite3,better-sqlite3,mysql,mysql2all answer exactlywhat they answered before, pinned explicitly because sibling cards this round
select behaviour through these getters.
The direction the tables derive in is load-bearing
DIALECT_CONNECT_TIMEOUTalready containedcockroachdb, andPOSTGRES_WIRE_CLIENTSalready containedredshift. Merging the tables into aunion would have granted both SQL-emission identity as a silent side effect
of a refactor — and that is precisely the open question in #11756. So the
emission sets stay narrow and explicit, and the other two tables extend
them; nothing derives an emission set from a union. Three test cases pin that
asymmetry, and rewriting them is how #11756 gets answered deliberately rather
than accidentally.
Two bounded in-place repairs, named rather than left unremarked
Both are the same defect class as the card (a dialect-identity literal that
disagrees with the others), mechanical, and pinned by knex's own table:
withUtcSessioncarried a fourth hand-written copy of the MySQL pair. It nowreads the shared set. Behaviour is unchanged in both directions and is pinned
by driving the real
pool.afterCreatewith a recording connection.POSTGRES_WIRE_CLIENTS' docblock calledpostgres"knex's own alias". It isthe canon, not an alias — the exact misreading this card fixes. Corrected.
Tests
New:
packages/drivers/driver-sql/src/sql-driver-11550-dialect-client-spellings.test.ts(17 cases). The sweep loads
CLIENT_ALIASES/SUPPORTED_CLIENTSout of theinstalled knex rather than copying them, and asserts an alias-closure
property: if the driver recognises any spelling of a dialect it must recognise
every spelling knex resolves to the same canon. A hand-copied list would keep
passing after a knex upgrade changed the answer.
The primary pin is the DDL
nowColumnDefaultemits, notgetter === true— agetter assertion still passes for a refactor that breaks the branch it feeds.
It runs against a driver constructed exactly as a host would (
client: 'postgres', no stand-in). Spellings this workspace cannot instantiate at all(
sqlite3,sqlite,mysql,mariadb— their npm drivers are not installed,so knex throws in its own constructor) are probed by re-spelling
config.client, and a case asserts that those constructions really do throw, sothe stand-in stays justified rather than merely convenient.
Reverse verification — two legs, both red, both restored
Each leg proved its mutation reached the disk before any result was read
(anchored grep on the removed and injected text), and carried a
trap … EXIT INT TERMrestore. No rebuild step is involved: the suite imports./sql-driver.jsrelatively, so it resolves tosrc/, never todist/.'postgres'back out of the emission set:ON-DISK PROOF: removed-text hits before=1 after=0 | injected-text hits after=1,then
Tests 9 failed | 8 passed (17), primary pin included.git checkout origin/main -- sql-driver.ts(proof:POSTGRES_EMIT_CLIENTS hits in reverted file = 0):Tests 9 failed | 8 passed (17), this time additionally reddeningclient: 'sqlite'.One case failed on first authoring and the test was wrong, not the code:
postgreslegitimately gets apool.afterCreate(the #11389 calendar-day wirehook), so the presence of that function proves nothing about which pin applied.
The case now drives both hooks with a recording connection, which is the only
thing that tells them apart.
Gates and suites — all on
ad0ef81bfd, quoting each gate's own verdict lineDerived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(13 path-matched families + 6 convention-triggered by the added test file), all
run, all green. Selected verdict lines:
driver-conformance before and after are identical: 45 covered, 0 DEBT, 0
exempt.
check:type-check-debtran against the fully built workspace closure(
turbo run build, 70/70 successful) as its header requires.Suites:
@objectstack/driver-sql—Test Files 125 passed | 8 skipped (133),Tests 1963 passed | 109 skipped (2072);typecheckexit 0.--filter '...@objectstack/driver-sql', the dependents direction — 48 projects in scope, 39 with atypecheckscript, incl.driver-turso,driver-sqlite-wasm,objectql,rest,runtime, the examples): all green.SqlDriversubclasses, run in full because they inherit the widened getters:driver-sqlite-wasm395/395,driver-turso1006/1006.Repo-wide
pnpm lintis CI's run; delivered here as a measured narrowing —eslint --no-inline-config --format jsonover the two changed TS files: 2 fileslinted, 0 errors, 0 warnings. The population is read from eslint's own
resolution (
--print-configresolves 6 rules for the changed file; an ignoredfile makes it fail), the count from the JSON, and the invariance holds because
this repo's
eslint.config.mjsnever enables type-aware linting for anyfile (no
parserOptions.project, noprojectService— confirmed from--print-config, and recorded with a positive control in that config's ownheader), so no rule reads cross-file type information and this diff cannot move
the verdict on a file it does not contain.
Deliberately not decided here
Whether
redshiftandcockroachdb— separate knex dialects that speak the pgwire protocol — should be treated as Postgres for SQL emission is a
support-scope question that decides which databases the platform claims to emit
correct DDL for. Out of scope: #11756. They keep their wire recognition and
gain no emission identity.
mariadbandpgnativeare the same class ofquestion and are noted there.
Generated by Claude Code
Generated by Claude Code