Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 27011bc

Browse files
committed
Fix old bug with coercing the result of a COLLATE expression.
There are hacks in parse_coerce.c to push down a requested coercion to below any CollateExpr that may appear. However, we did that even if the requested data type is non-collatable, leading to an invalid expression tree in which CollateExpr is applied to a non-collatable type. The fix is just to drop the CollateExpr altogether, reasoning that it's useless. This bug is ten years old, dating to the original addition of COLLATE support. The lack of field complaints suggests that there aren't a lot of user-visible consequences. We noticed the problem because it would trigger an assertion in DefineVirtualRelation if the invalid structure appears as an output column of a view; however, in a non-assert build, you don't see a crash just a (subtly incorrect) complaint about applying collation to a non-collatable type. I found that by putting the incorrect structure further down in a view, I could make a view definition that would fail dump/reload, per the added regression test case. But CollateExpr doesn't do anything at run-time, so this likely doesn't lead to any really exciting consequences. Per report from Yulin Pei. Back-patch to all supported branches. Discussion: https://postgr.es/m/HK0PR01MB22744393C474D503E16C8509F4709@HK0PR01MB2274.apcprd01.prod.exchangelabs.com
1 parent 82dd570 commit 27011bc

File tree

3 files changed

+39
-12
lines changed

3 files changed

+39
-12
lines changed

src/backend/parser/parse_coerce.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
9595
* *must* know that to avoid possibly calling hide_coercion_node on
9696
* something that wasn't generated by coerce_type. Note that if there are
9797
* multiple stacked CollateExprs, we just discard all but the topmost.
98+
* Also, if the target type isn't collatable, we discard the CollateExpr.
9899
*/
99100
origexpr = expr;
100101
while (expr && IsA(expr, CollateExpr))
@@ -114,7 +115,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
114115
ccontext, cformat, location,
115116
(result != expr && !IsA(result, Const)));
116117

117-
if (expr != origexpr)
118+
if (expr != origexpr && type_is_collatable(targettype))
118119
{
119120
/* Reinstall top CollateExpr */
120121
CollateExpr *coll = (CollateExpr *) origexpr;
@@ -382,20 +383,26 @@ coerce_type(ParseState *pstate, Node *node,
382383
{
383384
/*
384385
* If we have a COLLATE clause, we have to push the coercion
385-
* underneath the COLLATE. This is really ugly, but there is little
386-
* choice because the above hacks on Consts and Params wouldn't happen
386+
* underneath the COLLATE; or discard the COLLATE if the target type
387+
* isn't collatable. This is really ugly, but there is little choice
388+
* because the above hacks on Consts and Params wouldn't happen
387389
* otherwise. This kluge has consequences in coerce_to_target_type.
388390
*/
389391
CollateExpr *coll = (CollateExpr *) node;
390-
CollateExpr *newcoll = makeNode(CollateExpr);
391392

392-
newcoll->arg = (Expr *)
393-
coerce_type(pstate, (Node *) coll->arg,
394-
inputTypeId, targetTypeId, targetTypeMod,
395-
ccontext, cformat, location);
396-
newcoll->collOid = coll->collOid;
397-
newcoll->location = coll->location;
398-
return (Node *) newcoll;
393+
result = coerce_type(pstate, (Node *) coll->arg,
394+
inputTypeId, targetTypeId, targetTypeMod,
395+
ccontext, cformat, location);
396+
if (type_is_collatable(targetTypeId))
397+
{
398+
CollateExpr *newcoll = makeNode(CollateExpr);
399+
400+
newcoll->arg = (Expr *) result;
401+
newcoll->collOid = coll->collOid;
402+
newcoll->location = coll->location;
403+
result = (Node *) newcoll;
404+
}
405+
return result;
399406
}
400407
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
401408
&funcId);

src/test/regress/expected/collate.out

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,13 +676,26 @@ SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1));
676676
"C"
677677
(1 row)
678678

679+
-- old bug with not dropping COLLATE when coercing to non-collatable type
680+
CREATE VIEW collate_on_int AS
681+
SELECT c1+1 AS c1p FROM
682+
(SELECT ('4' COLLATE "C")::INT AS c1) ss;
683+
\d+ collate_on_int
684+
View "collate_tests.collate_on_int"
685+
Column | Type | Collation | Nullable | Default | Storage | Description
686+
--------+---------+-----------+----------+---------+---------+-------------
687+
c1p | integer | | | | plain |
688+
View definition:
689+
SELECT ss.c1 + 1 AS c1p
690+
FROM ( SELECT 4 AS c1) ss;
691+
679692
--
680693
-- Clean up. Many of these table names will be re-used if the user is
681694
-- trying to run any platform-specific collation tests later, so we
682695
-- must get rid of them.
683696
--
684697
DROP SCHEMA collate_tests CASCADE;
685-
NOTICE: drop cascades to 17 other objects
698+
NOTICE: drop cascades to 18 other objects
686699
DETAIL: drop cascades to table collate_test1
687700
drop cascades to table collate_test_like
688701
drop cascades to table collate_test2
@@ -700,3 +713,4 @@ drop cascades to table collate_test21
700713
drop cascades to table collate_test22
701714
drop cascades to collation mycoll2
702715
drop cascades to table collate_test23
716+
drop cascades to view collate_on_int

src/test/regress/sql/collate.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ SELECT collation for ('foo'::text);
258258
SELECT collation for ((SELECT a FROM collate_test1 LIMIT 1)); -- non-collatable type - error
259259
SELECT collation for ((SELECT b FROM collate_test1 LIMIT 1));
260260

261+
-- old bug with not dropping COLLATE when coercing to non-collatable type
262+
CREATE VIEW collate_on_int AS
263+
SELECT c1+1 AS c1p FROM
264+
(SELECT ('4' COLLATE "C")::INT AS c1) ss;
265+
\d+ collate_on_int
266+
261267

262268
--
263269
-- Clean up. Many of these table names will be re-used if the user is

0 commit comments

Comments
 (0)