cmd / pgfmt

pgfmt is a SQL formatter for Postgres query files. For the query layout, see go / postgres. For a static check over the same files, see cmd / nullscan.

I run it on */queries/*.sql so reviews do not spend time on style.

Install

pgfmt lives in its own module. Add it to a project as a module tool:

go get -tool github.com/croaky/pgfmt/cmd/pgfmt

That writes a tool line to go.mod next to the require line that pins the version:

tool github.com/croaky/pgfmt/cmd/pgfmt

Why go tool

Go 1.24 added the tool directive. go tool <name> builds and runs the pinned version from the module cache. Local commands and CI use the same version from go.mod. Several repos share one implementation.

There is no install step and nothing to put on $PATH. A tool upgrade appears in git history.

Go caches the binary after the first build.

Command

Format in place:

go tool pgfmt -w <pkg>/queries/*.sql

Format a named file to stdout:

go tool pgfmt query.sql

Format stdin to stdout:

cat query.sql | go tool pgfmt

CI

The -c flag checks formatting, exits with an error if a file needs changes, and writes nothing. It prints the command to fix each file:

FAIL: <path> needs formatting. Run: pgfmt -w <path>

-c and -w are mutually exclusive.

In a cibot Checkfile, that is one line:

sqlfmt: git ls-files -z '*queries/*.sql' | xargs -0 go tool pgfmt -c

Style

DDL keywords such as index, key, and add become uppercase only inside CREATE, ALTER, and DROP. In DML they are often column names, so they stay lowercase.

pgfmt is idempotent.

Safety

After formatting, pgfmt parses its output and compares the token stream to the input. If they differ, it fails and writes nothing.

Comments are the one exception. pgfmt keeps standalone comments on their own line, but drops trailing comments and comments inside parentheses.

Workflow

  1. Write or edit SQL in <pkg>/queries/*.sql.
  2. Run go tool pgfmt -w <pkg>/queries/*.sql.
  3. Commit the formatted SQL with the Go call-site changes.

← All articles