Skip to main content

metapg.migration

Raw SQL migrations for PostgreSQL with dependency tracking and rollback support

metapg.migration provides a powerful migration system using plain SQL files with support for dependencies, rollbacks, and multi-database environments.

Installation

pip install metapg.migration

Quick Start

import metapg.migration

# Create migration runner
runner = metapg.migration.MigrationRunner(db_name="main")

# Check migration status
status = await runner.get_status()
print(f"Applied: {len(status.applied)}, Pending: {len(status.pending)}")

# Apply pending migrations
applied = await runner.apply_pending()
print(f"Applied migrations: {applied}")

# Rollback if needed
await runner.rollback_migration("002_add_users")

Features

  • 📜 Raw SQL - Write migrations in plain SQL for full database feature access
  • 🔄 Rollback Support - Optional rollback SQL in the same file
  • 📊 Dependency Tracking - Declare migration dependencies
  • ✅ Checksums - Detect migration file changes after application
  • 🎛️ Multi-Database - Support for multiple named databases
  • ⚡ Async-First - Built on async database operations
  • 🔒 Transaction Safety - Each migration runs in its own transaction

Migration File Format

Create migration files in migrations/{db_name}/ directory:

-- migrations/default/001_create_users.sql

-- Create users table
-- Description: Add user authentication system
-- Depends: (none)

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);

-- ROLLBACK (optional)
DROP INDEX idx_users_email;
DROP TABLE users;

File Naming Convention

  • Format: {version}_{description}.sql
  • Version: 3-digit zero-padded number (001, 002, 003...)
  • Description: Snake_case description

Examples:

  • 001_initial_schema.sql
  • 002_add_users_table.sql
  • 003_add_posts_index.sql

API Reference

Migration Runner

import metapg.migration

# Create runner for specific database
runner = metapg.migration.MigrationRunner(
    db_name="default",
    migrations_dir=Path("migrations/default")
)

# Get migration status
status = await runner.get_status()

# Apply specific migration
migration = metapg.migration.Migration.from_file(Path("001_initial.sql"))
await runner.apply_migration(migration)

# Apply all pending migrations
applied = await runner.apply_pending()

# Apply up to specific target
applied = await runner.apply_pending(target="003_add_posts")

# Rollback migration
await runner.rollback_migration("002_add_users")

# Get applied migrations
records = await runner.get_applied_migrations()

Migration Models

# Load migration from file
migration = metapg.migration.Migration.from_file(Path("001_initial.sql"))
print(f"Name: {migration.name}")
print(f"Version: {migration.version}")
print(f"Checksum: {migration.checksum}")
print(f"Dependencies: {migration.dependencies}")

# Migration record from database
record = metapg.migration.MigrationRecord.from_dict({
    "name": "001_initial",
    "version": 1,
    "checksum": "abc123",
    "applied_at": datetime.now(),
    "duration_ms": 150.5
})

# Migration status
status = metapg.migration.MigrationStatus(
    db_name="main",
    applied=[record],
    pending=[migration],
    total_files=2
)
print(f"Up to date: {status.is_up_to_date}")

Directory Structure

project/
├── migrations/
│   ├── default/              # Default database
│   │   ├── 001_initial.sql
│   │   ├── 002_add_users.sql
│   │   └── 003_add_posts.sql
│   └── analytics/            # Named database
│       ├── 001_events.sql
│       └── 002_sessions.sql
└── app/
    └── main.py

Advanced Features

Migration Dependencies

-- migrations/default/005_add_foreign_keys.sql
-- Depends: 002_add_users, 003_add_posts

ALTER TABLE posts ADD CONSTRAINT fk_posts_user_id 
    FOREIGN KEY (user_id) REFERENCES users(id);

Conditional Rollbacks

-- Create table with data
CREATE TABLE settings (key VARCHAR(50), value TEXT);
INSERT INTO settings (key, value) VALUES ('version', '1.0');

-- ROLLBACK
-- Only drop if it exists
DROP TABLE IF EXISTS settings;

Multi-Database Migrations

# Set up multiple databases
import os
os.environ["DATABASE_URL"] = "postgresql://localhost/main_db"
os.environ["DATABASE_URL_ANALYTICS"] = "postgresql://localhost/analytics_db"

# Run migrations for each database
main_runner = metapg.migration.MigrationRunner("default")
analytics_runner = metapg.migration.MigrationRunner("analytics")

await main_runner.apply_pending()
await analytics_runner.apply_pending()

Error Handling

try:
    await runner.apply_pending()
except Exception as e:
    print(f"Migration failed: {e}")
    
    # Check which migrations failed
    status = await runner.get_status()
    for record in status.applied:
        if record.error:
            print(f"Failed migration: {record.name} - {record.error}")

Best Practices

1. Atomic Migrations

Each migration runs in a transaction and should be atomic.

2. Backward Compatible Changes

When possible, make changes that don't break existing code:

-- Good: Add optional column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Better: Add with default
ALTER TABLE users ADD COLUMN phone VARCHAR(20) DEFAULT '';

3. Data Migrations

Separate schema and data changes:

-- 001_add_status_column.sql
ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active';

-- 002_populate_status.sql  
UPDATE users SET status = 'inactive' WHERE last_login < NOW() - INTERVAL '1 year';

4. Testing Migrations

Always test migrations on a copy of production data.

License

MIT License - see LICENSE file for details.

Part of metapg

This package is part of the metapg metapackage for PostgreSQL operations.

Metadata

Release files for metapg.migrations 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for metapg.migrations 0.0.1
File Size Uploaded Provenance
metapg_migrations-0.0.1.tar.gz 22.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for metapg.migrations 0.0.1
File Interpreter ABI Platform
metapg_migrations-0.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 30.2 kB

Release files / metapg_migrations-0.0.1.tar.gz

Download URL metapg_migrations-0.0.1.tar.gz
Size 22.6 kB
Tags Source
SHA-256 checksum
How to use checksums
3dcdaa6e6c39a0e5b5c6c0d9c483fba70b72370137f67c6ee3508fbe4869e4b3
BLAKE2b-256 checksum
How to use checksums
6a13d37e929a5fe6687f0ef64b87f161743d0aa7c683a54d5363d0f1ba2d66da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release files / metapg_migrations-0.0.1-py3-none-any.whl

Download URL metapg_migrations-0.0.1-py3-none-any.whl
Size 7.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a50f5ffbc8c4aa3ec25531bb03764ea99646999d5efb05209d226443e809d21
BLAKE2b-256 checksum
How to use checksums
6a48550b147f6590c22ac9f6165937ba8b1082cb167c71a2c9c8a1e3ddb10593
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Security for metapg.migrations 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page