Find SQL bugs
before your users do

Property-based testing for PostgreSQL. Generates random valid datasets that respect your schema — then tries to break your queries.

$ pip install sqlproof
Get Started → View on GitHub

// why sqlproof

Your queries work on your fixtures.
Do they work on real data?

Hand-crafted test data misses edge cases. SqlProof generates thousands of valid random datasets and finds the one that breaks your invariants.

Schema-aware generation

Respects foreign keys, CHECK constraints, UNIQUE, NOT NULL, and enum types automatically. No invalid data, no constraint violations.

Minimal counterexamples

When a property fails, Hypothesis shrinks the dataset to the smallest possible example — so you fix the bug, not hunt for it.

Zero infrastructure

Spins up a disposable Postgres via testcontainers. No external DB needed. Each run gets its own isolated schema — fast and clean.

Works with your test runner

Drop it into pytest. Decorate a test with @sqlproof(...) or call proof.check() directly. No new runner to learn.

Four steps, zero boilerplate

1

Parse schema

Reads your SQL file or introspects a live Postgres DB to extract tables, types, FKs, and constraints.

2

Generate data

Creates random valid rows for every table in FK-dependency order. Respects all constraints.

3

Insert & run

Inserts into an isolated Postgres schema, runs your property, then drops the schema. Repeats up to N times.

4

Report

On failure, shrinks to the minimal counterexample and reports it with a reproducible seed.

// example

See it in action

test_orders.py
from sqlproof import SqlProof, sqlproof proof = SqlProof.from_schema_file('./schema.sql') @sqlproof(proof, sizes={ 'customers': 10, 'orders': 50 }, runs=50) def test_order_totals_non_negative(db): rows = db.query('SELECT total FROM orders') assert all(row['total'] >= 0 for row in rows) # On failure, SqlProof writes a minimal counterexample JSON. # Reproduce with: pytest ... --sqlproof-seed=1708891234