Repository navigation
Description
Summary
If SCHEMA_INFO holds a version string the server doesn't know, such as one written by a newer or different release, ServerMigrator treats the database as schema version 0 and starts running the legacy pre-2.0 migrations against it. Startup then fails with an error about a 2.x-era delta script, which says nothing about the real problem.
Steps to reproduce
With the default Derby database, from the server's install directory:
- Start a fresh install once so the database is created, then stop it (Ctrl+C once startup has finished):
./oieserver
- With the server stopped, set a version this release doesn't know:
java -cp 'server-lib/database/*' org.apache.derby.tools.ij <<'EOF' connect 'jdbc:derby:appdata/mirthdb'; UPDATE SCHEMA_INFO SET VERSION = '99.0.0'; SELECT * FROM SCHEMA_INFO; exit; EOF
- Start the server again:
./oieserver
On other databases, run the same UPDATE with that database's client.
After the failed start, the process keeps running instead of exiting, which is a separate bug. Stop it with Ctrl+C. On Derby that also hangs, so you'll need kill -9.
Expected
Startup stops with an error naming the unrecognized version, and no migrations run.
Actual
ServerMigrator.migrate() starts the migration chain at V0 and runs LegacyMigrator(1), which executes the first legacy delta script (derby-0-1.sql, postgres-0-1.sql and so on). Startup fails with Failed to execute script for that file.
On a stock schema the script fails at its first statement, DROP TABLE TRANSPORT, because that table no longer exists, so nothing is changed. Nothing guards the path, though: the scripts run with auto-commit on, so if a TRANSPORT table did exist, it would be dropped and the next statements would alter existing tables (CHANNEL, or EVENT on MySQL) before anything failed.
Cause
ServerMigrator.getCurrentVersion() returns Version.fromString() of the stored value, which is null for any string not in the Version enum. migrate() uses null to mean an empty SCHEMA_INFO table and starts from Version.values()[0]. updateVersion() uses the same null to choose INSERT over UPDATE, so a migration step that did succeed would add a second row to SCHEMA_INFO instead of updating the first.
Fix
getCurrentVersion() throws a MigrationException when SCHEMA_INFO has a row whose version isn't recognized, naming the version found. An empty table still starts at V0, as before. Startup already stops on that exception before any other migration runs.
Related
- Server process keeps running after a startup failure #498
- Stopping the server after a failed startup hangs on Derby #499
🤖 Drafted with Claude Code
Activity
Refuse to migrate a database with an unrecognized schema version
Refuse to migrate a database with an unrecognized schema version
Refuse to migrate a database with an unrecognized schema version