Schema versioning

Every .issue.db records the schema version it was written at, in SQLite’s PRAGMA user_version. issuedb applies pending schema changes on open, in ascending order, and refuses to open a database written by a newer issuedb than the installed one.

Why it matters

A .issue.db is a single file, and in practice that file gets shared: it is committed to git in many projects, copied between machines, and opened by a CLI invocation while the web UI is running. Without a recorded version there is no way for issuedb to tell a database it fully understands from one written by a later release whose schema it does not.

Checking the version

from issuedb.database import Database

db = Database(".issue.db")
db.schema_version            # what this file records
db.supported_schema_version  # the highest this build understands

From the shell:

sqlite3 .issue.db 'PRAGMA user_version'

Behaviour on open

Older database. Pending migrations are applied in ascending order. Each runs in its own transaction together with the version bump, so a crash leaves the database at a version that is true — never half-applied.

Database predating versioning. Files created before schema versioning existed report version 0 while already having every table. They are stamped to the baseline version. Baseline DDL is not re-run, so existing issues, comments and history are untouched.

Current database. Nothing is applied and the file is not modified.

Newer database. issuedb raises NewerDatabaseError and refuses to continue:

This database is at schema version 4, but this issuedb supports up to 2.
It was written by a newer issuedb. Upgrade issuedb (pip install -U issuedb)
rather than continuing: writing to it with older assumptions can lose data.

This is deliberate. If your team shares a .issue.db through git and one machine has an older install, that machine must upgrade rather than write to a schema it cannot interpret.

Failed migration. The transaction is rolled back and the recorded version stays where it was, so the version never describes a change that did not land. issuedb raises MigrationError naming the migration that failed.

Forward only

There are no down-migrations. A rollback path that is never exercised is a rollback path that does not work, and for a single-file SQLite database, restoring the file from git or a backup is both simpler and more reliable than replaying inverse DDL.

Concurrency

Migrations take SQLite’s write lock up front (BEGIN IMMEDIATE) and re-read the version inside it. When two processes open the same database at once, the loser of the race observes the winner’s work instead of repeating it.