Skip to main content

sqlpush

CI PyPI Python License: MIT

Prisma db push for SQLAlchemy. Apply your models (SQLAlchemy, SQLModel, anything built on MetaData) to a live PostgreSQL / TimescaleDB database directly, no migration files. sqlpush diffs your models against the real schema, classifies every operation by risk (safe / risky / destructive), and applies the plan atomically. Drift checks exit with codes your CI can gate on.

sqlpush diff "myapp.models:metadata"      # see the SQL, ordered by risk
sqlpush check "myapp.models:metadata"     # CI gate: exit 0/2/3
sqlpush push "myapp.models:metadata"      # apply (destructive gated)

If you've ever run Base.metadata.create_all() in production and known it was wrong, then sighed at the migration-script treadmill when you reached for alembic: sqlpush is for you.

Why

Declarative models are already the source of truth. Migration files re-encode what the models say, drift from them, and pile up forever. sqlpush closes the loop the way Prisma's db push does for its schema language, but for the SQLAlchemy ecosystem (SQLModel included):

  • No migration files, ever. The diff is the migration: computed fresh from models vs. live database on every run, via alembic's autogenerate engine used as a library.
  • Risk-aware by default. Every operation is classified safe / risky / destructive. Destructive ops (drops) are blocked until --allow-destructive: nothing executes at all while any is present.
  • Drift detection built for CI. check plans once and exits 0 clean / 2 drift / 3 destructive drift, scriptable without parsing output. --json emits a stable versioned contract.
  • Safe under concurrency. An advisory lock (keyed to the database, not the DSN) coordinates workers: one pusher at a time, losers wait bounded and re-verify, so deploy pipelines can race without corrupting anything.
  • Hypertables without hand-written SQL. Decorate a model with @hypertable and the create_hypertable directive is planned state-aware: idempotent pushes, clean checks, no false drift.

PostgreSQL only, by design.

Install

pip install sqlpush

Or from source:

git clone https://github.com/juanmicl/sqlpush && cd sqlpush && uv sync

The 30-second tour

Point sqlpush at your metadata (module:attribute) and a database (--dsn or $DATABASE_URL):

$ export DATABASE_URL="postgresql+psycopg://user:pass@host:5432/db"

$ sqlpush diff "myapp.models:metadata"
-- safe

CREATE TABLE hero (
    id SERIAL NOT NULL PRIMARY KEY,
    name VARCHAR(50) NOT NULL
);

-- risky

CREATE INDEX ix_hero_name ON hero (name);

Push it (the destructive gate is on by default):

$ sqlpush push "myapp.models:metadata"
1 destructive operation(s) blocked; re-run with --allow-destructive
$ echo $?
1

$ sqlpush push "myapp.models:metadata" --allow-destructive
$ echo $?
0

In CI, check drift and fail loudly (see exit codes below). Limit scope with repeated --schema / --exclude options.

Exit codes

verb 0 1 2 3
diff always
check clean drift destructive drift
push applied destructive blocked error (incl. partial failure)

push --safe-only runs only safe operations and skips the rest informationally (exit 0). A failed CREATE INDEX CONCURRENTLY marks the run as partial failure (exit 2) instead of silently half-applying.

FastAPI / SQLModel: replace create_all

from contextlib import asynccontextmanager
from sqlpush import aensure_schema


@asynccontextmanager
async def lifespan(app):
    await aensure_schema(SQLModel.metadata, engine, mode="check")
    yield

Push in the deploy pipeline, check at startup.

How it works

flowchart LR
    models["SQLAlchemy MetaData"] --> diff["diff<br>alembic autogenerate, scoped"]
    db[("live PostgreSQL")] --> diff
    diff --> risk["risk classification<br>safe / risky / destructive"]
    risk --> plan["plan"]
    plan --> render["render"]
    render --> apply["apply<br>atomic txn · CONCURRENTLY split · advisory lock"]
    apply --> report["report"]
  • Diff engine scopes reflection to your target schemas (default: the session's real search_path) and prunes system catalogs (TimescaleDB internals included) before reflection even starts.
  • Classifier maps each operation to a risk class; unknown operations are risky, never silently safe.
  • Executor splits the plan: CONCURRENTLY statements run one-per- transaction on autocommit, everything else applies in a single atomic transaction with a bounded lock_timeout.
  • Typed errors: only SqlpushError / ConnectFailed / MetadataImportError escape the API, never raw driver exceptions.

Comparison

An honest view of the neighborhood (stars as of 2026-08):

migration files source of truth risk gate CI drift exit codes TimescaleDB
sqlpush none (the diff is the migration) SQLAlchemy MetaData classified safe/risky/destructive, destructive blocked by default check 0/2/3 @hypertable directives
alembic (4.4k★) yes migration scripts (autogenerate assists) no no no
atlas (8.7k★) optional (HCL) HCL / SQL (ORMs via providers) lint policies yes no
prisma db push (47k★) none Prisma schema (Node/TS) no no no
migra (3.1k★) diff only SQL n/a partial no (deprecated)

sqlpush is narrower than atlas and younger than alembic, deliberately. It is one tool for one job: keep a PostgreSQL schema in lockstep with SQLAlchemy models, safely enough to run from CI.

Coming from migra (now deprecated)? There is a migration guide.

Design notes

  • import sqlpush stays light: the public API loads lazily, so the annotations module carries none of alembic/typer/psycopg.
  • The advisory-lock key derives from the database OID: two DSN spellings of the same database contend for the same lock.
  • --json output is a versioned contract ("version": 1) meant for tooling; additive changes only within a version.

Roadmap (0.1.x)

  • CREATE INDEX CONCURRENTLY by default for indexes on existing tables
  • asyncpg DSN translation in ensure_schema(AsyncEngine)
  • jsonschema-validated --json output

License

MIT · © 2026 Juan Miguel Contreras

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sqlpush-0.1.0.tar.gz (16.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sqlpush-0.1.0-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

Details for the file sqlpush-0.1.0.tar.gz.

File metadata

  • Download URL: sqlpush-0.1.0.tar.gz
  • Upload date:
  • Size: 16.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sqlpush-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a535d82011818246210abf7bbdaf2e528d407b3751a69ea02126e111f5e02941
MD5 8727d3db1b4e545ef34ca7e729ac32ab
BLAKE2b-256 a73cde06aad65f434064d3e567ea581931524dbee21ef4cd8ef1ad2c28ab14ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlpush-0.1.0.tar.gz:

Publisher: release.yml on juanmicl/sqlpush

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlpush-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: sqlpush-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sqlpush-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4932477b8ddc4270cfe42ae770517bc5f658d380b961f519fd116b885d59c2a1
MD5 543b84f7b9eae6a1b61cd899bfba1c40
BLAKE2b-256 9bf1776d1dc8a91ad053af51b5841f068e6017157b8f0368d3e0cf3e4e4c3fef

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlpush-0.1.0-py3-none-any.whl:

Publisher: release.yml on juanmicl/sqlpush

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.7.0

2 files

0.6.0

2 files

0.5.1

2 files

0.5.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

This release

0.1.0 This release

2 files

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