GizStack
View .mdOpen in ChatGPTOpen in Claude

gizdb:migrate

Bounded migration commands use the public DAL actor and its transaction owner.

gizlang
import * as Migrate from "gizdb:migrate";

Types

Backfill

gizlang
type Backfill = { source: string; keys: Vec<Typed.Slot>; inputs: Vec<Typed.Slot>; target: string; outputs: Vec<Typed.Slot>; mode: string; transform: (row: Vec<Value>) => Result<Vec<Value>, DbError> effects {fail}; }

Inputs and outputs start with the complete primary key, in storage order. The transform is pure. It can run again after an uncommitted batch fails. insert moves rows to a new table; update fills non-key columns in place.

  • source: string
  • keys: Vec<M79.Slot>
  • inputs: Vec<M79.Slot>
  • target: string
  • outputs: Vec<M79.Slot>
  • mode: string
  • transform: (row: Vec<M80.Value>) => _gizlang_sdk_core.Result<Vec<M80.Value>, M81.DbError> effects { fail }

Step

gizlang
type Step = | { kind: "schema"; statements: Vec<Statement>; } | { kind: "data"; backfill: Backfill; } | { kind: "contract"; statements: Vec<Statement>; }

The schema planner's caller supplies lowered statements. This executor does not derive a second schema. Only an explicit final contract can remove a column.

  • schema
  • data
  • contract

Migration

gizlang
type Migration = { id: string; digest: bytes; steps: Vec<Step>; }

The caller supplies a stable 32-byte digest of the complete plan and transform. Function values have no stable serialization. Reusing a digest after editing a transform violates this declaration.

  • id: string
  • digest: bytes
  • steps: Vec<M83.Step>

Limits

gizlang
type Limits = { rows: i64; bytes: i64; batches: i64; }

bytes bounds retained source pages and the encoded input, output and progress values of each batch. The DAL owns SQL/WAL allocation admission separately. batches=0 runs until completion; a positive value pauses after that many data commits.

  • rows: i64
  • bytes: i64
  • batches: i64

Progress

gizlang
type Progress = { id: string; step: i64; lastKey: string; rows: i64; state: string; digest: bytes; }

Where a migration stands: its step, the last key it moved, the rows so far, its state and its plan's digest.

  • id: string
  • step: i64
  • lastKey: string
  • rows: i64
  • state: string
  • digest: bytes

Report

gizlang
type Report = { progress: Progress; batches: i64; paused: bool; }

What a run did: the progress it reached, its batches, and whether it paused at Limits.batches.

  • progress: M83.Progress
  • batches: i64
  • paused: bool

Functions

status

gizlang
function status(connection: Connection, id: string): Option<Progress> throws DbError

Missing metadata or an unknown id returns none. Other failures remain errors.

Effects: fail, io.

apply

gizlang
function apply(connection: Connection, migration: Migration, limits: Limits): Report throws DbError

Each committed batch and its cursor are one actor write transaction. A fresh frontier is taken BEFORE progress and source reads. apply fences that frontier in the actor before it writes. Therefore every accepted transform used the row's current state at commit. A concurrent commit returns conflict; resume rereads progress and the failed batch. No stale value or progress is published.

Guarantee: per-row commit-time conversion, not a continuously maintained projection. Edits/inserts/deletes behind lastKey are not replayed. Same-table updates preserve fields outside outputs. Applications must maintain converted fields on subsequent writes before an explicit contract is safe. General online catch-up needs durable change delivery acknowledged in the SAME write as migration progress. Connection.changes currently publishes its checkpoint separately; it cannot provide that crash guarantee through this boundary. Transform evaluation can repeat; committed data batches cannot repeat.

Effects: fail, read, write, console, io.