pgsafe 0.9.0

Static safety linter for PostgreSQL DDL migrations — catches unsafe schema changes before they lock or break production.
Documentation

pgsafe

Static safety linter for PostgreSQL DDL migrations. pgsafe parses SQL and flags schema changes likely to lock or break production — no database connection, no network.

📖 Full documentation, the rules reference, and an in-browser playground: pgsafe.fixedwidth.tech

Install

Install from crates.io with a Rust toolchain:

cargo install pgsafe

Or download a prebuilt binary from the latest release (static Linux and macOS), or build from source with cargo build --release (binary at target/release/pgsafe).

Quickstart

pgsafe migration.sql                 # lint a file (exit 1 on a finding)
pgsafe --format json migration.sql   # machine-readable
pgsafe --list-rules                  # every rule this build checks

Applying fixes

Findings that have an unambiguous mechanical rewrite carry a fix. Preview them:

pgsafe --diff db/migrate/003_add_index.sql

The output is a standard unified diff, so it pipes straight into git apply:

pgsafe --diff db/migrate/003_add_index.sql | git apply

Apply them in place (or to stdout when reading stdin):

pgsafe --fix db/migrate/003_add_index.sql

--fix and --diff are human-output only and cannot be combined with each other or with --format json/--format github/--format sarif. A -- pgsafe:ignore finding is never auto-fixed. After --fix, the exit code reflects re-linting the fixed file.

Run it in CI with the GitHub Action:

- uses: fixed-width/pgsafe@v0.8.6
  with:
    files: "db/migrate/*.sql"

SARIF output (GitHub code scanning)

--format sarif emits SARIF 2.1.0, for upload to GitHub code scanning:

- run: pgsafe --format sarif db/migrate/*.sql > pgsafe.sarif
- uses: github/codeql-action/upload-sarif@v3
  # pgsafe exits non-zero when findings gate, so upload the results regardless:
  if: always()
  with:
    sarif_file: pgsafe.sarif

Findings (including -- pgsafe:ignore-suppressed ones, marked dismissed) become SARIF results; a file that fails to parse becomes a tool-execution notification.

A findings run (exit 1) and a parse error (exit 2) both still write valid SARIF, so if: always() uploads the results in the common cases. A configuration or I/O error (e.g. an unreadable path) exits 2 without writing SARIF — the resulting 0-byte file then (correctly) fails the upload. Pass repo-relative migration paths: absolute paths and stdin don't map back to files GitHub can annotate as code-scanning alerts.

See pgsafe.fixedwidth.tech/docs for configuration, output formats, and the full rules reference.

License

Apache-2.0. See LICENSE.