diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e7d6ff1b687..349a912427a 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -2233,9 +2233,18 @@ compute_semijoin_info(PlannerInfo *root, SpecialJoinInfo *sjinfo, List *clause) if (!(all_btree || all_hash)) return; + /* + * Ensure the RHS expression exposes the join's input collation (its + * type should be OK already); see comments for + * canonicalize_ec_expression. + */ + right_expr = (Node *) canonicalize_ec_expression((Expr *) copyObject(right_expr), + exprType(right_expr), + op->inputcollid); + /* so far so good, keep building lists */ semi_operators = lappend_oid(semi_operators, opno); - semi_rhs_exprs = lappend(semi_rhs_exprs, copyObject(right_expr)); + semi_rhs_exprs = lappend(semi_rhs_exprs, right_expr); } /* Punt if we didn't find at least one column to unique-ify */ diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 0fd5c1a2cb0..8b294c1b73c 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -2104,6 +2104,60 @@ SELECT string_to_array('ABCDEFGHI' COLLATE case_insensitive, NULL, 'b'); {A,NULL,C,D,E,F,G,H,I} (1 row) +-- Unique-ifying a semijoin's RHS must use the join's collation. test3cs +-- holds both 'abc' and 'ABC', so test1ci's 'abc' must come out once. +BEGIN; +SET LOCAL enable_seqscan TO off; +SET LOCAL enable_material TO off; +SET LOCAL enable_hashjoin TO off; +SET LOCAL enable_mergejoin TO off; +SET LOCAL enable_hashagg TO off; +EXPLAIN (COSTS OFF) +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + QUERY PLAN +---------------------------------------------------------------------------- + Aggregate + -> Nested Loop + -> Unique + -> Sort + Sort Key: ((test3cs.x)::text) COLLATE case_insensitive + -> Index Only Scan using test3cs_x_idx on test3cs + -> Index Only Scan using test1ci_x_idx on test1ci + Index Cond: (x = (test3cs.x)::text) +(8 rows) + +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + count +------- + 3 +(1 row) + +SET LOCAL enable_hashagg TO on; +SET LOCAL enable_sort TO off; +EXPLAIN (COSTS OFF) +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + QUERY PLAN +------------------------------------------------------------------ + Aggregate + -> Nested Loop + -> HashAggregate + Group Key: (test3cs.x)::text + -> Index Only Scan using test3cs_x_idx on test3cs + -> Index Only Scan using test1ci_x_idx on test1ci + Index Cond: (x = (test3cs.x)::text) +(7 rows) + +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + count +------- + 3 +(1 row) + +ROLLBACK; -- These queries should be able to use the index on test1ci.x: SET enable_seqscan = off; SET enable_indexonlyscan = off; diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index b091f05c113..2b38c640428 100644 --- a/src/test/regress/sql/collate.icu.utf8.sql +++ b/src/test/regress/sql/collate.icu.utf8.sql @@ -747,6 +747,33 @@ CREATE UNIQUE INDEX ON test3ci (x); -- error SELECT string_to_array('ABC,DEF,GHI' COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_insensitive, NULL, 'b'); +-- Unique-ifying a semijoin's RHS must use the join's collation. test3cs +-- holds both 'abc' and 'ABC', so test1ci's 'abc' must come out once. +BEGIN; + +SET LOCAL enable_seqscan TO off; +SET LOCAL enable_material TO off; +SET LOCAL enable_hashjoin TO off; +SET LOCAL enable_mergejoin TO off; +SET LOCAL enable_hashagg TO off; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + +SET LOCAL enable_hashagg TO on; +SET LOCAL enable_sort TO off; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); +SELECT count(*) FROM test1ci +WHERE x COLLATE case_insensitive IN (SELECT x FROM test3cs); + +ROLLBACK; + -- These queries should be able to use the index on test1ci.x: SET enable_seqscan = off; SET enable_indexonlyscan = off;