StringInfo fixes, v19 edition. Plus a few oddities

First seen: 2026-04-12 00:40:53+00:00 · Messages: 17 · Participants: 8

Latest Update

2026-05-06 · opus 4.7

StringInfo fixes, v19 edition. Plus a few oddities — Technical Analysis

Core Problem

This thread addresses two intertwined concerns that recur each PostgreSQL development cycle:

  1. Misuse of appendStringInfo() / appendPQExpBuffer() variants — these are printf-style functions that invoke vsnprintf() internally. When callers pass a plain literal with no format arguments (or a literal "%s" with a single string), they pay unnecessary CPU cost for format parsing when appendStringInfoString() / appendStringInfoChar() would be far cheaper. This is a recurring "whack-a-mole" problem — Rowley previously cleaned it up in commits 8461424fd (v17) and 928394b66 (v18), and is now doing the v19 round.

  2. Construction of translatable messages from fragments — discovered incidentally in append_tuple_value_detail() (introduced in 48efefa6ca for logical replication conflict reporting), where calls like _("."), _(": "), _(", ") were being concatenated to build conflict detail messages. This violates PostgreSQL's message style guide because translators cannot meaningfully localize disconnected punctuation fragments — the same "." token has no stable meaning across contexts, and some languages (e.g., French) have entirely different spacing/punctuation conventions (e.g., " : " with a non-breaking space before the colon).

Patch Structure and Disposition

Rowley proposed three patches:

Key Technical Debate: Make the Misuse Impossible

Andres Freund's proposal is the architecturally significant contribution. Rather than repeatedly hand-auditing, make the compiler enforce correct usage:

extern void appendStringInfoInternal(StringInfo str, const char *fmt, ...)
    pg_attribute_printf(2, 3);
#define appendStringInfo(str, fmt, ...) \
    appendStringInfoInternal(str, fmt, __VA_ARGS__)

With C99/GCC variadic macros, appendStringInfo(buf, "matched") fails to compile because __VA_ARGS__ is empty and expands to a trailing comma with nothing after. The error message is imperfect but actionable.

Rowley's counter-concerns:

No consensus was reached on committing the macro enforcement — this remains a v20 opportunity.

The Translation Fragment Fix

Tom Lane's reply anchored the design: the helper append_tuple_value_detail() should produce (v1, v2, v3) with untranslated SQL-syntax punctuation (parentheses, commas are universal SQL notation), and callers should use complete translatable sentences with a single %s placeholder:

appendStringInfo(&err_detail,
    _("Could not find the row to be updated: %s.\n"),
    tuple_buf.data);

Vignesh picked up the implementation. Several iterations refined edge cases:

Kuroda clarified this is not a backpatch candidate — it's a translation-quality improvement, not a bug fix. Amit Kapila pushed the final v4 on 2026-05-04.

Architectural Implications

  1. Recurring maintenance burden: The thread is the third consecutive release requiring a StringInfo cleanup sweep. This argues strongly for Andres's compile-time enforcement, even if the error messages are mediocre. The cost of one ugly diagnostic beats annual manual audits across millions of lines.

  2. Translation contract: PostgreSQL's style rule "don't construct messages from parts" is not pedantry — it's a hard requirement driven by the fact that word order, punctuation spacing, and even presence of separators vary per locale. The _(".") pattern is particularly pernicious because gettext will merge identical msgids across the whole catalog, so one "." translation has to satisfy every caller — an impossibility.

  3. Node-tree output hot path: Andres's observation that WRITE_CHAR_FIELD/WRITE_FLOAT_FIELD use appendStringInfo where appendStringInfoString would suffice is the performance-relevant nugget buried here. Node serialization runs on every plan cache miss, every parallel worker handoff, and every catalog dump involving stored expression trees. Converting these avoids per-call vsnprintf traversal.

Participant Weight