Improving pgindent's Handling of Designated Initializers and Variadic Function Signatures
The Core Problem
pgindent is PostgreSQL's canonical source-code formatter, built atop a vendored fork of BSD indent (pg_bsd_indent). Because the formatter is run tree-wide prior to each release branch cut, any change to its behavior produces churn across the entire codebase and affects every in-flight patch. This gives the tool an outsized political weight: even small formatting improvements must be carefully timed and justified.
Andreas Karlsson identifies two long-standing cosmetic defects in the tool's output, both rooted in the same underlying lexical confusion:
Defect 1: Designated (C99) initializers
Given a struct literal using the .field = value syntax, pg_bsd_indent fails to insert a space after the comma that precedes the next designator:
/* Before */
static RBTNode sentinel =
{
.color = RBTBLACK,.left = RBTNIL,.right = RBTNIL,.parent = NULL
};
/* After */
static RBTNode sentinel =
{
.color = RBTBLACK, .left = RBTNIL, .right = RBTNIL, .parent = NULL
};
The root cause is that the indenter treats the . token uniformly as the structure-member access operator (which binds tightly to its left operand with no surrounding whitespace, e.g. foo.bar). After a comma in an initializer list, however, the . is a designator introducer, syntactically distinct. The fix is to recognize that when a . follows a comma (in a context where an expression/designator begins), it should be preceded by a space like any other expression-starting token.
Andreas notes that the RBT sentinel is essentially the only in-tree occurrence — not because the pattern is unpopular, but because the ugly output has discouraged developers from ever writing multi-field designated initializers on a single line. The consequence is a subtle distortion of PostgreSQL's coding style imposed by a tool bug. Extensions, which don't hand-tweak around pgindent's quirks, more often exhibit the malformed output.
Defect 2: Variadic function declarations
The same token-classification logic affects ...:
/* Before */
errdetail(const char *fmt,...)
/* After */
errdetail(const char *fmt, ...)
Here the three-dot ellipsis is presumably being parsed as a sequence of . tokens and inheriting the "member access" no-space rule. Fixing the comma/dot interaction in (1) apparently also repairs (2) as a side effect — a clue that the underlying state machine conflates these contexts.
Architectural Significance
Though superficially cosmetic, this matters because:
- Coding style leaks from the tool's capabilities. When a formatter produces ugly output for a valid idiom, developers avoid the idiom. Designated initializers are strictly better than positional initialization for readability and for forward-compatibility when struct layouts change — PostgreSQL's style has been quietly suppressing them.
- Tree-wide reformatting has cost. Every byte changed invalidates
git blame, conflicts with pending patches, and fragments backporting. This is why changes topgindentare batched with release-branch creation. pg_bsd_indentis effectively PostgreSQL-owned. Upstream BSDindenthas diverged; fixes must be made in-tree and the version number bumped to force developers to rebuild.
Patch Mechanics and Tom Lane's Refinements
The committed version (staged by Tom Lane on 2026-05-05) makes three concrete changes to Andreas's submission:
- Version bump of
pg_bsd_indent. Critical for a formatter: without the bump, developers running a stale local build would silently reintroduce the old formatting, creating spurious diffs. This enforces tool-version discipline. - Folds
tests/declarations.0.stdoutupdate into the main patch. Andreas had separated the expected-output update, which would have causedmake check-worldto fail for any commit between the two — agit bisecthazard. Tom's insistence on atomic commits here reflects a well-established project invariant: every commit on master must build and pass tests cleanly. - A companion
v6-0002shows the tree-wide effect but is explicitly not to be committed until timing is right.
The Timing Question
The most substantive discussion is not about the patch itself but about when to land a pgindent change. Álvaro raises the question: before or after beta1?
- Before beta1: More time to shake out problems before users see them, but increases churn during stabilization and may conflict with bug-fix backports.
- After beta1: Any pg19 features likely to be reverted will already have been, reducing wasted reformatting.
Tom, drawing on historical precedent (citing commits from 2019, 2020, 2023, and 2025), settles the matter: mid-May, roughly two weeks before the new branch is cut, is the project's established rhythm. He dismisses the user-visible-problem concern — a formatter change that broke semantics would manifest as compile failures caught immediately, and the diffs are visually audited before commit. His binding constraint is instead: don't reformat too close to when the next CommitFest opens, because that would force every in-flight patch author to rebase over mechanical churn.
This exchange is a good example of how Tom's institutional memory functions as project policy — he doesn't need to re-litigate the timing question, he simply cites five prior instances of the pattern.
Participant Dynamics
- Andreas Karlsson drives the patch, rebases three times over ~two months, and accepts every piece of feedback without resistance. He's a seasoned PostgreSQL hacker; this is a well-scoped quality-of-life improvement in his wheelhouse.
- Tom Lane is the de facto owner of
pgindent(authored the last several intentional changes to it) and the committer here. His review focuses on commit hygiene (version bump, atomicity) rather than the formatting logic itself. - Álvaro Herrera (committer) contributes the useful framing of when to commit; his own discomfort with variadic indentation ("I've wondered why variadic functions had such a weird indentation and this explains it") is a mild endorsement from a long-tenured developer.
- Jelte Fennema-Nio and Evan Li provide lightweight +1s; Evan also catches typos (
varidic→variadic,treaing→treating) in the commit message.
No one opposes the change. The only tension — and it's low-temperature — is over release-cycle timing.
Technical Takeaway
The patch is small, but it illustrates a recurring PostgreSQL theme: tooling shapes style, and style shapes what idioms the project uses. Fixing pg_bsd_indent's token-context handling for . after , unlocks designated initializers as an ergonomic option across the codebase, and as a bonus cleans up variadic declarations. The real engineering judgment here is not in the diff but in the commit discipline (version bump, atomic test updates) and the scheduling (mid-May, before branch cut, after CF closes).