sqlite

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package sqlite is the SQLite migration dialect: it reads a live database into a schema.Schema through the SQLite catalog (sqlite_schema and the table-valued pragmas), renders a change plan as SQLite DDL, and rebuilds a table with the twelve-step procedure of https://www.sqlite.org/lang_altertable.html#otheralter when ALTER TABLE cannot make the change.

The package has no cgo dependency of its own: it works on any momo.PinnedConn, and only the tests that open a real database need driver/sqlitedriver.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ForceUnlock

func ForceUnlock(ctx context.Context, c momo.PinnedConn) (previousHolder string, err error)

ForceUnlock removes the lock row whoever holds it and returns the holder text it removed ("" when nobody held the lock). It is for a lock left behind by a process that died; calling it while a migration runs lets a second process start. A database without the lock table has nothing to remove.

func Rebuild

func Rebuild(ctx context.Context, c momo.PinnedConn, table string, desired schema.Table, desiredIndexes []schema.Index) ([]migrate.Statement, error)

Rebuild writes the statements that rebuild table to the desired definition on the live database c: it captures the views and triggers that mention the table, reads the table's current columns, and calls RebuildStatements. Rows are copied for the columns that exist in both.

Precondition: the caller ran Setup(ctx, c, SessionOptions{ForeignKeysOff: true}) outside any transaction, and runs the result with migrate.RunStatements (one transaction; the fk-check statement fails it on a violation).

func RebuildStatements

func RebuildStatements(table string, oldColumns []string, desired schema.Table, desiredIndexes []schema.Index, deps []Dependent) ([]migrate.Statement, error)

RebuildStatements is Rebuild without I/O. The statements, in order:

  1. DROP VIEW / DROP TRIGGER IF EXISTS for each dependent, in reverse order.
  2. CREATE TABLE "new_<table>" from desired.
  3. INSERT INTO "new_<table>" (common) SELECT common FROM "<table>", where common is the desired columns (generated ones excepted) that are in oldColumns, in desired order; omitted when no column is common.
  4. DROP TABLE "<table>".
  5. ALTER TABLE "new_<table>" RENAME TO "<table>".
  6. CREATE [UNIQUE] INDEX for each of desiredIndexes.
  7. Each dependent's stored SQL, in the order given, hinted "dependent:<type>:<name>" so that a failure wraps migrate.ErrRebuildBlocked and names the object; after a view, a SELECT that forces SQLite to resolve the view's names (CREATE VIEW does not), with the same hint.
  8. PRAGMA foreign_key_check, hinted "fk-check".

It returns an error when an enum column's values are needed: desired is written without the schema's enums, so give it TEXT columns with a Checks entry instead, or render through RenderSchema.

Types

type Dependent

type Dependent struct {
	Type  string // "view" or "trigger"
	Name  string
	Table string // the trigger's table (sqlite_schema.tbl_name); the view's own name for a view
	SQL   string // the stored CREATE statement
}

Dependent is a view or trigger that a table rebuild must drop and recreate.

func Capture

func Capture(ctx context.Context, c momo.PinnedConn, table string) ([]Dependent, error)

Capture reads, in rowid order, every view and every trigger whose SQL mentions table (a whole-word, case-insensitive match that ignores string literals), plus every trigger on table, plus every view or trigger that mentions a view already captured: SQLite fails ALTER TABLE ... RENAME while any view names a missing object, so a view that sits on top of a captured view has to go too. Over-inclusion is safe: recreating an unaffected object verbatim does not change behaviour.

type Dialect

type Dialect struct{}

Dialect is the SQLite implementation of migrate.Dialect. It holds no state; every method works on the pinned connection it is given.

func New

func New() Dialect

New returns the SQLite dialect.

func (Dialect) Dialect

func (Dialect) Dialect() *dialect.Dialect

Dialect describes SQLite: identifier quoting, placeholders and capabilities.

func (Dialect) HistoryDDL

func (Dialect) HistoryDDL() []string

HistoryDDL returns the momo_migrations DDL. SQLite stores the timestamps as RFC 3339 text, the same as the history writer sends them.

func (Dialect) Inspect

Inspect reads the live database into a schema.Schema: tables with their columns, primary key, unique constraints, indexes, foreign keys and checks, views, and triggers. opts is not used: SQLite has one namespace per database file (the engine applies Config.Exclude itself), and the bookkeeping tables momo_migrations and momo_migration_lock, the sqlite_ internal tables, virtual tables and the shadow tables of virtual tables are left out.

Objects that SQLite cannot hold (extensions, native enums, functions, policies, partitions, row-level security, schemas) are never reported. A TEXT column whose table carries CHECK ("col" IN ('a', 'b')) is reported as an enum column: Column.Enum is "<table>_<column>" and Schema.Enums lists the values, which is how Render writes such a column back.

func (Dialect) Lock

func (Dialect) Lock(ctx context.Context, c momo.PinnedConn, _ string) (unlock func() error, holder func(context.Context) (string, error), err error)

Lock takes the migration lock: one row (id = 1) in the table momo_migration_lock, which Lock creates on first use (an existing table costs no DDL). SQLite has no advisory locks, and a lock held by a transaction would not survive the autocommit statements of a migration that opts out of one, so the lock is a row that outlives statements and is removed by unlock.

The key is not used: a SQLite database file has one migration history, so it has one lock. When another holder has the row, Lock returns migrate.ErrLocked with the holder named ("pid=N host=H at=T") and a holder function that reads it again. Otherwise it returns unlock, which deletes the row only while it still carries this holder text, so a lock released with ForceUnlock and taken by another process is not removed. unlock ignores cancellation of ctx.

A process that dies while it holds the lock leaves the row behind. After confirming that no process is migrating, remove it with ForceUnlock.

func (Dialect) NeedsRebuild

func (Dialect) NeedsRebuild(from *schema.Schema, c migrate.Change) bool

NeedsRebuild reports whether applying c to the schema from takes a table rebuild. AutoMigrate uses it to skip such a change, because the rebuild writes the whole desired table and would also apply what AutoMigrate skipped.

func (Dialect) Render

func (Dialect) Render(plan []migrate.Change) ([]migrate.Statement, error)

Render turns an ordered plan into SQLite statements. The plan must already be dependency-ordered by migrate.OrderErr or migrate.OrderWithViews; Render never reorders.

A change SQLite's ALTER TABLE can make (rename_table, rename_column, create_index, drop_index, create_view, drop_view, create_trigger, drop_trigger, and add_column when it meets the restrictions of https://www.sqlite.org/lang_altertable.html) becomes one native statement. A change it cannot make (alter_column, the primary key, unique, check and foreign key changes, drop_column, and an add_column that breaks a restriction) needs the rebuild of its table: Render emits one marker statement per table, with Hint "rebuild" and no SQL, at the position of the table's first such change, and folds every other change to that table into it. A marker is not executable; RenderSchema, which Engine.Diff and AutoMigrate call, writes the rebuild out in full, and Rebuild writes it from a live connection.

Render returns an error wrapping momo.ErrUnsupported for the kinds SQLite has no equivalent of: create_schema, create_extension, create_enum, alter_enum_add_value, drop_enum, the policy and row-level security kinds and the function kinds. Nothing is dropped silently.

func (Dialect) RenderSchema

func (Dialect) RenderSchema(from, to *schema.Schema, plan []migrate.Change) ([]migrate.Statement, error)

RenderSchema is Render with the schema the plan starts from and the one it ends at. With them it writes each table rebuild as the plain statements of the twelve-step procedure (so a migration file can hold them), decides drop_column by what the table text really uses, turns create_enum and drop_enum into nothing (an enum is only the CHECK on its TEXT columns, written with the column) and rebuilds the tables that use an enum when alter_enum_add_value widens it.

func (Dialect) Setup

func (Dialect) Setup(ctx context.Context, c momo.PinnedConn, s migrate.SessionOptions) (reset func() error, err error)

Setup prepares the session for a migration. When s.ForeignKeysOff is set it turns foreign-key enforcement off with PRAGMA foreign_keys = OFF, which a table rebuild needs (step 1 of the procedure in the SQLite documentation: DROP TABLE would otherwise delete or cascade through the rows of the child tables). The PRAGMA is a no-op inside a transaction, so Setup must run outside one; it reads the setting back and fails when it did not change. The returned reset restores the value Setup found. It is idempotent and ignores cancellation of ctx; when the restore fails it discards the connection (momo.Discarder) so that a session with enforcement off cannot return to the pool.

The lock and statement timeouts of SessionOptions have no SQLite counterpart here: busy handling belongs to the driver (busy_timeout), so they are ignored.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL