Fix SQLite startup crash dropping workspaces.package_manager on populated databases - #550
Open
jbouder wants to merge 2 commits into
Open
Fix SQLite startup crash dropping workspaces.package_manager on populated databases#550jbouder wants to merge 2 commits into
jbouder wants to merge 2 commits into
Conversation
…ated databases The glebarez/sqlite driver emulates DropColumn by rebuilding the table (create workspaces__temp, copy rows, drop workspaces, rename), and unlike DropTable/AlterColumn it does not suspend foreign_keys first, so DROP TABLE on the old workspaces violates the foreign keys held by jobs/publications rows and aborts startup on any pre-#522 database with real usage history. Run the drop pinned to a single connection with foreign_keys suspended (the DSN pragma re-enables it on every new pooled connection, and SQLite ignores the pragma inside a transaction), and drop any workspaces__temp table stranded by a previously failed rebuild before AutoMigrate runs. Fixes #549 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jbouder
marked this pull request as draft
August 28, 2026 16:20
The driver's DropColumn rebuild (create temp, copy, drop, rename) runs inside a single transaction, so the pre-fix FK failure rolls the temp table back — a committed database can never contain a stranded workspaces__temp from that path. Verified empirically: reproducing the crash leaves no temp table, and a real pre-upgrade database that lived through the crash has none either. Drop the startup cleanup and the test's manufactured temp table, which guarded a state that cannot arise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VGNXRuSFe8PvDQYyX1BVDS
jbouder
marked this pull request as ready for review
August 28, 2026 16:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #549
Problem
Since #522, every SQLite-backed nebi (desktop app and
nebi serve) crashes on startup when migrating a database created before thepackage_managerremoval that contains workspaces referenced by child rows (jobs,publications, …):The glebarez/sqlite driver emulates
DropColumnby rebuilding the table (createworkspaces__temp→ copy rows →DROP TABLE workspaces→ rename), and — unlike itsDropTable/AlterColumnpaths — does not suspendforeign_keysfirst. With nebi's DSN-level_pragma=foreign_keys(ON), theDROP TABLEon the parent violates the foreign keys held by referencing rows and aborts startup. The rebuild runs inside a single transaction, so the failure rolls back cleanly — the database is left intact but cannot boot, and the crash recurs on every startup.Postgres is unaffected (
DropColumnis a nativeALTER TABLE ... DROP COLUMNthere).Fix
package_managerdrop pinned to a single connection (db.Connection) withPRAGMA foreign_keys = OFF/ONaround it. Pinning is required because the DSN pragma re-enables enforcement on every new pooled connection, and the pragma must be flipped outside the rebuild's transaction, where SQLite ignores it.An earlier revision of this PR also dropped a "stranded"
workspaces__temptable at startup, on the theory that failed attempts leave the rebuild's temp table behind. That premise was wrong — the rebuild is transactional and rolls the temp table back on failure (verified empirically and against a real post-crash database) — so that cleanup has been removed.Testing
TestMigrateDropsLegacyPackageManagerColumnWithReferencingRowsreproduces the crash scenario: legacy schema with theNOT NULLcolumn and ajobsrow holding an FK to a workspace. It verifies the migration succeeds, the column is gone, and all rows survive.nebi.db(legacy column + referencing job rows):nebi servepreviously crashed, now boots cleanly with data intact.go test ./internal/db/,go build ./internal/...,go vet,gofmtall clean.🤖 Generated with Claude Code