Commit a73b7561 authored by Tom Lane's avatar Tom Lane

transformExpr() did the Wrong Thing if applied to a SubLink node that

had already been transformed.  This led to failure in examples like
UPDATE table SET fld = (SELECT ...).  Repair this, and revise the
comments to explain that transformExpr has to be robust against this
condition.  Someday we might want to fix the callers so that
transformExpr is never invoked on its own output, but that someday
is not today.
parent 52d02657
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.74 2000/03/17 05:29:05 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.75 2000/03/19 07:13:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -61,10 +61,27 @@ parse_expr_init(void) ...@@ -61,10 +61,27 @@ parse_expr_init(void)
/* /*
* transformExpr - * transformExpr -
* analyze and transform expressions. Type checking and type casting is * Analyze and transform expressions. Type checking and type casting is
* done here. The optimizer and the executor cannot handle the original * done here. The optimizer and the executor cannot handle the original
* (raw) expressions collected by the parse tree. Hence the transformation * (raw) expressions collected by the parse tree. Hence the transformation
* here. * here.
*
* NOTE: there are various cases in which this routine will get applied to
* an already-transformed expression. Some examples:
* 1. At least one construct (BETWEEN/AND) puts the same nodes
* into two branches of the parse tree; hence, some nodes
* are transformed twice.
* 2. Another way it can happen is that coercion of an operator or
* function argument to the required type (via coerce_type())
* can apply transformExpr to an already-transformed subexpression.
* An example here is "SELECT count(*) + 1.0 FROM table".
* While it might be possible to eliminate these cases, the path of
* least resistance so far has been to ensure that transformExpr() does
* no damage if applied to an already-transformed tree. This is pretty
* easy for cases where the transformation replaces one node type with
* another, such as A_Const => Const; we just do nothing when handed
* a Const. More care is needed for node types that are used as both
* input and output of transformExpr; see SubLink for example.
*/ */
Node * Node *
transformExpr(ParseState *pstate, Node *expr, int precedence) transformExpr(ParseState *pstate, Node *expr, int precedence)
...@@ -254,6 +271,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) ...@@ -254,6 +271,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
List *qtrees; List *qtrees;
Query *qtree; Query *qtree;
/* If we already transformed this node, do nothing */
if (IsA(sublink->subselect, Query))
{
result = expr;
break;
}
pstate->p_hasSubLinks = true; pstate->p_hasSubLinks = true;
qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate); qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate);
if (length(qtrees) != 1) if (length(qtrees) != 1)
...@@ -390,7 +413,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) ...@@ -390,7 +413,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
/* /*
* It's not shorthand anymore, so drop the implicit * It's not shorthand anymore, so drop the implicit
* argument. This is necessary to keep the executor from * argument. This is necessary to keep the executor from
* seeing an untransformed expression... * seeing an untransformed expression... not to mention
* keeping a re-application of transformExpr from doing
* the wrong thing.
*/ */
c->arg = NULL; c->arg = NULL;
...@@ -528,18 +553,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) ...@@ -528,18 +553,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break; break;
} }
/* Some nodes do _not_ come from the original parse tree, /*
* but result from parser transformation in this phase. * Quietly accept node types that may be presented when we are called
* At least one construct (BETWEEN/AND) puts the same nodes * on an already-transformed tree.
* into two branches of the parse tree; hence, some nodes *
* are transformed twice.
* Another way it can happen is that coercion of an operator or
* function argument to the required type (via coerce_type())
* can apply transformExpr to an already-transformed subexpression.
* An example here is "SELECT count(*) + 1.0 FROM table".
* Thus, we can see node types in this routine that do not appear in the
* original parse tree. Assume they are already transformed, and just
* pass them through.
* Do any other node types need to be accepted? For now we are taking * Do any other node types need to be accepted? For now we are taking
* a conservative approach, and only accepting node types that are * a conservative approach, and only accepting node types that are
* demonstrably necessary to accept. * demonstrably necessary to accept.
...@@ -555,6 +572,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) ...@@ -555,6 +572,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
result = (Node *) expr; result = (Node *) expr;
break; break;
} }
default: default:
/* should not reach here */ /* should not reach here */
elog(ERROR, "transformExpr: does not know how to transform node %d" elog(ERROR, "transformExpr: does not know how to transform node %d"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment