Skip to content

fix(db): open a real v0.6.0 database in the test suite, and stop copying broken migrations - #597

Merged
rainmanjam merged 1 commit into
mainfrom
fix/db-migration-guard
Aug 27, 2026
Merged

fix(db): open a real v0.6.0 database in the test suite, and stop copying broken migrations#597
rainmanjam merged 1 commit into
mainfrom
fix/db-migration-guard

Conversation

@rainmanjam

Copy link
Copy Markdown
Owner

Closes #580 #582.

Nothing in this repo opened a database built from a shipped schema

schema.sql is CREATE TABLE IF NOT EXISTS, so a column declared only there reaches fresh installs and never an upgrade — the asymmetry that has caused data loss here before.

Every test was blind to it: dbtest's template is a fresh install, each migration test hand-builds only the table it cares about, and the "0.6.x-shaped" fixture in schema_version_test.go is a hand-written five-column users table, not the schema v0.6.0 actually shipped.

So four migrations were correct because four separate changes each remembered one. That's rung zero, and it happened to hold four times.

The device: the previous release's real schema.sql, checked in as a fixture, opened through the ordinary Open path and compared object-for-object — columns included, because a table present in both but short a column is the case that matters — against a fresh install.

Verified by removing MigrateHookAllowPrivateTarget from Open: it fails naming column hooks.allow_private_target.

The fixture is a historical artefact. When this test fails the answer is a Migrate* on Open's path, never an edit to the fixture — and the test says so, because that's the tempting wrong fix.

Two migrations returned bare

Nine closed the handle and wrapped; two didn't. They were the two most recently added — because the way you write a migration here is to copy the one above it, and these two had been copied from each other.

The AST guard that now holds that shape found a third case the issue hadn't reported, and it turned out to be legitimate: MigrateSchemaVersion closes but doesn't wrap, because its own error already reads stamp schema version 1: … and a migrate: prefix would add nothing. It's recorded as an exemption with that reason rather than "fixed" into consistency it doesn't need.

Warning rung, not Control — Go can't express "this call must be followed by those two statements". Control would need the migrations behind a runner that owns the handle; worth doing at the tenth, not the eleventh line of a fix.

Three comments had drifted off their calls

Including one attributing MigrateSources's ordering constraint — "Last, because it reads settings and writes to destinations" — to a hooks migration inserted between the comment and its call.

Both guards mutation-verified. gofmt, go vet, internal/db green.

https://claude.ai/code/session_01A8N3W5ct9SZtHK9sCDD9cL

…ing broken migrations

Closes #580 #582.

NOTHING IN THIS REPO OPENED A DATABASE BUILT FROM A SHIPPED SCHEMA. schema.sql
is CREATE TABLE IF NOT EXISTS, so a column declared only there reaches fresh
installs and never an upgrade -- an asymmetry that has caused data loss here
before. Every test was blind to it: dbtest's template is a fresh install, each
migration test hand-builds only the table it cares about, and the "0.6.x-shaped"
fixture in schema_version_test.go is a hand-written five-column users table
rather than the schema v0.6.0 actually shipped.

So four migrations were correct because four changes each REMEMBERED one. That
is rung zero, and it held four times.

The device is the previous release's real schema.sql, checked in as a fixture,
opened through the ordinary Open path and compared object-for-object -- columns
included, because a table present in both but short a column is the case that
matters -- against a fresh install. Verified by removing
MigrateHookAllowPrivateTarget from Open: it fails naming
`column hooks.allow_private_target`.

The fixture is a historical artefact. When this test fails the answer is a
Migrate* on Open's path, never an edit to the fixture, and the test says so.

TWO MIGRATIONS RETURNED BARE while nine closed the handle and wrapped. They were
the two most recently added, because the way you write a migration here is to
copy the one above it -- and these two had been copied from each other. A leaked
handle on a path about to exit is small; an inconsistency the next copy inherits
is not.

The AST guard that now holds that shape found a third case the issue had not
reported, and it turned out to be legitimate: MigrateSchemaVersion closes but
does not wrap, because its own error already reads `stamp schema version 1: ...`
and a "migrate: " prefix would add nothing. It is recorded as an exemption with
that reason rather than being "fixed" into consistency it does not need.

Three comments had drifted off the calls they describe, including one that
attributed MigrateSources's ordering constraint -- "Last, because it reads
settings and writes to destinations" -- to a hooks column migration inserted
between the comment and its call.

Claude-Session: https://claude.ai/code/session_01A8N3W5ct9SZtHK9sCDD9cL
Copilot AI lite review requested due to automatic review settings August 26, 2026 21:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
16.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@rainmanjam
rainmanjam merged commit f753066 into main Aug 27, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

release: two migrations skip sqldb.Close() and error wrapping on failure, and one carries the wrong comment

2 participants