diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 3e1f210652d..61202774e9d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -3262,13 +3262,23 @@ eval_const_expressions_mutator(Node *node, /* Copy the node and const-simplify its arguments */ expr = (NullIfExpr *) ece_generic_processing(node); - /* If either argument is NULL they can't be equal */ + /* + * If either argument is NULL they can't be equal, so the + * result is the first argument, relabeled if needed to expose + * the NULLIF's typmod and collation + */ foreach(arg, expr->args) { if (!IsA(lfirst(arg), Const)) has_nonconst_input = true; else if (((Const *) lfirst(arg))->constisnull) - return (Node *) linitial(expr->args); + return applyRelabelType((Node *) linitial(expr->args), + expr->opresulttype, + exprTypmod(node), + expr->opcollid, + COERCE_IMPLICIT_CAST, + -1, + false); } /* @@ -3750,10 +3760,17 @@ eval_const_expressions_mutator(Node *node, /* * If no non-FALSE alternatives, CASE reduces to the default - * result + * result, relabeled if needed to expose the CASE's typmod and + * collation */ if (newargs == NIL) - return defresult; + return applyRelabelType(defresult, + caseexpr->casetype, + exprTypmod(node), + caseexpr->casecollid, + COERCE_IMPLICIT_CAST, + -1, + false); /* Otherwise we need a new CASE node */ newcase = makeNode(CaseExpr); newcase->casetype = caseexpr->casetype; @@ -3820,26 +3837,19 @@ eval_const_expressions_mutator(Node *node, /* * We can remove null constants from the list. For a - * nonnullable expression, if it has not been preceded by - * any non-null-constant expressions then it is the - * result. Otherwise, it's the next argument, but we can - * drop following arguments since they will never be - * reached. + * nonnullable expression, we can drop following arguments + * since they will never be reached. */ if (IsA(e, Const)) { if (((Const *) e)->constisnull) continue; /* drop null constant */ - if (newargs == NIL) - return e; /* first expr */ newargs = lappend(newargs, e); break; } if (expr_is_nonnullable(context->root, (Expr *) e, NOTNULL_SOURCE_HASHTABLE)) { - if (newargs == NIL) - return e; /* first expr */ newargs = lappend(newargs, e); break; } @@ -3858,10 +3868,18 @@ eval_const_expressions_mutator(Node *node, /* * If there's exactly one surviving argument, we no longer - * need COALESCE at all: the result is that argument + * need COALESCE at all: the result is that argument, + * relabeled if needed to expose the COALESCE's typmod and + * collation */ if (list_length(newargs) == 1) - return (Node *) linitial(newargs); + return applyRelabelType((Node *) linitial(newargs), + coalesceexpr->coalescetype, + exprTypmod(node), + coalesceexpr->coalescecollid, + COERCE_IMPLICIT_CAST, + -1, + false); newcoalesce = makeNode(CoalesceExpr); newcoalesce->coalescetype = coalesceexpr->coalescetype; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 5ec848b82e4..69b2f5109ad 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -3860,6 +3860,31 @@ SELECT a, b FROM groupby_collation_t GROUP BY a, b ORDER BY a, b; (2 rows) DROP TABLE groupby_collation_t; +-- +-- An expression that is simplified to one of its inputs must keep its own +-- collation. +-- +CREATE TABLE simplify_collation_t (a text COLLATE "C" NOT NULL, b text); +INSERT INTO simplify_collation_t VALUES ('foo'), ('FOO'); +SELECT count(*) FROM (SELECT COALESCE(a, b COLLATE case_insensitive) FROM simplify_collation_t GROUP BY 1); + count +------- + 1 +(1 row) + +SELECT count(*) FROM (SELECT CASE WHEN true THEN a ELSE b COLLATE case_insensitive END FROM simplify_collation_t GROUP BY 1); + count +------- + 1 +(1 row) + +SELECT count(*) FROM (SELECT NULLIF(a, NULL::text COLLATE case_insensitive) FROM simplify_collation_t GROUP BY 1); + count +------- + 1 +(1 row) + +DROP TABLE simplify_collation_t; -- virtual generated columns CREATE TABLE t5 ( a int, diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index b50cf3f6c7c..87cea263780 100644 --- a/src/test/regress/sql/collate.icu.utf8.sql +++ b/src/test/regress/sql/collate.icu.utf8.sql @@ -1492,6 +1492,19 @@ SELECT a, b FROM groupby_collation_t GROUP BY a, b ORDER BY a, b; DROP TABLE groupby_collation_t; +-- +-- An expression that is simplified to one of its inputs must keep its own +-- collation. +-- +CREATE TABLE simplify_collation_t (a text COLLATE "C" NOT NULL, b text); +INSERT INTO simplify_collation_t VALUES ('foo'), ('FOO'); + +SELECT count(*) FROM (SELECT COALESCE(a, b COLLATE case_insensitive) FROM simplify_collation_t GROUP BY 1); +SELECT count(*) FROM (SELECT CASE WHEN true THEN a ELSE b COLLATE case_insensitive END FROM simplify_collation_t GROUP BY 1); +SELECT count(*) FROM (SELECT NULLIF(a, NULL::text COLLATE case_insensitive) FROM simplify_collation_t GROUP BY 1); + +DROP TABLE simplify_collation_t; + -- virtual generated columns CREATE TABLE t5 ( a int,