packages/library/odin-migrate
o

odin-migrate

v8d8749elibrary

dbmate port for Odin apps

0 stars0 forksUnknownupdated 2 months ago
Open repo

odin-migrate

Claude code assisted port of dbmate for Odin apps.

Currently supports PostgreSQL.

Prerequisites

  • Odin compiler
  • PostgreSQL client library (libpq)
    • macOS: brew install libpq
    • Debian/Ubuntu: apt install libpq-dev
  • pg_dump (included with libpq) for schema dump

Build

make build

# or directly
odin build cli -out:bin/omg

For an optimized release build:

make build-release

Configuration

Set your database URL via environment variable:

export DATABASE_URL="postgres://user:password@localhost:5432/myapp?sslmode=disable"

Or pass it as a flag:

omg --url "postgres://user:password@localhost:5432/myapp?sslmode=disable" <command>

Commands

Create a new migration

omg new create_users
# Created: db/migrations/20260215120000_create_users.sql

Edit the generated file:

-- migrate:up
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT NOT NULL UNIQUE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- migrate:down
DROP TABLE IF EXISTS users;

Create the database

omg create
# Created database "myapp".

Apply all pending migrations

omg migrate
# Applying: 20260215120000_create_users.sql
# Applied 1 migration(s).

Create database + migrate (one step)

omg up
# Created database "myapp".
# Applying: 20260215120000_create_users.sql
# Applied 1 migration(s).

Check migration status

omg status
#   [x]  20260215120000_create_users.sql
#   [ ]  20260215130000_add_posts.sql

Rollback the last migration

omg rollback
# Rolling back: 20260215120000_create_users.sql
# Rolled back 1 migration.

Rollback all migrations

omg down
# Rolling back: 20260215130000_add_posts.sql
# Rolling back: 20260215120000_create_users.sql
# Rolled back 2 migration(s).

Dump schema to file

omg dump
# Wrote: ./db/schema.sql

Load schema from file

omg load
# Schema loaded successfully.

Drop the database

omg drop
# Dropped database "myapp".

Wait for database availability

omg wait
# Waiting for database ok

Flags

Flag Short Description Default
--url -u Database URL (or set DATABASE_URL env)
--migrations-dir -d Migrations directory ./db/migrations
--schema-file -s Schema file path ./db/schema.sql
--migrations-table Migrations table name schema_migrations
--wait-timeout Wait timeout in seconds 60
--no-dump-schema Skip schema dump after migrate/rollback false

Migration file format

Migration files use plain SQL with marker comments:

-- migrate:up
<SQL to apply>

-- migrate:down
<SQL to revert>

Files are named with a timestamp prefix: YYYYMMDDHHMMSS_description.sql

Disabling transaction wrapping

Some PostgreSQL operations cannot run inside a transaction (e.g., CREATE INDEX CONCURRENTLY, ALTER TYPE ... ADD VALUE). Add transaction:false to the marker line:

-- migrate:up transaction:false
ALTER TYPE status ADD VALUE 'archived';

-- migrate:down transaction:false
-- PostgreSQL does not support removing enum values

Using as a library

The core omg package can be imported directly into your Odin project:

import omg "path/to/odin-migrate"
import "path/to/odin-migrate/pg"

main :: proc() {
    config := omg.default_config()
    config.database_url = "postgres://user:pass@localhost:5432/myapp?sslmode=disable"

    driver := pg.make_driver()

    db, err := omg.make_db(config, driver)
    if err != nil { /* handle error */ }
    defer omg.db_close(&db)

    // Create database + run migrations
    created, result, up_err := omg.db_up(&db)
    if up_err != nil { /* handle error */ }

    // Check status
    entries, status_err := omg.db_status(&db)
    // entries is [dynamic]Status_Entry with .version, .name, .applied
}

All library functions return data instead of printing, so the caller controls output.

Embedding migrations in a binary

Use Odin's #load_directory to embed all migration files at compile time, then use load_directory_migrations to parse them. The compiled binary contains all migrations — no external files needed at runtime:

import omg "path/to/odin-migrate"
import "path/to/odin-migrate/pg"

// Embed entire migrations directory at compile time
MIGRATIONS :: #load_directory("db/migrations")

main :: proc() {
    config := omg.default_config()
    config.database_url = "postgres://user:pass@localhost:5432/myapp?sslmode=disable"
    config.no_dump_schema = true // no filesystem access at runtime

    driver := pg.make_driver()
    db, err := omg.make_db(config, driver)
    if err != nil { /* handle */ }
    defer omg.db_close(&db)

    // Parse embedded migrations (filters .sql, sorts by version)
    migrations, parse_err := omg.load_directory_migrations(MIGRATIONS)
    if parse_err != nil { /* handle */ }

    // Create DB + run embedded migrations
    created, result, up_err := omg.db_up_with(&db, migrations[:])
    if up_err != nil { /* handle */ }
}
Package Info
Version
v8d8749e
License
Unknown
Author
@ankitpatial
Type
library
Forks
0
Created
5 months ago
Updated
2 months ago
Health
maintained
has releases
has readme
has license
Activity
last 56 weeks