cmd / pgfmt
pgfmt is a small SQL formatter for Postgres query files.
For package-local SQL query layout and embedding, see
go / postgres. For a static check
over the same query files, see cmd / nullscan.
I run it on */queries/*.sql so SQL style stays consistent
without review bikeshedding.
Why
When SQL lives in many package-local directories, style drifts fast. Diffs get noisy. Reviews spend time on formatting.
A formatter keeps diffs focused on behavior.
Command
Format in place:
go run ./cmd/pgfmt -w <pkg>/queries/*.sql
Format a named file to stdout:
go run ./cmd/pgfmt query.sql
Format stdin to stdout:
cat query.sql | go run ./cmd/pgfmt
CI behavior
The -c flag checks formatting, exits non-zero if any file would
change, and writes nothing. It prints the exact command to fix each
file:
FAIL: <path> needs formatting. Run: go run ./cmd/pgfmt -w <path>
-c reports only real formatting violations, so it does not need a
clean working tree. -c and -w are mutually exclusive.
Style it enforces
- Uppercase SQL keywords. Type names (
bigint,text,interval) stay lowercase. - Lowercase function names.
- Two-space indentation.
- An 80-column limit decides when a line stays inline or wraps.
- Vertical clause lists (
SELECT,FROM,WHERE,ORDER BY). - Stable wrapping for common constructs (
CASE,NOT EXISTS, joins). - Paired-argument functions (
jsonb_build_object) wrap two arguments per line.
DDL keywords like index, key, and add uppercase only inside
CREATE/ALTER/DROP. In DML, where they are often column names,
they stay lowercase.
pgfmt is idempotent. Running it twice produces the same output.
Safety
After formatting, pgfmt re-lexes its own output and compares the token stream to the input. If they differ, it fails and writes nothing. The formatter cannot silently corrupt a query.
Comments are the one exception. Standalone comments on their own line survive; trailing comments and comments inside parentheses are dropped.
Workflow
For package-local queries:
- Write or edit SQL in
<pkg>/queries/*.sql. - Run pgfmt on those files.
- Commit the formatted SQL with the Go call-site changes.