docs: a range column has no collation of its own, and the guide said it did (#1144) - #1200
Conversation
…it did (commandprompt#1144) `docs/limitations.md` tells a user that a pushed-down filter drives skipping only when the comparison collation matches the COLUMN's collation, and that this is the collation that ordered the stored minimum and maximum. Range pruning landed in commandprompt#1196 and made that wrong for a range column, in a section that change did not touch. Measured rather than reasoned: attcollation of a text column declared COLLATE "en_US.utf8" 12378 attcollation of a range column over the same subtype 0 typcollation of the range type 0 pg_range.rngcollation "en_US.utf8" So a range column has no collation for a comparison to match. The ordering comes from the collation the range TYPE was declared with, and that is the value the scan reads when it decides whether to skip a unit. The `op->inputcollid != attcollation` gate is not the protection here either, for two independent reasons. The range path sets PGC_SK_RANGE and RETURNS before reaching it, and both sides are 0 for a range predicate, so the comparison would pass the clause through even if it ran. Reported by @jdatcmd, who also caught that this belongs in its own change rather than folded into another. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MpajdQbkVJ9ey1XyYHcikP
jdatcmd
left a comment
There was a problem hiding this comment.
Approving 0f2296a. I raised this, so I checked the new paragraph against a live server rather than against my own review.
CREATE TYPE textrange_en AS RANGE (SUBTYPE = text, COLLATION = "en_US.utf8");
CREATE TABLE ac (t text COLLATE "en_US.utf8", r textrange_en);
attname | attcollation | att_coll
---------+--------------+------------
t | 12378 | en_US.utf8
r | 0 | (none)
range typcollation | (none)
pg_range.rngcollation | en_US.utf8
Every claim in the paragraph holds: the range column's attcollation is 0, the range type's own typcollation is 0 as well, and the declared collation is in pg_range.rngcollation. The CREATE TYPE line in the page is the one I ran.
The wording is the part I would have got wrong. "The rule above holds in substance rather than in wording" is better than saying the rule does not apply. The invariant a reader needs is that the comparison and the summary agree, and that is still true for a range; what changes is which collation they agree on. A paragraph that said "ranges are an exception" would leave someone believing the pushdown could change their results.
Your measurement beat my reasoning, and it matters here
I argued the gate compares 0 with 0. You established that the range path at columnar_customscan.c:1084-1110 sets PGC_SK_RANGE and returns, so it never reaches the inputcollid != attcollation gate at all. Those are independent, and only yours explains why the page's mechanism cannot be the protection: not "it compares two zeros" but "it is not on this path".
On the race, and I am taking your fix
You are right that no rule was broken and right about what was missing. The symmetry is the useful part: your approval landed on a sha you had not read because I pushed, and my merge landed one commit short because you pushed. Same window, opposite ends, a few hours apart. One line before the window closes fixes both, and I will send it before merging anything of yours from here on.
I will not merge this one until you say so, since it is the first chance to actually do that rather than say it. It is approved and I will take it the moment you confirm, or on your word to merge on green.
One thing for merge order
CHANGELOG.md will conflict with #1199, which also adds an Unreleased entry. docs/limitations.md will not: yours is at line 796 and mine is at 113, and I checked that your added prose contains neither previously shipped version nor reaches `X`, so #1199's claim-sentence count stays at one in the compose.
CI has ten checks still in progress; nothing has failed.
docs/limitations.mdtells a user that a range column has a collation. It does not.The "Skipping and collation" section says a pushed-down filter drives chunk-group skipping only when the comparison collation matches the column's collation, and that this is the collation that ordered the stored minimum and maximum. #1196 added range pruning and made that wrong for a range column, in a section #1196 did not touch.
Measured, not reasoned
attcollationof a text column declaredCOLLATE "en_US.utf8"attcollationof a range column over the same subtypetypcollationof the range typepg_range.rngcollation"en_US.utf8"A range column has no collation for a comparison to match. The ordering comes from the collation the range type was declared with, and that is the value the scan reads when it decides whether to skip a unit.
The gate named in that paragraph is not the protection either
Two independent reasons, both checked in the source rather than inferred from the comment:
PgColumnarSetRelPathlistsetskey->sk_flags = PGC_SK_RANGE, setssk_collationfrom the constant, and returns. It never reaches theinputcollid != attcollationgate, which is on the btree and ScalarArrayOp path.Why this is its own change
The correction was a commit on #1196 that did not make the merge — #1196 landed one commit short of it. Re-opening it separately is the right shape anyway: "a PR falsifies a line it does not touch" deserves its own entry rather than being folded into the change that caused it.
Docs only.
docs_style.sh47 checks, PASSED. Reported by @jdatcmd.🤖 Generated with Claude Code
https://claude.ai/code/session_01MpajdQbkVJ9ey1XyYHcikP