diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ad98b364f138000ca6723caa19e869f39b267303..456887665a046e4a104c9a7047c64ee3d562ca48 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 e9267c56fc5e9c5b577dbc3ab9c32455405f68dc..3b1b6d0139c581e10c8024e7f600edf72cd7c94f 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 a233fce003e2eafd5f4dfe3f5750a7e8be1ec535..5eb5f8b4cd79cf6e840570ed9e513d943087d7c3 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
 {