Skip to content

Record each table upgrade and refuse a database a newer schema format upgraded [all-db] - #517

Merged
jeff377 merged 1 commit into
mainfrom
claude/table-upgrade-records
Nov 15, 2026
Merged

jeff377 merged 1 commit into
mainfrom
claude/table-upgrade-records

Conversation

@jeff377

@jeff377 jeff377 commented Nov 15, 2026

Copy link
Copy Markdown
Contributor

Part of #512. This is the first step: per-table upgrade records, and the guard that stops an older tool from undoing a newer tool's upgrade.

What changes

  • st_table_upgrade, one per database, one row per upgraded table:

    • the SchemaFormat and framework version of the tool that ran the upgrade;
    • the status (Upgrading / Succeeded / Failed) and the error;
    • the applied TableSchema as XML, and its SHA-256.

    The table is created on first use, from a schema built in code, through each dialect's create builder. It is not declared in any definition.

  • TableUpgradeOrchestrator.Execute does three things, in order:

    1. Refuses the upgrade, with nothing changed, when the database's highest recorded format is above SchemaFormat.Current.
    2. Marks the table Upgrading before any statement runs, so an older tool is refused even after a failed or half-finished upgrade.
    3. Records the outcome. A failure keeps the schema of the last successful upgrade.
  • What creates no record:

    • an empty plan;
    • TableSchemaBuilder.GetCommandText;
    • a dry run.
  • DatabaseInitializer.UpgradeTables checks each database once. A database a newer format upgraded becomes a single problem in the report, and none of its tables is touched (dry run included).

  • SchemaFormat.Current starts at 1.

Compatibility

Public API changes are additions only:

  • SchemaFormat
  • TableUpgradeRecordStore
  • TableUpgradeRecord
  • TableUpgradeStatus
  • UpgradePlan.DefineTable, as an init property

Existing callers stay binary compatible. A plan built by hand without DefineTable runs without a record, but the format guard still applies.

Known limits (left to later phases of #512)

  • A table that is already up to date gets no record until something changes it. The fast compare in phase 2 needs a record for such tables, so it will write one after a full compare finds no difference.
  • Two processes upgrading the same table at the same moment can leave a misleading record: the losing process's Failed can overwrite the winner's Succeeded. The data is unaffected.
  • The format check and the record write are not atomic, so an old and a new tool racing can both pass the check once. Later runs are still blocked by the other rows.

Verification

  • dotnet build Polhem.slnx -c Release: 0 warnings.
  • ./test.sh locally with all five databases, everything passing except Generate_iOS_Builds, which fails because the local Xcode does not match the iOS workload. That test is unrelated to this change.
  • New tests:
    • TableUpgradeRecordDbTests covers success, failure and the newer-format refusal on each database, plus an empty plan and a plan without its definition.
    • Two DatabaseInitializerSqliteTests cover records after a build, and one report per newer-format database.
  • An adversarial review of the diff before opening this PR led to these fixes:
    • a record write that fails after the table changed no longer reports the upgrade as failed;
    • the update and the insert use separate parameters;
    • the table_name column is widened to 128;
    • framework versions are compared as versions, not as text.

… upgraded [all-db]

Every upgrade TableUpgradeOrchestrator.Execute runs now writes a row per table to st_table_upgrade in the
database it changes, before any statement runs, then records the outcome: the applied schema and its
SHA-256 on success, the error on failure. The highest format version recorded in the database is checked
first; a database a tool of a higher SchemaFormat upgraded is refused with nothing changed, so an older
tool cannot change the newer shape back. DatabaseInitializer reports such a database once and skips its
tables.

PublicAPI: additions only (SchemaFormat, TableUpgradeRecordStore, TableUpgradeRecord, TableUpgradeStatus,
UpgradePlan.DefineTable as an init property), so existing callers stay binary compatible.

Part of #512
@jeff377 jeff377 added the enhancement New feature or request label Nov 15, 2026
@jeff377
jeff377 enabled auto-merge November 15, 2026 07:41
@jeff377 jeff377 added the area: database Polhem.Db, database providers, repositories label Nov 15, 2026
@sonarqubecloud

Copy link
Copy Markdown

@jeff377
jeff377 added this pull request to the merge queue Nov 15, 2026
Merged via the queue into main with commit 9d1872b Nov 15, 2026
5 checks passed
@jeff377
jeff377 deleted the claude/table-upgrade-records branch November 15, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: database Polhem.Db, database providers, repositories enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant