docs: defects get fixed or measured away, not written down and kept#165
Conversation
ChronicallyJD
left a comment
There was a problem hiding this comment.
Approving, and I am the immediate example of why this rule is needed — I did exactly the thing it forbids, today, on #164.
I gave the projected fetch a Bitmapset where NULL meant "every column", discovered that a Bitmapset cannot distinguish empty from NULL so a computed set that came out empty silently asked for the opposite, and then wrote a careful comment at both declarations explaining the hazard and calling it "the safe direction". A comment warning a reader about behaviour we control is a record of having left it in place. The fix was an explicit bool allColumns flag — the ambiguity now cannot be expressed rather than being annotated. Pushed as dea3c65 before reading this PR, which is coincidence, but it does make the point better than agreeing with it would.
The rule is right, and the second branch is the part people skip
"Fix it, or measure it and show it is not a defect" is the important formulation. The second is a real resolution and it usually gets treated as a lesser one, when it is often the more useful outcome: it closes the question permanently and stops the next person re-deriving it.
ColumnarDeleteVectorBufferedDeleted is a good demonstration, and it is my finding, so I will say plainly that I was wrong about it twice over. I flagged the nested-scan shape from reading it. You measured it and found #143 instead. Now you have implemented the obvious fix and shown it makes things slower — 317.6 ms against 299.2, 652.2 against 624.1 — because the chunk list one transaction accumulates stays short enough that the extra branch costs more than the walk it skips, and doubling the table doubles the time either way, so the term is linear.
Not shipping the patch is the right call, and recording the numbers is what makes it a resolution rather than a deferral. "Reasoning from shape" is how I found it and measurement is what settled it; the audit doc now says so.
Verified the ANALYZE rewrite rather than reading it
Since limitations.md now makes positive claims about my code, I checked them on an assert build, 200,000 rows, columnar against a heap mirror:
| columnar | heap | |
|---|---|---|
histogram_bounds present |
yes | yes |
| histogram buckets | 101 | 101 |
most_common_vals present |
yes | yes |
avg_width |
> 0 | > 0 |
correlation present |
yes | yes |
So "the same set it collects for a heap table" holds literally, down to the bucket count. The correlation paragraph is the one worth having: it is the statistic that makes vacuum_sorted and Z-ordering legible to the planner, and it was very nearly lost — my first revision of #159 concluded it was unreachable by that route and said so in the PR body.
The reltuples paragraph matches what I measured independently: 5% low at 500,000 rows and 1.3% at 1,000,000, and the reason given (blocks holding no row-group data count as visited while offering no rows) is the right one. "The planner does not use that figure for columnar tables" is also correct — columnar_relation_estimate_size supplies rel->tuples from row-group metadata regardless.
"Sweep the docs in the same change"
This is the part I would have got wrong again without it being written down. #159 merged and limitations.md still described ANALYZE as collecting nothing; I did not think to look, because the docs were not in my diff. A fix that leaves the documentation contradicting it is worse than either state alone — a reader now has two sources and no way to know which is current.
Worth noting that it generalises past limitations.md: design/ plans have the same property, and #156 is currently carrying a claim about correlation that #159 disproved.
One small thing
"It is not a parking space" is doing a lot of work in that paragraph and I would keep it. The distinction it draws — external constraint versus nobody-has-fixed-it — is the whole test, and it is the sentence someone will quote back in review.
Verdict
Approving. Documentation only, the two claims about my code check out against measurement, and the rule is one I needed today.
Owner's rule: "we don't document bugs and keep them. we document bugs and fix them." Two had accumulated here, and both are now closed. ANALYZE collecting no column statistics was resolved by the July audit as "documented rather than changed" and sat in docs/limitations.md until #159 implemented sampling. That section now describes what the code does, including correlation, which is the statistic that makes vacuum_sorted and Z-order legible to the planner. It also records that reltuples runs a few percent low and that the planner does not use it, rather than leaving a reader to wonder. ColumnarDeleteVectorBufferedDeleted is the other kind of resolution. The audit listed it as retaining the nested-scan shape that #134 fixed next door. I implemented that same last-chunk probe and measured it: 317.6 ms against 299.2 ms without, and doubling the table doubles the time either way, so the term is linear and the extra branch costs more than the walk it skips. The shape is real and the cost is not. Recording the numbers closes it; carrying a patch that buys nothing would not have, so the patch is not here. The rule itself goes in docs/testing.md, next to the differential-oracle and matrix sections, because it is the same class of thing: how this project decides something is done. It says what limitations.md is for (external constraints an extension cannot fix) and what it is not for (defects waiting on someone), and to sweep the docs in the same change as the fix, since ANALYZE read as a limitation for hours after the implementation merged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uKWWwBDt5TWWS5DR2tzDb
b702433 to
6814386
Compare
Standing rule, applied to the two defects that were sitting documented.
The rule
"We don't document bugs and keep them. We document bugs and fix them."
A limitation written into the docs stops reading as a defect. It becomes a design choice, people plan around it, and nobody reopens it. Filing an issue is tracking, not resolving: an issue with no change behind it is a defect the project has decided to keep.
Written into
docs/testing.mdbeside the differential-oracle and matrix sections, because it is the same class of thing, how this project decides something is done. It states whatdocs/limitations.mdis for (constraints an extension genuinely cannot fix: WAL behaviour, missing APIs on PG13/14) and what it is not for.The two that had accumulated
ANALYZEcollecting no statistics. Resolved by the July audit as "documented rather than changed", and it sat inlimitations.mduntil #159 implemented sampling. That section now describes what the code does, including correlation, and records thatreltuplesruns a few percent low and that the planner does not use it. It read as a limitation for hours after the implementation merged, which is why the rule now says to sweep the docs in the same change as the fix.ColumnarDeleteVectorBufferedDeleted. The audit listed it as keeping the nested-scan shape that #134 fixed next door. This is the other legitimate resolution, and the interesting one: I implemented the same last-chunk probe and measured it.Twice the table costs twice the time either way, so the term is linear, and the extra branch costs more than the walk it skips because the chunk list one transaction accumulates stays short. The shape is real and the cost is not. Recording the numbers closes it; carrying a patch that buys nothing would not, so the patch is not in this PR.
Measuring something and finding it is not a defect is a resolution. Writing it down and leaving it is not.
Scope
Documentation only, no code.
git difftouchesdocs/limitations.md,docs/testing.mdanddesign/EXTERNAL_AUDIT_2026_07.md.