spacr.database_schema

Versioned SQLite schema migrations for spaCR measurement databases.

SQLite’s PRAGMA user_version is the on-disk schema version. Migrations are registered here as a contiguous, ordered sequence and are applied in one transaction. A database created by an older spaCR release therefore follows the same path whether it is opened for reading or for writing, while a database created by a newer release is rejected before any mutation.

The module deliberately uses only the Python standard library and spacr.schema, which is itself standard-library-only at module scope. Measurement workers can import it without importing pandas, plotting, or optional analysis dependencies.

The canonical column-name vocabulary lives in spacr.schema and is re-exported here rather than redefined; see the comment above DB_COLUMN_RENAMES for what that repaired.

Exceptions

DatabaseMigrationError

A measurements database could not be migrated safely.

DatabaseSchemaTooNewError

The database was written by a newer, unsupported spaCR schema.

Classes

Migration

One ordered database schema transition.

MigrationReport

Result of bringing one database to a requested schema version.

Functions

database_schema_version(→ int)

Return source's SQLite user_version.

ensure_database_schema(→ MigrationReport)

Migrate a database and repair schema drift at the current version.

migrate_connection(→ MigrationReport)

Migrate an open SQLite connection atomically.

migrate_database(→ MigrationReport)

Migrate an existing SQLite database path and close it on every path.

repair_legacy_columns(db_path, *[, timeout])

Re-run the non-destructive column repair without changing the version.

Module Contents

exception spacr.database_schema.DatabaseMigrationError[source]

Bases: RuntimeError

A measurements database could not be migrated safely.

Initialize self. See help(type(self)) for accurate signature.

exception spacr.database_schema.DatabaseSchemaTooNewError[source]

Bases: DatabaseMigrationError

The database was written by a newer, unsupported spaCR schema.

Initialize self. See help(type(self)) for accurate signature.

class spacr.database_schema.Migration[source]

One ordered database schema transition.

version is the schema version after apply succeeds. Consequently a migration numbered 3 upgrades version 2 to version 3.

Parameters:
  • version – schema version reached by this transition. It determines registry order and selection and becomes SQLite user_version after successful application.

  • name – human-readable transition label appended to MigrationReport.applied when the migration runs.

  • apply – callable invoked with the open SQLite connection inside the migration transaction. It mutates the schema and returns (table, old, new) column-renaming records; an exception rolls the transition back.

class spacr.database_schema.MigrationReport[source]

Result of bringing one database to a requested schema version.

Parameters:
  • path – database path label copied into the report, or None for an unnamed connection.

  • from_version – schema version observed before migration.

  • to_version – schema version reached after successful migration.

  • applied – ordered names of migrations that ran.

  • column_renames – (table, old, new) column repairs performed by the migrations.

property changed: bool[source]

a version transition or a rename.

A column repair at an unchanged version, as ensure_database_schema() performs, also counts, so this can be True while applied is empty.

Type:

Whether the database changed

spacr.database_schema.database_schema_version(source) → int[source]

Return source’s SQLite user_version.

Parameters:

source – open SQLite connection or path to an existing database.

source may be an open sqlite3.Connection or a path. A path must already exist; inspecting a typo must not create an empty database.

spacr.database_schema.ensure_database_schema(db_path, *, target_version: int = CURRENT_SCHEMA_VERSION, timeout: float = 30.0) → MigrationReport[source]

Migrate a database and repair schema drift at the current version.

Parameters:

db_path – database file to migrate after expanding user-relative path syntax.

Old spaCR readers performed the non-destructive column repair on every open. Retaining that small safety net matters for databases manually edited after migration, while ordinary legacy databases still follow the explicit one-time migration path.

spacr.database_schema.migrate_connection(connection: sqlite3.Connection, *, target_version: int = CURRENT_SCHEMA_VERSION, migrations: Sequence[Migration] = MIGRATIONS, path: str | None = None) → MigrationReport[source]

Migrate an open SQLite connection atomically.

A schema newer than this spaCR installation is rejected with an actionable error. Every selected migration and the final user_version update share one transaction, so an exception leaves both schema and version unchanged.

Parameters:
  • connection – migrated in place. If it is already inside a transaction the work nests in a SAVEPOINT, so nothing is durable until the caller commits and an outer rollback discards the whole migration. A read-only connection is only safe when nothing needs applying; otherwise SQLite raises OperationalError.

  • target_version – version to stop at. Below the database’s current version is a downgrade and is refused; above CURRENT_SCHEMA_VERSION is refused as well. Equal to the current version returns an empty report and writes nothing at all – not even application_id, which is stamped only when a migration actually runs.

  • migrations – registry, sorted by version before use, so declaration order does not matter. Versions 1 through target_version must all be present and contiguous; entries numbered above it are accepted and never applied.

  • path – a label, not an input. It is never opened and never checked against connection; it only names the database in the too-new error message (None makes that message say database) and is copied verbatim into the report.

Raises:
spacr.database_schema.migrate_database(db_path, *, target_version: int = CURRENT_SCHEMA_VERSION, migrations: Sequence[Migration] = MIGRATIONS, timeout: float = 30.0) → MigrationReport[source]

Migrate an existing SQLite database path and close it on every path.

Parameters:
  • db_path – an existing database file. It is made absolute but not tilde-expanded, so ~/x.db is resolved under the working directory and raises FileNotFoundError even when the home-relative file exists. A missing file raises the same and no database is created. The returned report carries the absolute path, not the string given.

  • target_version – forwarded to migrate_connection(), with the same downgrade and upper-bound rules.

  • migrations – forwarded to migrate_connection(), sorted by version and required to be contiguous from 1 through target_version.

  • timeout – seconds SQLite waits for a lock, and only that; it does not bound the migration itself. A negative value is silently clamped to 0, which makes locking non-blocking, while a non-numeric value raises ValueError.

Raises:

FileNotFoundError – db_path is not an existing file.

spacr.database_schema.repair_legacy_columns(db_path, *, timeout: float = 30.0)[source]

Re-run the non-destructive column repair without changing the version.

Parameters:

db_path – database file whose legacy column aliases are repaired.

This compatibility operation remains useful for a manually edited database that already declares the current version. Normal opens should use migrate_database(), which runs each migration only once.