Documentation ¶
Overview ¶
Package squibble provides a schema migration assistant for SQLite databases.
Overview ¶
A Schema value manages the schema of a SQLite database that will be modified over time. The current database schema is stored in the Current field, and migrations from previous versions are captured as UpdateRules.
When the program starts up, it should pass the open database to the Apply method of the Schema. This verifies that the Schema is valid, then checks whether the database is up-to-date. If not, it applies any relevant update rules to bring it to the current state. If Apply fails, the database is rolled back.
The Schema tracks schema versions by hashing the schema with SHA256, and it stores a record of upgrades in a _schema_history table that it maintains. Apply creates this table if it does not already exist, and updates it as update rules are applied.
Update Rules ¶
The Updates field of the Schema must contain an ordered list of update rules for all the versions of the schema prior to the Current one, from oldest to newest. Each rule has the hash of a previous schema version and a function that can be applied to the database to upgrade it to the next version in sequence.
When revising the schema, you must add a new rule mapping the old (existing) schema to the new one. These rules are intended to be a permanent record of changes, and should be committed into source control as part of the program. As a consistency check, each rule must also declare the hash of the target schema it upgrades to.
When Apply runs, it looks for the most recent version of the schema recorded in the _schema_history table. If there is none, and the database is otherwise empty, the current schema is assumed to be the initial version, and it is applied directly. Otherwise, Apply compares the hash of the most recent update to the current version: If they differ, it finds the most recent update hash in the Updates list, and applies all the updates from that point forward. If this succeeds, the current schema is recorded as the latest version in _schema_history.
Validation ¶
You use the Validate function to check that the current schema in the special sqlite_schema table maintained by SQLite matches a schema written as SQL text. If not, it reports a diff describing the differences between what the text wants and what the real schema has.
Limitations ¶
Currently this package only handles the main database, not attachments.
Index ¶
- func DBDigest(ctx context.Context, db DBConn) (string, error)
- func Exec(stmts ...string) func(context.Context, DBConn) error
- func Logf(ctx context.Context, msg string, args ...any)
- func NoAction(context.Context, DBConn) error
- func SQLDigest(text string) (string, error)
- func Validate(ctx context.Context, db DBConn, schema string) error
- type DBConn
- type HistoryRow
- type Schema
- type UpdateRule
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DBDigest ¶
DBDigest computes a hex-encoded SHA256 digest of the SQLite schema encoded in the specified database.
func Exec ¶
Exec returns an UpdateRule apply function that executes the specified statements sequentially.
func Logf ¶
Logf sends a log message to the logger attached to ctx, or to log.Printf if ctx does not have a logger attached. The context passed to the apply function of an UpdateRule will have this set to the logger for the Schema.
func SQLDigest ¶
SQLDigest computes a hex-encoded SHA256 digest of the SQLite schema encoded by the specified string.
Types ¶
type DBConn ¶
type DBConn interface { QueryContext(context.Context, string, ...any) (*sql.Rows, error) ExecContext(context.Context, string, ...any) (sql.Result, error) }
DBConn is the subset of the sql.DB interface needed by the functions defined in this package.
type HistoryRow ¶
type HistoryRow struct { Timestamp time.Time `json:"timestamp"` // In UTC Digest string `json:"digest"` // The digest of the schema at this update Schema string `json:"sql,omitempty"` // The SQL of the schema at this update }
HistoryRow is a row in the schema history maintained by the Schema type.
type Schema ¶
type Schema struct { // Current is the SQL definition of the most current version of the schema. // It must not be empty. Current string // Updates is a sequence of schema update rules. The slice must contain an // entry for each schema version prior to the newest. Updates []UpdateRule // Logf is where logs should be sent; the default is log.Printf. Logf func(string, ...any) }
Schema defines a family of SQLite schema versions over time, expressed as a SQL definition of the current version of the schema, plus an ordered collection of upgrade rules that define how to update each version to the next.
func (*Schema) Apply ¶
Apply applies any pending schema migrations to the given database. It reports an error immediately if s is not consistent (per Check); otherwise it creates a new transaction and attempts to apply all applicable upgrades to db within it. If this succeeds and the transaction commits successfully, then Apply succeeds. Otherwise, the transaction is rolled back and Apply reports the reason wny.
When applying a schema to an existing unmanaged database, Apply reports an error if the current schema is not compatible with the existing schema; otherwise it applies the current schema and updates the history.
func (*Schema) Check ¶
Check reports an error if there are consistency problems with the schema definition that prevent it from being applied.
A Schema is consistent if it has a non-empty Current schema text, all the update rules are correctly stitched (prev.Target == next.Source), and the last update rule in the sequence has the current schema as its target.
type UpdateRule ¶
type UpdateRule struct { // Source is the hex-encoded SHA256 digest of the schema at which this // update applies. It must not be empty. Source string // Target is the hex-encoded SHA256 digest of the schema reached by applying // this update. It must not be empty. Target string // Apply applies the necessary changes to update the schema to the next // version in sequence. It must not be nil. // // An apply function can use squibble.Logf(ctx, ...) to write log messages // to the logger defined by the associated Schema. Apply func(ctx context.Context, db DBConn) error }
An UpdateRule defines a schema upgrade.
type ValidationError ¶
type ValidationError struct { // Diff is a human readable summary of the difference between what was in // the database (-lhs) and the expected schema (+rhs). Diff string }
ValidationError is the concrete type of errors reported by the Validate function.
func (ValidationError) Error ¶
func (v ValidationError) Error() string