diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 2ffe47026b..7e70cc8fbd 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -94,6 +94,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, * *must* know that to avoid possibly calling hide_coercion_node on * something that wasn't generated by coerce_type. Note that if there are * multiple stacked CollateExprs, we just discard all but the topmost. + * Also, if the target type isn't collatable, we discard the CollateExpr. */ origexpr = expr; while (expr && IsA(expr, CollateExpr)) @@ -113,7 +114,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, ccontext, cformat, location, (result != expr && !IsA(result, Const))); - if (expr != origexpr) + if (expr != origexpr && type_is_collatable(targettype)) { /* Reinstall top CollateExpr */ CollateExpr *coll = (CollateExpr *) origexpr; @@ -385,20 +386,26 @@ coerce_type(ParseState *pstate, Node *node, { /* * If we have a COLLATE clause, we have to push the coercion - * underneath the COLLATE. This is really ugly, but there is little - * choice because the above hacks on Consts and Params wouldn't happen + * underneath the COLLATE; or discard the COLLATE if the target type + * isn't collatable. This is really ugly, but there is little choice + * because the above hacks on Consts and Params wouldn't happen * otherwise. This kluge has consequences in coerce_to_target_type. */ CollateExpr *coll = (CollateExpr *) node; - CollateExpr *newcoll = makeNode(CollateExpr); - newcoll->arg = (Expr *) - coerce_type(pstate, (Node *) coll->arg, - inputTypeId, targetTypeId, targetTypeMod, - ccontext, cformat, location); - newcoll->collOid = coll->collOid; - newcoll->location = coll->location; - return (Node *) newcoll; + result = coerce_type(pstate, (Node *) coll->arg, + inputTypeId, targetTypeId, targetTypeMod, + ccontext, cformat, location); + if (type_is_collatable(targetTypeId)) + { + CollateExpr *newcoll = makeNode(CollateExpr); + + newcoll->arg = (Expr *) result; + newcoll->collOid = coll->collOid; + newcoll->location = coll->location; + result = (Node *) newcoll; + } + return result; } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out index c42ab8f703..0b60b55514 100644 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -688,13 +688,26 @@ SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1)); "C" (1 row) +-- old bug with not dropping COLLATE when coercing to non-collatable type +CREATE VIEW collate_on_int AS +SELECT c1+1 AS c1p FROM + (SELECT ('4' COLLATE "C")::INT AS c1) ss; +\d+ collate_on_int + View "collate_tests.collate_on_int" + Column | Type | Collation | Nullable | Default | Storage | Description +--------+---------+-----------+----------+---------+---------+------------- + c1p | integer | | | | plain | +View definition: + SELECT ss.c1 + 1 AS c1p + FROM ( SELECT 4 AS c1) ss; + -- -- Clean up. Many of these table names will be re-used if the user is -- trying to run any platform-specific collation tests later, so we -- must get rid of them. -- DROP SCHEMA collate_tests CASCADE; -NOTICE: drop cascades to 18 other objects +NOTICE: drop cascades to 19 other objects DETAIL: drop cascades to table collate_test1 drop cascades to table collate_test_like drop cascades to table collate_test2 @@ -713,3 +726,4 @@ drop cascades to table collate_test21 drop cascades to table collate_test22 drop cascades to collation mycoll2 drop cascades to table collate_test23 +drop cascades to view collate_on_int diff --git a/src/test/regress/sql/collate.sql b/src/test/regress/sql/collate.sql index 82f9c855b8..30f811ba89 100644 --- a/src/test/regress/sql/collate.sql +++ b/src/test/regress/sql/collate.sql @@ -266,6 +266,12 @@ SELECT collation for ('foo'::text); SELECT collation for ((SELECT a FROM collate_test1 LIMIT 1)); -- non-collatable type - error SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1)); +-- old bug with not dropping COLLATE when coercing to non-collatable type +CREATE VIEW collate_on_int AS +SELECT c1+1 AS c1p FROM + (SELECT ('4' COLLATE "C")::INT AS c1) ss; +\d+ collate_on_int + -- -- Clean up. Many of these table names will be re-used if the user is