Module Sqlite.Migration

Database migration system with tracking and versioning.

Provides types for defining migrations (SQL or OCaml), helpers for creating and applying them, and a functor for managing migration state via a tracking table.

Writing SQL migrations

SQL migrations are plain .sql files named N_name.sql where N is a sequence number (e.g. 000_initial.sql, 001_add_index.sql). The SQL content is split on semicolons and each statement is executed in order.

Writing OCaml migrations

OCaml migrations are .ml files named mN_name.ml where N matches the sequence number (e.g. m002_backfill.ml). The module must expose:

  val name : string
  val up : Sqlite.Migration.step list

Steps can be SQL statements or arbitrary OCaml functions:

  let name = "backfill"

  let up =
    [
      Sqlite.Migration.Sql
        (Sqlite.Request.(Caqti_type.Std.unit ->. Caqti_type.Std.unit)
        @@ "CREATE INDEX idx_foo ON bar(baz)");
      Sqlite.Migration.Ocaml
        (fun conn ->
          let open Lwt_result_syntax in
          (* ... arbitrary logic using Db.exec, Db.find, etc. ... *)
          return_unit);
    ]

Mixing SQL and OCaml in a single step

An Ocaml step can execute raw SQL via Db.exec:

  Sqlite.Migration.Ocaml
    (fun conn ->
      let open Lwt_result_syntax in
      let* () =
        Db.exec conn
          (Sqlite.Request.(Caqti_type.Std.unit ->. Caqti_type.Std.unit)
          @@ "ALTER TABLE t ADD COLUMN c INTEGER")
          ()
      in
      (* ... then do OCaml logic ... *)
      return_unit)

Numbering rules

SQL and OCaml migrations share the same numbering sequence. The gen_migrations tool validates that numbering starts at 0, has no gaps, and has no duplicates. SQL and OCaml files can be freely interleaved (e.g. 000_init.sql, m001_backfill.ml, 002_add_column.sql).

type step =
  1. | Sql of (unit, unit, [ `Zero ]) Request.t
  2. | Ocaml of Db.conn -> unit Tezos_error_monad.Error_monad.tzresult Lwt.t

A single migration step: either a SQL statement or an OCaml function.

type t = {
  1. name : string;
  2. steps : step list;
}

A migration with a name and a list of steps to apply in order.

val make_sql : name:string -> string -> t

make_sql ~name sql creates a SQL-only migration by splitting sql on semicolons into individual statements. Empty statements are ignored.

module type MIGRATION_CONFIG = sig ... end

Configuration for the Make functor.

module Make (C : MIGRATION_CONFIG) : sig ... end

Functor that creates a migration manager for a given configuration.