diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ad98b364f13..456887665a0 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -13341,6 +13341,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, x->args = args; /* xmloption, if relevant, must be filled in by caller */ /* type and typmod will be filled in during parse analysis */ + x->type = InvalidOid; /* marks the node as not analyzed */ x->location = location; return (Node *) x; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index e9267c56fc5..3b1b6d0139c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -92,6 +92,8 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname, * 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". + * 3. CREATE TABLE t1 (LIKE t2 INCLUDING INDEXES) can pass in + * already-transformed index expressions. * 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 @@ -1775,11 +1777,17 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, static Node * transformRowExpr(ParseState *pstate, RowExpr *r) { - RowExpr *newr = makeNode(RowExpr); + RowExpr *newr; char fname[16]; int fnum; ListCell *lc; + /* If we already transformed this node, do nothing */ + if (OidIsValid(r->row_typeid)) + return (Node *) r; + + newr = makeNode(RowExpr); + /* Transform the field expressions */ newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind); @@ -1880,16 +1888,23 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) static Node * transformXmlExpr(ParseState *pstate, XmlExpr *x) { - XmlExpr *newx = makeNode(XmlExpr); + XmlExpr *newx; ListCell *lc; int i; + /* If we already transformed this node, do nothing */ + if (OidIsValid(x->type)) + return (Node *) x; + + newx = makeNode(XmlExpr); newx->op = x->op; if (x->name) newx->name = map_sql_identifier_to_xml_name(x->name, false, false); else newx->name = NULL; newx->xmloption = x->xmloption; + newx->type = XMLOID; /* this just marks the node as transformed */ + newx->typmod = -1; newx->location = x->location; /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index a233fce003e..5eb5f8b4cd7 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -958,7 +958,8 @@ typedef struct MinMaxExpr * * Note: result type/typmod/collation are not stored, but can be deduced * from the XmlExprOp. The type/typmod fields are just used for display - * purposes, and are NOT the true result type of the node. + * purposes, and are NOT necessarily the true result type of the node. + * (We also use type == InvalidOid to mark a not-yet-parse-analyzed XmlExpr.) */ typedef enum XmlExprOp {