Fix coerce_to_target_type for coerce_type's klugy handling of COLLATE.

Because coerce_type recurses into the argument of a CollateExpr,
coerce_to_target_type's longstanding code for detecting whether coerce_type
had actually done anything (to wit, returned a different node than it
passed in) was broken in 9.1.  This resulted in unexpected failures in
hide_coercion_node; which was not the latter's fault, since it's critical
that we never call it on anything that wasn't inserted by coerce_type.
(Else we might decide to "hide" a user-written function call.)

Fix by removing and replacing the CollateExpr in coerce_to_target_type
itself.  This is all pretty ugly but I don't immediately see a way to make
it nicer.

Per report from Jean-Yves F. Barbier.
This commit is contained in:
Tom Lane 2012-01-02 14:43:45 -05:00
parent a8ab8d0eaa
commit ac7a5a3f25
3 changed files with 35 additions and 1 deletions

View File

@ -79,10 +79,24 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
int location)
{
Node *result;
Node *origexpr;
if (!can_coerce_type(1, &exprtype, &targettype, ccontext))
return NULL;
/*
* If the input has a CollateExpr at the top, strip it off, perform the
* coercion, and put a new one back on. This is annoying since it
* duplicates logic in coerce_type, but if we don't do this then it's too
* hard to tell whether coerce_type actually changed anything, and we
* *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.
*/
origexpr = expr;
while (expr && IsA(expr, CollateExpr))
expr = (Node *) ((CollateExpr *) expr)->arg;
result = coerce_type(pstate, expr, exprtype,
targettype, targettypmod,
ccontext, cformat, location);
@ -98,6 +112,18 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
(cformat != COERCE_IMPLICIT_CAST),
(result != expr && !IsA(result, Const)));
if (expr != origexpr)
{
/* Reinstall top CollateExpr */
CollateExpr *coll = (CollateExpr *) origexpr;
CollateExpr *newcoll = makeNode(CollateExpr);
newcoll->arg = (Expr *) result;
newcoll->collOid = coll->collOid;
newcoll->location = coll->location;
result = (Node *) newcoll;
}
return result;
}
@ -318,7 +344,7 @@ 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
* otherwise.
* otherwise. This kluge has consequences in coerce_to_target_type.
*/
CollateExpr *coll = (CollateExpr *) node;
CollateExpr *newcoll = makeNode(CollateExpr);

View File

@ -574,6 +574,9 @@ ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20;
RESET enable_seqscan;
RESET enable_hashjoin;
RESET enable_nestloop;
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
CREATE TEMP TABLE vctable (f1 varchar(25));
INSERT INTO vctable VALUES ('foo' COLLATE "C");
--
-- 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

View File

@ -214,6 +214,11 @@ RESET enable_seqscan;
RESET enable_hashjoin;
RESET enable_nestloop;
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
CREATE TEMP TABLE vctable (f1 varchar(25));
INSERT INTO vctable VALUES ('foo' COLLATE "C");
--
-- 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