Fix two aged-database migration bugs behind #2748's Lite startup failure - #2750
Conversation
Root-caused both errors from the reported v47->v56 upgrade log against real DuckDB (1.5.5, matching this repo's pin), not just the error text: - v48 (server_properties NOT NULL relaxation): fails on any database that completed a prior startup, because DuckDbSchemaGenerator's default-case index (idx_server_properties_time) already exists and DuckDB's ALTER COLUMN dependency check refuses a table with ANY index on it, even one naming none of the altered columns. Confirmed empirically that a plain archive VIEW does NOT trigger this - only the index does; an earlier attempt at this fix targeted the view and was wrong. Fix: drop the index before the ALTERs, let the schema loop's unconditional index-creation step (already runs right after migrations) recreate it. - v53 (config_database_state_expected new columns): fails on any database old enough to predate the table's introduction (#2166/ #2203), because the table has no numbered CREATE migration of its own - it only exists via Schema.GetAllTableStatements()'s unconditional CREATE TABLE IF NOT EXISTS, which does not run until after RunMigrationsAsync returns. v54's migration already does this correctly (CREATE-then-ALTER); v53 never did. Fix: call the same idempotent CreateDatabaseStateExpectedTable DDL before the ALTERs, matching v54's established pattern. Both fixes validated against a standalone DuckDB.NET 1.5.5 repro reproducing the exact logged error text before the fix and confirming success after. Added AgedDatabaseMigrationTests.cs seeding both real preconditions (an aged NOT NULL server_properties + its real default index; a v47 database missing config_database_state_expected entirely) against the actual DuckDbInitializer.InitializeAsync path. Neither bug is confirmed as the actual "app does not start" failure - v53's failure self-heals moments later regardless (the unconditional CREATE TABLE IF NOT EXISTS runs right after migrations either way), and v48's collector writes happen on a fire-and-forget background task that would not surface as a startup-blocking exception. Both are real, independently worth fixing, and match the two errors the user's log actually shows - but the log cuts off right after migrations complete, before whatever actually blocked startup. Commenting on the issue to ask for the rest of the log rather than closing it. Part of #2748. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Reviewed. Note: this PR's diff against its recorded base ( Correctness — looks solid.
Minor, non-blocking: the |
| before that later step ever creates it. CreateDatabaseStateExpectedTable is itself | ||
| idempotent, so calling it here is a no-op for anyone who already has the table (from a | ||
| prior run) and a correct fresh create — new columns included — for anyone who does not. */ | ||
| await ExecuteNonQueryAsync(connection, Schema.CreateDatabaseStateExpectedTable); |
There was a problem hiding this comment.
Good fix — matches v54's create-then-alter pattern and Schema.CreateDatabaseStateExpectedTable already has both new columns, so the two ALTERs below are no-ops on a fresh create.
One thing worth tightening: the catch { } a few lines below (line ~1419, "Table doesn't exist yet — will be created with the full schema below") is now stale, since the table is unconditionally created right here inside the same try. If that catch still fires, it's for some other reason, not a missing table. It's also the only bare, non-logging catch among the migration blocks in this file — every sibling (v47, v48, v49, v54) logs via _logger?.LogWarning. Given this whole PR exists to fix a silently-swallowed migration failure (#2748), adding a log line here would surface a future failure of the same shape instead of repeating it.
There was a problem hiding this comment.
Fixed in 0ebf8e8 — now logs via _logger?.LogWarning, matching the sibling migration blocks.
Review catch on #2750: the CREATE TABLE IF NOT EXISTS just added means "table doesn't exist yet" can no longer be why this catch fires - a catch here now means something else went wrong. Log it like every sibling migration block does, rather than repeat the exact silently-swallowed-failure shape this PR exists to fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Reviewed. Both fixes are correct and well-verified:
One minor (non-blocking) nit: the seeded No blocking issues found. |
Part of #2748.
What's actually wrong (two distinct bugs, both empirically confirmed against real DuckDB 1.5.5)
v48 (server_properties NOT NULL relaxation) fails on any database that has completed a prior startup.
DuckDbSchemaGenerator's default-case index (idx_server_properties_time) already exists by then, and DuckDB'sALTER COLUMNdependency check refuses a table with any index on it — even one naming none of the altered columns. I initially guessed this was the archive view (v_server_properties) and was wrong; a standalone repro against the pinned DuckDB.NET 1.5.5 showed a plainSELECT *view does not trigger the dependency error, only the index does. Fix: dropidx_server_properties_timebefore the threeALTER COLUMNstatements; the schema loop's unconditional index-creation step (already runs right after migrations, every startup) recreates it.v53 (
config_database_state_expectednew columns) fails on any database old enough to predate the table's introduction (#2166/#2203). That table has no numberedCREATEmigration of its own — it only exists viaSchema.GetAllTableStatements()'s unconditionalCREATE TABLE IF NOT EXISTS, which doesn't run until afterRunMigrationsAsyncreturns. v54's migration already does this correctly (create-then-alter, idempotent); v53 never did. Fix: call the same idempotentCreateDatabaseStateExpectedTableDDL before the twoALTERstatements, matching v54's established pattern.Both reproduce the exact logged error text from the issue, character for character (confirmed via a standalone DuckDB.NET 1.5.5 script, not just by reading the code).
What this PR does not confirm
Neither bug is confirmed as the actual "app does not start" symptom:
CREATE TABLE IF NOT EXISTSruns right after migrations either way, so the table ends up correctly shaped even without this fix, for this specific user's case (table didn't exist at all)._ = Task.Run(() => _backgroundService.StartAsync(...))inMainWindow_Loaded) — aNOT NULLviolation there would not surface as a startup-blocking exception visible to the user.Both are real, independently worth fixing, and match the two errors the user's log actually shows — but the pasted log cuts off right after migrations complete ("Analysis schema initialized at version 5"), before whatever actually blocked startup. I'm commenting on the issue to ask for the rest of the log rather than claiming this closes it.
Testing
Lite.Tests/AgedDatabaseMigrationTests.cs: seeds both real preconditions (an aged NOT NULLserver_properties+ its real default index; a v47 database missingconfig_database_state_expectedentirely) against the actualDuckDbInitializer.InitializeAsyncpath, and asserts the upgrade both completes AND leaves the database in a genuinely correct, writable state — not just that it didn't throw.