moonbitstack/moonorm/migrate does not have a README file

    Change

    pub(all) struct Change {
    up : Op
    down : Op
    }

    One change and the change that undoes it, both derived from the same comparison — so the downgrade of an autogenerated revision is written for you and cannot drift from the upgrade.

    ColumnDiff

    pub(all) enum ColumnDiff {
    Added(name~ : String, col_type~ :
    ColumnType
    )
    Removed(String)
    TypeChanged(name~ : String, from~ :
    ColumnType
    , to~ :
    ColumnType
    )
    } derive(Eq)

    One difference between a declared schema and a reflected one (Alembic-autogenerate style): a column Added in the model but absent in the database, one Removed from the model but still present, or one whose TypeChanged. Compared by column name; ordering follows the declared columns, then the leftover reflected ones.

    ColumnDiff::equal

    fn ColumnDiff::equal(ColumnDiff, ColumnDiff) -> Bool

    ColumnDiff::not_equal

    fn ColumnDiff::not_equal(x : ColumnDiff, y : ColumnDiff) -> Bool

    HistoryEntry

    pub(all) struct HistoryEntry {
    id : String
    parents : Array[String]
    name : String
    applied : Bool
    }

    One line of a migration history: the revision, its parents, its message, and whether the database has it.

    Migration

    pub(all) struct Migration {
    version : Int
    name : String
    up : Array[String]
    down : Array[String]
    }

    One schema change: a monotonically increasing version, a human name, and the ordered SQL statements that apply it (up) and undo it (down). Each statement runs through Session::execute, so a backend that prepares one statement per call (SQLite) still applies a multi-statement migration.

    Migrator

    pub struct Migrator {
    table : String
    revisions_table : String
    }

    Tracks and applies migrations against a bookkeeping table (its name is configurable for coexistence with other tools). Numeric migrations record their version in table; revision migrations record their ids in revisions_table. Build one with Migrator::new.

    Migrator::applied_revisions

    The revisions this database has applied, in the order the graph puts them.

    Migrator::applied_versions

    Every applied version, ascending.

    Migrator::current_version

    The highest applied version, or 0 when nothing has been applied yet.

    Migrator::down_to

    fn Migrator::down_to(self : Migrator, sess :
    Session
    , migrations : Array[Migration], target : Int) -> Int raise
    DbError

    Roll back every applied migration whose version is greater than target, in descending version order, running each one's down statements and removing its bookkeeping row. Returns how many were rolled back. down_to(0) unwinds everything.

    Migrator::downgrade

    fn Migrator::downgrade(self : Migrator, sess :
    Session
    , revisions : Array[Revision], target? : String) -> Array[String] raise
    DbError

    Undo every applied revision above target, descendants first, and return the ids undone. downgrade(target="base") unwinds the whole history.

    Migrator::downgrade_sql

    fn Migrator::downgrade_sql(self : Migrator, revisions : Array[Revision], applied : Array[String], target? : String, dialect? :
    Dialect
    ) -> Array[Step] raise
    DbError

    The statements of a downgrade from applied to target, descendants first. Each step runs the revision's down operations and then forgets the revision.

    Migrator::ensure_revisions_table

    Create the revisions table if it is absent. Idempotent, like the older schema_migrations table, so it is safe to call before every command.

    The width is explicit because MySQL cannot index a bare TEXT column and the primary key is what makes a re-applied revision a conflict rather than a duplicate.

    Migrator::ensure_table

    Create the bookkeeping table if it is absent. Idempotent, so it is safe to call before every up/down.

    Migrator::heads

    Where the database is: the applied revisions nothing else applied descends from, in graph order. More than one means the database sits on several branch tips.

    Migrator::history

    The whole history, newest first, each entry marked with whether this database has it — Alembic's history and current in one call.

    Migrator::new

    fn Migrator::new(table? : String, revisions_table? : String) -> Migrator raise
    DbError

    A migrator recording applied versions in table (default schema_migrations) and applied revisions in revisions_table (default moonorm_revisions). Both names must be bare SQL identifiers — they are interpolated as identifiers, never bound — so a non-identifier is refused up front.

    Migrator::stamp

    fn Migrator::stamp(self : Migrator, sess :
    Session
    , revisions : Array[Revision], target? : String) -> Unit raise
    DbError

    Record the database as being at target without running any operation — Alembic's stamp, which is how an existing database joins a revision history.

    Revisions the target implies are inserted, ones it does not are removed, and no schema operation runs. All of it is one transaction, so a stamp never half-applies.

    Migrator::up

    Apply every pending migration (those whose version is not yet recorded), in ascending version order, running each one's up statements and recording it. Returns how many were applied. Already-applied versions are skipped, so this is safe to run repeatedly (it converges the schema to the latest version).

    Migrator::upgrade

    fn Migrator::upgrade(self : Migrator, sess :
    Session
    , revisions : Array[Revision], target? : String) -> Array[String] raise
    DbError

    Apply every pending revision up to target and return the ids applied, in order. Each revision and the row recording it run in one transaction, so an interrupted upgrade resumes at the revision it did not finish.

    target accepts "head" (the single tip), "heads" (every tip), "base" (do nothing) or a revision id.

    Migrator::upgrade_sql

    fn Migrator::upgrade_sql(self : Migrator, revisions : Array[Revision], applied : Array[String], target? : String, dialect? :
    Dialect
    ) -> Array[Step] raise
    DbError

    The statements of an upgrade from applied to target, one step per revision, in the order to run them. Nothing touches a database, which is what makes this the offline mode: print the steps and run them elsewhere.
    pub(all) enum Op {
    CreateTable(name~ : String, columns~ : Array[
    Column
    ], if_not_exists~ : Bool)
    DropTable(name~ : String, if_exists~ : Bool)
    AddColumn(table~ : String, column~ :
    Column
    )
    DropColumn(table~ : String, name~ : String)
    RenameTable(from~ : String, to~ : String)
    RenameColumn(table~ : String, from~ : String, to~ : String)
    AlterColumn(table~ : String, name~ : String, new_name~ : String?, col_type~ :
    ColumnType
    ?, nullable~ : Bool?, default~ : String?, drop_default~ : Bool)
    AddForeignKey(table~ : String, name~ : String, columns~ : Array[String], ref_table~ : String, ref_columns~ : Array[String], on_delete~ : String?, on_update~ : String?)
    DropForeignKey(table~ : String, name~ : String)
    CreateIndex(name~ : String, table~ : String, columns~ : Array[String], unique~ : Bool, if_not_exists~ : Bool)
    DropIndex(name~ : String, table~ : String?, if_exists~ : Bool)
    RebuildTable(name~ : String, columns~ : Array[
    Column
    ], copy~ : Array[String])
    Execute(sql~ : String)
    }

    One schema change. AlterColumn carries only what changes, so a rename is a rename rather than a full column redefinition; RebuildTable is SQLite's batch alter, where the column set is supplied whole because the engine cannot alter a column in place.

    Op::table

    fn Op::table(self : Op) -> String

    The name of the table an operation touches, for ordering and reporting.

    Op::to_sql

    The statements this operation is, rendered for dialect. Most are one statement; AlterColumn is several when more than one property changes, and RebuildTable is the four-step swap SQLite needs.

    Raises rather than emitting something the engine would reject: SQLite cannot add or drop a foreign key or change a column's type in place, and rebuild_table is the wrong tool for PostgreSQL and MySQL, which alter in place. The error names the engine and points at the operation that does work.

    ReflectedIndex

    pub(all) struct ReflectedIndex {
    table : String
    name : String
    columns : Array[String]
    unique : Bool
    }

    One index read back from a database: the table it belongs to, its name, its columns in order, and whether it is unique. Reflection groups it into a TableSpec index.

    Revision

    pub(all) struct Revision {
    id : String
    parents : Array[String]
    name : String
    up : Array[Op]
    down : Array[Op]
    }

    One revision: an identifier, the revisions it follows (parents), a message, and the operations that apply and undo it.

    parents is empty for a base revision, holds one id for a linear step, and two or more for a merge. That is the whole difference between a chain and a DAG — the rest of the runner walks whichever shape the parents describe.

    Revision::ancestors

    fn Revision::ancestors(revisions : Array[Revision], id : String) -> Array[String]

    Every revision reachable from id by following parents, id included — the set a database at id must have applied.

    Revision::check

    fn Revision::check(revisions : Array[Revision]) -> Unit raise
    DbError

    Refuse a revision set that cannot be walked: a duplicate id, or a parent that is not in the set. Both are authoring mistakes that would otherwise surface as a silently skipped step — a dangling parent reads as a base revision, so a typo would apply a child before its parent ever existed.

    Revision::doomed

    fn Revision::doomed(revisions : Array[Revision], applied : Array[String], target : String) -> Array[String] raise
    DbError

    The revisions a downgrade from applied to target has to undo, in the order to undo them — descendants first, which is the reverse of the upgrade order.

    Revision::find

    fn Revision::find(revisions : Array[Revision], id : String) -> Revision?

    The revision with this id, or None.

    Revision::history

    fn Revision::history(revisions : Array[Revision], applied : Array[String]) -> Array[HistoryEntry] raise
    DbError

    The migration history, newest first — the order history prints in, so the tip the database should be at is the first line.

    Revision::is_base

    fn Revision::is_base(self : Revision) -> Bool

    Whether this revision applies to an empty database.

    Revision::new

    fn Revision::new(id~ : String, parents? : Array[String], name~ : String, up~ : Array[Op], down~ : Array[Op]) -> Revision

    Declare a revision. A revision with no parents is a base: it applies to an empty database. Two revisions may share a parent — that is a branch — and one revision may name two parents — that is a merge.

    Revision::order

    fn Revision::order(revisions : Array[Revision]) -> Array[String] raise
    DbError

    The revisions in the order an upgrade must apply them: a revision comes after all of its parents, and among the ones that are ready the earliest declared goes first — so the order is a function of the input array, not of a hash or a clock.

    Refuses a set check would refuse, and a cycle, which has no valid order at all.

    Revision::parent

    fn Revision::parent(self : Revision) -> String?

    The revision's parent when it has exactly one, which is the linear case a caller usually wants to follow back.

    Revision::pending

    fn Revision::pending(revisions : Array[Revision], applied : Array[String], target : String) -> Array[String] raise
    DbError

    The revisions an upgrade to target has to apply, given what the database already has, in the order to apply them.

    target is "head" (the single tip; refuses when the graph has several), "heads" (every tip), "base" (nothing), or a revision id. Revisions the database already applied are left out, so a run that was interrupted resumes where it stopped.

    Revision::resolve

    fn Revision::resolve(revisions : Array[Revision], target : String) -> Array[String] raise
    DbError

    Turn a target into the set of revisions a database at that target has applied: a revision id, or "head" / "heads" for the tips, or "base" for none.

    Statement

    pub(all) struct Statement {
    sql : String
    params : Array[
    Value
    ]
    }

    One statement to run, with the values it binds.

    Step

    pub(all) struct Step {
    id : String
    name : String
    statements : Array[Statement]
    }

    One revision's worth of work: the operations rendered, in order, plus the bookkeeping statement. A runner executes every statement of a step in one transaction.

    add_column

    fn add_column(table : String, column :
    Column
    ) -> Op

    Add one column.

    add_foreign_key

    fn add_foreign_key(table : String, name : String, columns : Array[String], ref_table : String, ref_columns : Array[String], on_delete? : String, on_update? : String) -> Op

    Add a foreign-key constraint to an existing table. SQLite cannot add one without rebuilding the table, so it raises there.

    alter_column

    fn alter_column(table : String, name : String, new_name? : String, col_type? :
    ColumnType
    , nullable? : Bool, default? : String, drop_default? : Bool) -> Op

    Change a column: its name, its type, its nullability, or its default. Only the parts given are rendered. SQLite cannot change a type or a nullability in place, so that combination raises and points at rebuild_table.

    autogenerate

    Just the operations an upgrade needs, in order — what a migration body contains.

    autogenerate_changes

    The difference between a declared schema and a reflected one, as up/down pairs.

    The order is the order a migration must run in: create missing tables and their indexes first, then bring existing tables in line column by column, then drop the indexes and tables that are gone. Columns are compared by name; a type compares as dialect spells it, so an Int64 reflected as INTEGER on SQLite is not a change; nullability is compared only off the primary key, which SQLite reports as nullable because INTEGER PRIMARY KEY is the rowid.

    autogenerate_revision

    fn autogenerate_revision(id~ : String, parents? : Array[String], name~ : String, declared : Array[
    TableSpec
    ], reflected : Array[
    TableSpec
    ], dialect? :
    Dialect
    ) -> Revision

    A revision built from the difference, with its up and down both filled in. The downgrade is exact over the reflected state, which is what makes it safe to run.

    create_index

    fn create_index(name : String, table : String, columns : Array[String], unique? : Bool, if_not_exists? : Bool) -> Op

    Create an index.

    create_table

    fn create_table(name : String, columns : Array[
    Column
    ], if_not_exists? : Bool) -> Op

    Create a table from its column list.

    diff_schema

    Diff a model's declared columns against the reflected ones from a live table: declared-only columns are Added, reflected-only are Removed, and a name in both with a different col_type is TypeChanged. An empty result means the table matches the model. This is the write half of autogenerate — feed the diff to DDL to synthesise the migration.

    Scope: reflection recovers only a column's storage type, so declare a model at the same granularity to avoid spurious diffs — a Bool stored as INTEGER reflects as Int, Uuid/Json stored as TEXT reflect as Text. And the diff reports add/remove/type only, not nullable / default / unique / PK / FK changes.

    With dialect the types compare as that engine writes them, so a BigIntType declared against SQLite, where every integer is INTEGER, is no change; without it they compare as declared.

    drop_column

    fn drop_column(table : String, name : String) -> Op

    Drop one column.

    drop_foreign_key

    fn drop_foreign_key(table : String, name : String) -> Op

    Drop a foreign-key constraint by name.

    drop_index

    fn drop_index(name : String, table? : String, if_exists? : Bool) -> Op

    Drop an index. table matters on MySQL, which names an index as table.index.

    drop_table

    fn drop_table(name : String, if_exists? : Bool) -> Op

    Drop a table, optionally tolerating its absence.

    execute

    fn execute(sql : String) -> Op

    Run SQL verbatim. For the changes the operation set does not model — a trigger, an engine-specific clause — and for data migrations.

    index_ddl

    fn index_ddl(name : String, table : String, columns : Array[String], unique? : Bool) -> String

    Render a CREATE INDEX statement (SQLAlchemy's Index / index=True). With unique=true it is a CREATE UNIQUE INDEX; multiple columns make a composite index. Names are trusted identifiers, never bound. Pair it with a Migration's up, and a DROP INDEX in its down.

    indexes_of_rows

    Group index rows — (table, index, unique, column), one row per indexed column, in column order — into indexes. The shape is the one postgres_indexes_query and mysql_indexes_query return, so one parser serves both; SQLite builds the same rows from its two pragmas.

    mysql_columns_query

    fn mysql_columns_query(table : String) -> String raise
    DbError

    The columns of one MySQL table (SHOW COLUMNS): Field, Type, Null, Key, Default,Extra.

    mysql_indexes_query

    fn mysql_indexes_query() -> String

    Every index in the current MySQL database, one row per indexed column: TABLE_NAME, INDEX_NAME, is_unique, COLUMN_NAME. NON_UNIQUE is inverted in the query so all three backends report the same thing in the same column.

    mysql_tables_query

    fn mysql_tables_query() -> String

    The base tables in the current MySQL database.

    postgres_columns_query

    fn postgres_columns_query() -> String

    The columns of one PostgreSQL table, in declaration order. The table arrives as the statement's only bound parameter, so this is the text alone.

    postgres_indexes_query

    fn postgres_indexes_query() -> String

    Every index in the current PostgreSQL schema that was created as an index, one row per indexed column: tablename, indexname, is_unique, attname.

    PostgreSQL backs a PRIMARY KEY or UNIQUE constraint with an index, and those are not indexes the schema declares — reporting them would make every key look like a stray index autogenerate wants to drop.

    postgres_tables_query

    fn postgres_tables_query() -> String

    The base tables in the current PostgreSQL schema.

    rebuild_table

    fn rebuild_table(name : String, columns : Array[
    Column
    ], copy : Array[String]) -> Op

    SQLite's batch alter: build the table again from columns, carrying over the listed copy columns, and swap it in. Indexes on the old table are dropped with it, so pair this with the create_index operations that re-establish them.

    reflect_columns

    Parse PRAGMA table_info rows into Columns — the reflection core, testable without a live database. The pragma projects, in order, cid, name, type,notnull, dflt_value, pk; a non-zero pk marks the primary key and notnull its NOT NULL. This is the read half of Alembic-style autogenerate: compare the reflected columns against a Model's declared columns to diff a schema.

    reflect_columns_mysql

    Parse MySQL SHOW COLUMNS rows (Field, Type, Null, Key, Default, Extra) into Columns, recovering the primary key (Key == "PRI") and autoincrement (Extra contains auto_increment).

    reflect_columns_pg

    Parse PostgreSQL information_schema.columns rows (column_name, data_type, is_nullable) into Columns — the PG counterpart of reflect_columns.

    reflect_schema

    Reflect a whole database: every base table, its columns, and its indexes — the read half of autogenerate, and what a round-trip test compares before and after.

    The per-dialect query texts are public (sqlite_tables_query and friends) so a driver reached over a network can issue the same statements and feed the same parsers, which is how the PostgreSQL round trip runs without a synchronous driver.

    reflect_table

    Reflect a table's columns off a live connection: run PRAGMA table_info and parse it. The thin driver wrapper over reflect_columns.

    reflect_table_for

    Reflect a table's columns off a live connection for a specific Dialect: SQLite via PRAGMA table_info, PostgreSQL via information_schema.columns, MySQL via SHOW COLUMNS. The multi-dialect counterpart of reflect_table.

    rename_column

    fn rename_column(table : String, from : String, to : String) -> Op

    Rename a column. Every dialect here spells this RENAME COLUMN, so it is the one column change that does not need a table rebuild on SQLite.

    rename_table

    fn rename_table(from : String, to : String) -> Op

    Rename a table.

    revision_bases

    fn revision_bases(revisions : Array[Revision]) -> Array[String]

    The revisions with no parents, ascending in declaration order.

    revision_heads

    fn revision_heads(revisions : Array[Revision]) -> Array[String]

    The revisions nothing else descends from — the tips of the graph. More than one means the history has branches that have not been merged yet.

    revisions_table_sql

    fn revisions_table_sql(table : String) -> String raise
    DbError

    The CREATE TABLE IF NOT EXISTS for a revisions table. Public because a caller that executes a plan itself — an offline run, or a driver that lives behind the async seam — has no Session to call ensure_revisions_table on, and the bookkeeping table has to exist before the plan's inserts run.

    sqlite_columns_query

    fn sqlite_columns_query(table : String) -> String raise
    DbError

    PRAGMA table_info for one table: cid, name, type, notnull, dflt_value, pk.

    sqlite_index_info_query

    fn sqlite_index_info_query(index : String) -> String raise
    DbError

    PRAGMA index_info for one index: seqno, cid, name.

    sqlite_index_list_query

    fn sqlite_index_list_query(table : String) -> String raise
    DbError

    PRAGMA index_list for one table: seq, name, unique, origin, partial. Only rows with origin = 'c' are indexes the schema declared; 'u' and 'pk' are the implicit ones a UNIQUE or PRIMARY KEY constraint creates, which have no name to drop.

    sqlite_tables_query

    fn sqlite_tables_query() -> String

    The tables in the current SQLite database, excluding SQLite's own.

    table_names_of

    The table names in a result set whose first column is the name.