A PostgreSQL SQL formatter with multiple style options.
pgfmt is a small command-line wrapper around libpgfmt, a Rust library that parses SQL with tree-sitter-postgres and reformats it according to one of several well-known style guides.
brew tap gmr/postgres
brew install pgfmtNote
Homebrew 6.0 added tap trust, and some
versions fail to install third-party taps inside the build sandbox (the
error mentions build.rb ... exited with 1). If you hit this, trust the
formula first:
brew trust --formula gmr/postgres/pgfmtor, as a temporary workaround, set HOMEBREW_NO_REQUIRE_TAP_TRUST=1 for
the install.
curl -fsSL https://raw.githubusercontent.com/gmr/pgfmt/main/install.sh | shTo install to a custom directory:
INSTALL_DIR=~/.local/bin curl -fsSL https://raw.githubusercontent.com/gmr/pgfmt/main/install.sh | shVERSION=v1.0.0 curl -fsSL https://raw.githubusercontent.com/gmr/pgfmt/main/install.sh | shcargo install pgfmtPre-built binaries for Linux and macOS (x86_64 and aarch64) are available on the GitHub Releases page.
# Format a file (default: aweber style)
pgfmt query.sql
# Format from stdin
echo "SELECT a,b FROM t WHERE x=1" | pgfmt
# Choose a style
pgfmt --style mozilla query.sql
pgfmt --style dbt query.sql
# Round-trip pg_dump / pg_get_viewdef output
pg_dump --schema-only mydb | pgfmt --style pg_dump
# Check if already formatted (exit 1 if not)
pgfmt --check query.sql| Style | Based On |
|---|---|
| aweber (default) | AWeber SQL Style Guide |
| dbt | How we style our SQL by dbt Labs |
| gitlab | GitLab SQL Style Guide |
| kickstarter | Kickstarter SQL Style Guide by Fred Benenson |
| mattmc3 | Modern SQL Style Guide by mattmc3 |
| mozilla | Mozilla SQL Style Guide |
| pg_dump | PostgreSQL's built-in ruleutils.c deparser (pg_dump / pg_get_viewdef) |
| river | SQL Style Guide by Simon Holywell |
Based on river style with JOINs as river keywords. INNER JOIN, LEFT JOIN, etc. participate in river alignment. Uppercase keywords.
SELECT r.last_name
FROM riders AS r
INNER JOIN bikes AS b
ON r.bike_vin_num = b.vin_num
AND b.engines > 2;Based on dbt Labs' SQL style. Lowercase keywords, 4-space indent, blank lines between clauses, generous whitespace. Explicit join types.
select
a.title,
a.release_date
from albums as a
where
a.title = 'Charcoal Lane'
or a.title = 'The New Danger'Based on the GitLab SQL Style Guide. Uppercase keywords, 2-space indent, blank lines inside CTE bodies.
SELECT
a.title,
a.release_date
FROM albums AS a
WHERE
a.title = 'Charcoal Lane'
OR a.title = 'The New Danger';Based on the Kickstarter SQL Style Guide. Uppercase keywords, 2-space indent, JOIN ON on same line, compact CTE chaining.
SELECT
a.title,
a.release_date
FROM albums AS a
INNER JOIN orders AS o ON a.id = o.album_id
WHERE
a.title = 'Charcoal Lane'
AND a.year > 2000;Based on the Modern SQL Style Guide
by mattmc3. Lowercase river-style with leading commas. Uses plain join
instead of inner join.
select a.title
, a.release_date
from albums as a
join orders as o
on a.id = o.album_id
where a.title = 'Charcoal Lane'
and a.year > 2000;Based on the Mozilla SQL Style Guide. Keywords left-aligned at column 0, content indented 4 spaces underneath. One item per line. Uppercase keywords.
SELECT
a.title,
a.release_date
FROM albums AS a
WHERE
a.title = 'Charcoal Lane'
OR a.title = 'The New Danger';Mimics PostgreSQL's internal ruleutils.c deparser — the layout emitted by
pg_dump, pg_get_viewdef, and pg_get_functiondef. Uppercase keywords,
leading-space indentation, lowercase built-in functions. The correctness bar
is byte-identical round-tripping of genuine deparser output. Also accepts
pgdump and postgres as aliases.
SELECT u.email,
count(*) AS n,
sum(o.total) AS revenue
FROM app.users u
JOIN app.orders o ON o.user_id = u.id
WHERE o.placed_at > (now() - '30 days'::interval)
GROUP BY u.email
HAVING sum(o.total) > 100::numeric
ORDER BY (sum(o.total)) DESC;Based on sqlstyle.guide by Simon Holywell. Keywords are right-aligned to form a visual "river" separating keywords from content. Uppercase keywords.
SELECT a.title,
a.release_date
FROM albums AS a
WHERE a.title = 'Charcoal Lane'
OR a.title = 'The New Danger';