Re: Wrong results from inner-unique joins caused by collation mismatch
Richard Guo <guofenglinux@gmail.com>
From: Richard Guo <guofenglinux@gmail.com>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Pg Hackers <pgsql-hackers@lists.postgresql.org>
Date: 2026-04-24T15:44:32Z
Lists: pgsql-hackers
On Fri, Apr 24, 2026 at 11:53 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Richard Guo <guofenglinux@gmail.com> writes: > > My first thought was to fix this by: > > > + if (!IndexCollMatchesExprColl(ind->indexcollations[c], > > + exprInputCollation((Node *) rinfo->clause))) > > + continue; > > > However, this caused an unexpected plan diff in join.out where a > > left-join removal over (name, text) stopped working, because name and > > text use different collations. So this check is too strict: a > > mismatch between two deterministic collations should be OK for > > uniqueness proof, as a deterministic collation treats two strings as > > equal iff they are byte-wise equal (see CREATE COLLATION). > Yes, we'd be taking a serious performance hit if we insisted on > exact collation matches for this purpose. I agree that disallowing > non-matching non-deterministic collations is the right fix. Thanks for taking a look! > > Hence, I got attached patch. Thoughts? > I don't love doing it like this, for two reasons: > > 1. I think there are other places in the planner that will need > substantially this same logic. I recommend breaking out a > subroutine defined more or less as "do these collations have > equivalent notions of equality". Right. I just found several other places that need this same logic, which are in query_is_distinct_for(). Without it, we produce wrong results: select * from t t1 join (select distinct a from t) t2 on t1.a = t2.a COLLATE "ci"; a | a ---+--- A | a a | a (2 rows) select * from t t1 join (select a from t group by a) t2 on t1.a = t2.a COLLATE "ci"; a | a ---+--- A | a a | a (2 rows) > 2. I find the test next to unreadable as written --- for example, > it's more difficult than it should be to figure out what happens > if one collation is deterministic and the other not. Using a > subroutine would help here by letting you break down the test > into multiple steps. Agreed. Will wrap the logic in a subroutine. - Richard
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Consider collation when proving subquery uniqueness
- 172034f6e088 14.23 landed
- bab4f7fa5621 15.18 landed
- 5a24cef082a0 16.14 landed
- 13226050e85d 17.10 landed
- bed3ffbf9d95 18.4 landed
- 574581b50ac9 19 (unreleased) landed
-
Consider collation when proving uniqueness from unique indexes
- 8395446dff08 14.23 landed
- 872c9fae78bc 15.18 landed
- 748fe9e6085c 16.14 landed
- d0e73bb18017 17.10 landed
- b62f514ac533 18.4 landed
- 5a55ea507a2d 19 (unreleased) landed