Technical Analysis: tablecmds.c recurse vs recursing Parameter Naming Clarity
Core Problem
PostgreSQL's tablecmds.c is one of the largest and most complex files in the codebase, implementing all ALTER TABLE subcommands. Many of its internal functions accept two boolean parameters — recurse and recursing — whose names are visually similar but semantically distinct:
-
recurse: Indicates whether the command should propagate to child tables. This is derived from the SQL syntax:truewhenONLYwas not specified (i.e., the user wants the operation to apply to the entire inheritance/partition hierarchy). It affects both inherited tables and partitioned tables. -
recursing: An internal flag indicating that the current function invocation is itself a recursive call from a parent table's processing. This only affects inherited table logic (not partitioning per se).
The confusion arises because:
- The names differ by only a suffix (
-evs-ing) - There is virtually no documentation in
tablecmds.cexplaining these parameters - All four boolean combinations are theoretically possible, making the state space non-obvious
Proposed Solutions
v1: Rename recurse to no_only (Rejected)
The original proposal renamed recurse to no_only to directly reflect its SQL-level meaning (the absence of the ONLY keyword). This was rejected by both Peter Eisentraut and Tom Lane because:
no_onlyis essentially a double negative ("not ONLY" → "do include children"), which harms readability- The existing names, while confusable, are at least positively-worded
- Renaming touches many functions for marginal benefit
v2: Header comments + scattered references (Partially accepted)
The author pivoted to adding documentation. v2 added detailed descriptions in the header comments of ATController() and ATPrepCmd(), plus brief references in 16 other functions that take both parameters. Tom Lane objected to the "partial documentation" approach for other functions.
v3/v4: Focused header comment on ATPrepCmd() (Accepted approach)
Following Tom Lane's guidance, the final approach documents all arguments of ATPrepCmd() comprehensively in its header comment, and relies on developers extrapolating that understanding to other functions. This avoids:
- Incomplete documentation scattered across 16+ functions
- The maintenance burden of keeping many duplicated comments in sync
A minor addition documents the previously-undocumented is_internal parameter of addFkConstraint(), squashed into the same commit.
Key Technical Insights
The Four-State Boolean Space
Ashutosh Bapat's analysis of the (recurse, recursing) combinations is architecturally revealing:
| recurse | recursing | Meaning |
|---|---|---|
| true | false | Top-level call, will recurse to children (root of hierarchy) |
| false | false | Operating on a standalone table (no inheritance/partition context) |
| true | true | Mid-tree recursion: processing a non-leaf, non-root table that should continue recursing |
| false | true | Processing a child table but should NOT recurse further to its children |
Tom Lane noted that the (false, true) case would arise if the inheritance tree were flattened into a list early on — but most code paths that flatten the tree don't use recurse/recursing parameters at all. This suggests potential for simplification via an enum, though no one pursued this.
Architectural Context
The recurse/recursing pattern is central to how PostgreSQL handles DDL propagation in inheritance hierarchies. When ALTER TABLE parent ALTER COLUMN ... is issued without ONLY:
ATPrepCmd()is called withrecurse=true, recursing=false- It identifies child tables and calls itself (or the execution function) with
recurse=true, recursing=true - The
recursing=trueflag triggers different validation logic (e.g., inherited columns may have different constraints on what can be altered)
This two-boolean design is a pragmatic encoding of the call-stack context that avoids threading additional state through deeply nested function calls.
Design Philosophy Discussion
Tom Lane's position reveals a broader PostgreSQL development philosophy: documentation should be complete or absent — partial documentation of function parameters is worse than none because it creates a false sense of completeness and becomes a maintenance liability when signatures change. The preferred approach is to document one canonical location thoroughly and trust developers to read it.
Current Status
The patch (v5, rebased with no substantive changes from v4) is a pure comment addition to ATPrepCmd() documenting all its parameters. It remains uncommitted as of the last message (May 2026), with the author bumping it due to its continued utility while working on related inheritance/recursion patches.