Uh oh!
There was an error while loading. Please reload this page.
test(driver-sql): isolate the live-dialect matrix per test file - #10381
Conversation
Every live-matrix file resolved its connection from one env var per dialect, so all 14 of them shared one conformance database — one `public` schema on Postgres, one `conformance` database on MySQL — including the driver's internal `_objectstack_sequences` counter table, which each file's autonumber path lazily creates and writes. `cell.config()` now derives a per-FILE schema (Postgres) / database (MySQL) from vitest's own `testPath`, created by a `pool.afterCreate` hook so every pooled connection lands in it. No assertion changed; no test is skipped, quarantined, retried, or given a larger budget. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
📓 Docs Drift Check17 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ 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 1f5eb7fec7f7d29c5e0be15a351cec9613193eb5 && git checkout 1f5eb7fec7f7d29c5e0be15a351cec9613193eb5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bc400aff75d5891f4b147bd1286e637a83d9896d 80589de9a77947f48a18940836eba5884d61f0f2 && git checkout -B drift-repro bc400aff75d5891f4b147bd1286e637a83d9896d && git merge --no-ff 80589de9a77947f48a18940836eba5884d61f0f2
node scripts/docs-audit/affected-docs.mjs --json bc400aff75d5891f4b147bd1286e637a83d9896d |
…t PG introspection follow the session (#9350) The first attempt moved the session with `use` (MySQL) while leaving the connection pointed at `conformance`. knex binds `client.database()` — the CONNECTION's database — into `columnInfo`, so DDL ran in the per-file database while the column read answered from `conformance`: the driver saw an empty column set for a fully populated table and emitted `alter table ... add <column>`, which the server rejected with *Duplicate column name*. Measured on a live MariaDB 10.11: 18 red, matching CI's three failing files exactly. The database is now named in the connection URL, so the two halves cannot disagree, and a vitest globalSetup creates the schemas before any pool opens. Postgres' index read pinned `n.nspname = 'public'`, which returned [] for a table that measurably had a primary key and a declared unique index once the suites moved off `public` — and [] reads as "no indexes", which assertConflictTargetHonoured turns into a refusal. It now resolves the table with to_regclass, the same way every other statement in the session resolves it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
os-elon
commented
Aug 20, 2026
The PR body was rewritten for the second push and lost its signature footer in the edit; recording it here rather than rewriting the body a third time. Authored by the Generated by Claude Code Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9350
Per-file schema isolation for the live-dialect matrix — option C, the maintainer's ruling of 2026-08-20 (
「C 拆掉争用(推荐)」).What was shared
Every live-matrix file resolved its connection from one env var per dialect, so all of them ran against one conformance database: one
publicschema on Postgres, oneconformancedatabase on MySQL. That included_objectstack_sequences— the driver's internal auto-number counter table, which each file's autonumber path lazily creates through ahasTable→createTablecheck-then-act pair, then writes rows into, and which the driver will also rebuild in place (_objectstack_sequences__rebuild, a drop+rename). Vitest runs those files in parallel workers.Measured on a real Postgres 16 before the change: a run of the live-PG files left exactly one schema behind —
public, holding that one shared table.What changed
cell.config()derives a per-file schema (Postgres) / database (MySQL) from vitest's owntestPath.currentLiveSchema()takes no argument — a parameter is the one thing a caller could get wrong, and two files handed the same literal are back to sharing a database with nothing at the call site looking wrong.client.database()and the session cannot disagree. Postgres carriessearchPath.globalSetupcreates the schemas before any pool opens, and drops them afterwards. It has to beglobalSetup:cell.config()is reached from insidebeforeEachin most of the eleven consumers, and the testkit module is cached per worker rather than per file, so a hook registered at its module scope would attach to one file and no other.public— see the driver change.process.env.OS_TEST_*_URLdirectly now go through the cell; a source scan in the new suite keeps the cell the only route to a live server. The 20 existingcell.config()call sites needed no change.⛔ Not in this change, deliberately: no test is skipped, quarantined, retry-wrapped, or given a larger budget; the 5000ms default is untouched; every assertion is byte-identical; nothing in
.github/moves.What went wrong the first time
The first push moved the MySQL session with
usewhile leaving the connection pointed atconformance. knex bindsclient.database()— the connection's database — intocolumnInfo(mysql-querycompiler.js:table_schema = ?). So DDL executed in the per-file database while the column read answered fromconformance: the driver saw an empty column set for a fully populated table and emittedalter table … add label, which the server rejected with Duplicate column name. The unique indexes it thought were missing were never created either, which is where the#8807identity assertions went red.Measured, rather than reasoned about:
client.database()database()conformanceconformanceuse)conformanceos_lv_…← the splitNaming the database in the URL makes those the same value by construction; there is no second place for them to disagree.
The driver change
introspectIndexespinnedn.nspname = 'public'andintrospectSchemapinnedtable_schema = 'public'. Once the suites moved offpublic, both returned empty — measured on live Postgres 16,[]for a table carrying a primary key and a declared unique index, and no tables at all fromintrospectSchema.Empty does not read as "I could not see" downstream; it reads as "there are no indexes", which
assertConflictTargetHonouredturns into a refusal. A fail-open on an identity check, invisible to every existing test because the suites that exercise that path are MySQL-gated.introspectIndexesnow resolves the table withto_regclass(the same first-match-along-search_pathresolution every other statement performs, and unambiguous where a schema list would not be);introspectSchemalistsANY (current_schemas(false)). Identical results for a default deployment — therecurrent_schemas(false)is exactly{public}. Changeset included.Verification — on live servers, both dialects
Postgres 16 at
Asia/Shanghaiand MariaDB 10.11 at+08:00, processTZ=America/New_York, at80589de9a:driver-sqlsuite, both servers,OS_EXPECT_LIVE_DIALECT_MATRIX=1: 106 passed | 1 failed (107 files). The one failure isER_BAD_FIELD_ERROR (1054) — canary lands on 'absent', which is red at the merge base too on this server: MariaDB saysUnknown column '…' in 'INSERT INTO'where MySQL 8.0 saysin 'field list'. An artefact of the stand-in server, not of this change — CI's real MySQL 8.0 never flagged that file.sql-driver-datetime-mysql-storage,sql-driver-time-live-dialects,sql-driver-upsert-conflict-target-dialects— go 18 red → 97 passed with the isolation suite alongside.pnpm --filter @objectstack/driver-sql test→ 102 passed | 5 skipped;typecheckclean.Isolation, measured on the servers rather than asserted: 107 per-file databases on MySQL and 107 per-file schemas on Postgres, with 0 tables left in the shared
conformancedatabase and 0 inpublic— where before the changepublicheld_objectstack_sequences. The two files that use that counter table now each hold their own copy.The controls, each predicted before it was run
The flake ran 4 times in 3 days and zero times in the 3 days since, so a green run proves nothing — it was already going green. Every claim here therefore comes with the negative case:
21b756e38on live MariaDB: 18 failed. Same three files at the merge base: 80 passed, 0 failed.introspectIndexesreturning[]against two indexes the server really holds — and only those two.Restore legs were re-run and re-measured in every case. No build step is involved — the suites import by relative path, so vite loads the source; a stale-artifact read would have left each ablation green.
Gates
Re-derived at the final commit with
node scripts/pm/dispatch-gates.mjs(not a hand-written list) and run there:check:nul-bytes,check:changeset-gate-self-tests,check:objectui-changeset,check:slot-lookup,check:test-source-alias,check:type-source-resolution,check:query-options-erasure,check:type-check-coverage,check:engine-double-contract,check:where-matcher,check-adr-0087-registration,check-changeset-no-major,check-empty-changeset,check-cross-package-test-inputs,scripts/docs-audit/check-affected-docs.mjs— all green on their own verdict lines.check:type-check-debt --re-measurewas not run: its population is the DEBT/TEST_DEBT ledger entries,@objectstack/driver-sqlcarries none, and both changed source files are inside the package's tsc program, which is green.