From 5077b82d77d429da6d582f67c5bd7ee686e2428e Mon Sep 17 00:00:00 2001
From: William Bernbaum <wbernbaum@dwdev.com>
Date: Wed, 26 Aug 2026 20:46:56 -0700
Subject: [PATCH v2 17/17] Test deduplication under non-deterministic collation

---
 .../regress/expected/collate.icu.utf8.out     | 95 +++++++++++++++++++
 src/test/regress/sql/collate.icu.utf8.sql     | 55 +++++++++++
 2 files changed, 150 insertions(+)

diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index fcfcc658bea..c1c4c3812b1 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -3648,6 +3648,101 @@ GROUP BY t1.id, t1.val;
 DROP TABLE eager_agg_t1;
 DROP TABLE eager_agg_t2;
 --
+-- Test that a deduplication is not pushed below a join on such a grouping
+-- key either.  It would decide which of two equal values survives, and equal
+-- values are distinguishable under this collation.
+--
+CREATE TABLE eager_dedup_d (id int,
+							ci text COLLATE case_insensitive,
+							c text COLLATE "C");
+CREATE TABLE eager_dedup_f (id int, d_id int);
+INSERT INTO eager_dedup_d
+  SELECT i, CASE WHEN i % 2 = 0 THEN 'a' ELSE 'A' END, 'c' || i
+    FROM generate_series(1, 100) i;
+INSERT INTO eager_dedup_f
+  SELECT i, i % 100 + 1 FROM generate_series(1, 2000) i;
+ANALYZE eager_dedup_d;
+ANALYZE eager_dedup_f;
+-- Twenty rows of f per row of d, so a deduplication would pay
+EXPLAIN (COSTS OFF)
+SELECT DISTINCT d.ci
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+                  QUERY PLAN                   
+-----------------------------------------------
+ HashAggregate
+   Group Key: d.ci
+   ->  Hash Join
+         Hash Cond: (f.d_id = d.id)
+         ->  Seq Scan on eager_dedup_f f
+         ->  Hash
+               ->  Seq Scan on eager_dedup_d d
+(7 rows)
+
+SELECT count(*) FROM (
+  SELECT DISTINCT d.ci
+    FROM eager_dedup_d d
+    JOIN eager_dedup_f f ON f.d_id = d.id) s;
+ count 
+-------
+     1
+(1 row)
+
+-- The same holds when a DISTINCT aggregate supplies the key
+EXPLAIN (COSTS OFF)
+SELECT count(DISTINCT d.ci)
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+                     QUERY PLAN                      
+-----------------------------------------------------
+ Aggregate
+   ->  Sort
+         Sort Key: d.ci COLLATE case_insensitive
+         ->  Hash Join
+               Hash Cond: (f.d_id = d.id)
+               ->  Seq Scan on eager_dedup_f f
+               ->  Hash
+                     ->  Seq Scan on eager_dedup_d d
+(8 rows)
+
+SELECT count(DISTINCT d.ci)
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+ count 
+-------
+     1
+(1 row)
+
+-- The same query on the deterministic column does push a deduplication down
+EXPLAIN (COSTS OFF)
+SELECT DISTINCT d.c
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+                     QUERY PLAN                      
+-----------------------------------------------------
+ HashAggregate
+   Group Key: d.c
+   ->  Hash Join
+         Hash Cond: (d.id = f.d_id)
+         ->  Seq Scan on eager_dedup_d d
+         ->  Hash
+               ->  Partial HashAggregate
+                     Group Key: f.d_id
+                     ->  Seq Scan on eager_dedup_f f
+(9 rows)
+
+SELECT count(*) FROM (
+  SELECT DISTINCT d.c
+    FROM eager_dedup_d d
+    JOIN eager_dedup_f f ON f.d_id = d.id) s;
+ count 
+-------
+   100
+(1 row)
+
+DROP TABLE eager_dedup_d;
+DROP TABLE eager_dedup_f;
+--
 -- A unique index can prove functional dependency for GROUP BY column
 -- removal only if its per-column collation agrees on equality with
 -- the GROUP BY column's collation.  An index built under a different
diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql
index ce4e2bb3ffd..c36da89db31 100644
--- a/src/test/regress/sql/collate.icu.utf8.sql
+++ b/src/test/regress/sql/collate.icu.utf8.sql
@@ -1355,6 +1355,61 @@ GROUP BY t1.id, t1.val;
 DROP TABLE eager_agg_t1;
 DROP TABLE eager_agg_t2;
 
+--
+-- Test that a deduplication is not pushed below a join on such a grouping
+-- key either.  It would decide which of two equal values survives, and equal
+-- values are distinguishable under this collation.
+--
+
+CREATE TABLE eager_dedup_d (id int,
+							ci text COLLATE case_insensitive,
+							c text COLLATE "C");
+CREATE TABLE eager_dedup_f (id int, d_id int);
+
+INSERT INTO eager_dedup_d
+  SELECT i, CASE WHEN i % 2 = 0 THEN 'a' ELSE 'A' END, 'c' || i
+    FROM generate_series(1, 100) i;
+INSERT INTO eager_dedup_f
+  SELECT i, i % 100 + 1 FROM generate_series(1, 2000) i;
+
+ANALYZE eager_dedup_d;
+ANALYZE eager_dedup_f;
+
+-- Twenty rows of f per row of d, so a deduplication would pay
+EXPLAIN (COSTS OFF)
+SELECT DISTINCT d.ci
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+
+SELECT count(*) FROM (
+  SELECT DISTINCT d.ci
+    FROM eager_dedup_d d
+    JOIN eager_dedup_f f ON f.d_id = d.id) s;
+
+-- The same holds when a DISTINCT aggregate supplies the key
+EXPLAIN (COSTS OFF)
+SELECT count(DISTINCT d.ci)
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+
+SELECT count(DISTINCT d.ci)
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+
+-- The same query on the deterministic column does push a deduplication down
+EXPLAIN (COSTS OFF)
+SELECT DISTINCT d.c
+  FROM eager_dedup_d d
+  JOIN eager_dedup_f f ON f.d_id = d.id;
+
+SELECT count(*) FROM (
+  SELECT DISTINCT d.c
+    FROM eager_dedup_d d
+    JOIN eager_dedup_f f ON f.d_id = d.id) s;
+
+DROP TABLE eager_dedup_d;
+DROP TABLE eager_dedup_f;
+
 --
 -- A unique index can prove functional dependency for GROUP BY column
 -- removal only if its per-column collation agrees on equality with
