diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 85d7a96..0656279 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1344,7 +1344,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 	int			sublist_length = -1;
 	bool		lateral = false;
 	RangeTblEntry *rte;
-	int			rtindex;
+	ParseNamespaceItem *nsitem;
 	ListCell   *lc;
 	ListCell   *lc2;
 	int			i;
@@ -1516,15 +1516,15 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 									  NULL, lateral, true);
 	addRTEtoQuery(pstate, rte, true, true, true);
 
-	/* assume new rte is at end */
-	rtindex = list_length(pstate->p_rtable);
-	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
+	/* grab the namespace item made by addRTEtoQuery */
+	nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
+	Assert(rte == nsitem->p_rte);
 
 	/*
 	 * Generate a targetlist as though expanding "*"
 	 */
 	Assert(pstate->p_next_resno == 1);
-	qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1);
+	qry->targetList = expandNSItemAttrs(pstate, nsitem, 0, -1);
 
 	/*
 	 * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index fe41918..ebbba2d 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -52,7 +52,8 @@
 #include "utils/syscache.h"
 
 /* Convenience macro for the most common makeNamespaceItem() case */
-#define makeDefaultNSItem(rte)	makeNamespaceItem(rte, true, true, false, true)
+#define makeDefaultNSItem(rte, rti) \
+	makeNamespaceItem(rte, rti, true, true, false, true)
 
 static void extractRemainingColumns(List *common_colnames,
 									List *src_colnames, List *src_colvars,
@@ -78,7 +79,7 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n,
 									 List **namespace);
 static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 								Var *l_colvar, Var *r_colvar);
-static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte,
+static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, int rtindex,
 											 bool rel_visible, bool cols_visible,
 											 bool lateral_only, bool lateral_ok);
 static void setNamespaceColumnVisibility(List *namespace, bool cols_visible);
@@ -216,12 +217,15 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 	rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
 										RowExclusiveLock,
 										relation->alias, inh, false);
-	pstate->p_target_rangetblentry = rte;
 
 	/* assume new rte is at end */
 	rtindex = list_length(pstate->p_rtable);
 	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 
+	/* remember the RTE as being the query target */
+	pstate->p_target_rangetblentry = rte;
+	pstate->p_target_rtindex = rtindex;
+
 	/*
 	 * Override addRangeTableEntry's default ACL_SELECT permissions check, and
 	 * instead mark target table as requiring exactly the specified
@@ -1084,7 +1088,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 		*top_rte = rte;
 		*top_rti = rtindex;
-		*namespace = list_make1(makeDefaultNSItem(rte));
+		*namespace = list_make1(makeDefaultNSItem(rte, rtindex));
 		rtr = makeNode(RangeTblRef);
 		rtr->rtindex = rtindex;
 		return (Node *) rtr;
@@ -1102,7 +1106,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 		*top_rte = rte;
 		*top_rti = rtindex;
-		*namespace = list_make1(makeDefaultNSItem(rte));
+		*namespace = list_make1(makeDefaultNSItem(rte, rtindex));
 		rtr = makeNode(RangeTblRef);
 		rtr->rtindex = rtindex;
 		return (Node *) rtr;
@@ -1120,7 +1124,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 		*top_rte = rte;
 		*top_rti = rtindex;
-		*namespace = list_make1(makeDefaultNSItem(rte));
+		*namespace = list_make1(makeDefaultNSItem(rte, rtindex));
 		rtr = makeNode(RangeTblRef);
 		rtr->rtindex = rtindex;
 		return (Node *) rtr;
@@ -1138,7 +1142,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 		*top_rte = rte;
 		*top_rti = rtindex;
-		*namespace = list_make1(makeDefaultNSItem(rte));
+		*namespace = list_make1(makeDefaultNSItem(rte, rtindex));
 		rtr = makeNode(RangeTblRef);
 		rtr->rtindex = rtindex;
 		return (Node *) rtr;
@@ -1481,6 +1485,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		 */
 		*namespace = lappend(my_namespace,
 							 makeNamespaceItem(rte,
+											   j->rtindex,
 											   (j->alias != NULL),
 											   true,
 											   false,
@@ -1617,13 +1622,15 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
  *	  Convenience subroutine to construct a ParseNamespaceItem.
  */
 static ParseNamespaceItem *
-makeNamespaceItem(RangeTblEntry *rte, bool rel_visible, bool cols_visible,
+makeNamespaceItem(RangeTblEntry *rte, int rtindex,
+				  bool rel_visible, bool cols_visible,
 				  bool lateral_only, bool lateral_ok)
 {
 	ParseNamespaceItem *nsitem;
 
 	nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
 	nsitem->p_rte = rte;
+	nsitem->p_rtindex = rtindex;
 	nsitem->p_rel_visible = rel_visible;
 	nsitem->p_cols_visible = cols_visible;
 	nsitem->p_lateral_only = lateral_only;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index eb91da2..25e92de 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -114,8 +114,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
 static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
 static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
 static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
-static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
-								  int location);
+static Node *transformWholeRowRef(ParseState *pstate,
+								  ParseNamespaceItem *nsitem,
+								  int sublevels_up, int location);
 static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
@@ -510,7 +511,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 	char	   *nspname = NULL;
 	char	   *relname = NULL;
 	char	   *colname = NULL;
-	RangeTblEntry *rte;
+	ParseNamespaceItem *nsitem;
 	int			levels_up;
 	enum
 	{
@@ -653,11 +654,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 					 * PostQUEL-inspired syntax.  The preferred form now is
 					 * "rel.*".
 					 */
-					rte = refnameRangeTblEntry(pstate, NULL, colname,
-											   cref->location,
-											   &levels_up);
-					if (rte)
-						node = transformWholeRowRef(pstate, rte,
+					nsitem = refnameNamespaceItem(pstate, NULL, colname,
+												  cref->location,
+												  &levels_up);
+					if (nsitem)
+						node = transformWholeRowRef(pstate, nsitem, levels_up,
 													cref->location);
 				}
 				break;
@@ -670,11 +671,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				Assert(IsA(field1, String));
 				relname = strVal(field1);
 
-				/* Locate the referenced RTE */
-				rte = refnameRangeTblEntry(pstate, nspname, relname,
-										   cref->location,
-										   &levels_up);
-				if (rte == NULL)
+				/* Locate the referenced nsitem */
+				nsitem = refnameNamespaceItem(pstate, nspname, relname,
+											  cref->location,
+											  &levels_up);
+				if (nsitem == NULL)
 				{
 					crerr = CRERR_NO_RTE;
 					break;
@@ -683,20 +684,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				/* Whole-row reference? */
 				if (IsA(field2, A_Star))
 				{
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					break;
 				}
 
 				Assert(IsA(field2, String));
 				colname = strVal(field2);
 
-				/* Try to identify as a column of the RTE */
-				node = scanRTEForColumn(pstate, rte, colname, cref->location,
-										0, NULL);
+				/* Try to identify as a column of the nsitem */
+				node = scanNSItemForColumn(pstate, nsitem, levels_up, colname,
+										   cref->location);
 				if (node == NULL)
 				{
 					/* Try it as a function call on the whole row */
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					node = ParseFuncOrColumn(pstate,
 											 list_make1(makeString(colname)),
 											 list_make1(node),
@@ -718,11 +721,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				Assert(IsA(field2, String));
 				relname = strVal(field2);
 
-				/* Locate the referenced RTE */
-				rte = refnameRangeTblEntry(pstate, nspname, relname,
-										   cref->location,
-										   &levels_up);
-				if (rte == NULL)
+				/* Locate the referenced nsitem */
+				nsitem = refnameNamespaceItem(pstate, nspname, relname,
+											  cref->location,
+											  &levels_up);
+				if (nsitem == NULL)
 				{
 					crerr = CRERR_NO_RTE;
 					break;
@@ -731,20 +734,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				/* Whole-row reference? */
 				if (IsA(field3, A_Star))
 				{
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					break;
 				}
 
 				Assert(IsA(field3, String));
 				colname = strVal(field3);
 
-				/* Try to identify as a column of the RTE */
-				node = scanRTEForColumn(pstate, rte, colname, cref->location,
-										0, NULL);
+				/* Try to identify as a column of the nsitem */
+				node = scanNSItemForColumn(pstate, nsitem, levels_up, colname,
+										   cref->location);
 				if (node == NULL)
 				{
 					/* Try it as a function call on the whole row */
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					node = ParseFuncOrColumn(pstate,
 											 list_make1(makeString(colname)),
 											 list_make1(node),
@@ -779,11 +784,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 					break;
 				}
 
-				/* Locate the referenced RTE */
-				rte = refnameRangeTblEntry(pstate, nspname, relname,
-										   cref->location,
-										   &levels_up);
-				if (rte == NULL)
+				/* Locate the referenced nsitem */
+				nsitem = refnameNamespaceItem(pstate, nspname, relname,
+											  cref->location,
+											  &levels_up);
+				if (nsitem == NULL)
 				{
 					crerr = CRERR_NO_RTE;
 					break;
@@ -792,20 +797,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				/* Whole-row reference? */
 				if (IsA(field4, A_Star))
 				{
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					break;
 				}
 
 				Assert(IsA(field4, String));
 				colname = strVal(field4);
 
-				/* Try to identify as a column of the RTE */
-				node = scanRTEForColumn(pstate, rte, colname, cref->location,
-										0, NULL);
+				/* Try to identify as a column of the nsitem */
+				node = scanNSItemForColumn(pstate, nsitem, levels_up, colname,
+										   cref->location);
 				if (node == NULL)
 				{
 					/* Try it as a function call on the whole row */
-					node = transformWholeRowRef(pstate, rte, cref->location);
+					node = transformWholeRowRef(pstate, nsitem, levels_up,
+												cref->location);
 					node = ParseFuncOrColumn(pstate,
 											 list_make1(makeString(colname)),
 											 list_make1(node),
@@ -2648,14 +2655,9 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b)
 static Node *
 transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
 {
-	int			sublevels_up;
-
 	/* CURRENT OF can only appear at top level of UPDATE/DELETE */
-	Assert(pstate->p_target_rangetblentry != NULL);
-	cexpr->cvarno = RTERangeTablePosn(pstate,
-									  pstate->p_target_rangetblentry,
-									  &sublevels_up);
-	Assert(sublevels_up == 0);
+	Assert(pstate->p_target_rtindex > 0);
+	cexpr->cvarno = pstate->p_target_rtindex;
 
 	/*
 	 * Check to see if the cursor name matches a parameter of type REFCURSOR.
@@ -2703,14 +2705,10 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
  * Construct a whole-row reference to represent the notation "relation.*".
  */
 static Node *
-transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
+transformWholeRowRef(ParseState *pstate, ParseNamespaceItem *nsitem,
+					 int sublevels_up, int location)
 {
 	Var		   *result;
-	int			vnum;
-	int			sublevels_up;
-
-	/* Find the RTE's rangetable location */
-	vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
 	/*
 	 * Build the appropriate referencing node.  Note that if the RTE is a
@@ -2720,13 +2718,14 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
 	 * historically.  One argument for it is that "rel" and "rel.*" mean the
 	 * same thing for composite relations, so why not for scalar functions...
 	 */
-	result = makeWholeRowVar(rte, vnum, sublevels_up, true);
+	result = makeWholeRowVar(nsitem->p_rte, nsitem->p_rtindex,
+							 sublevels_up, true);
 
 	/* location is not filled in by makeWholeRowVar */
 	result->location = location;
 
 	/* mark relation as requiring whole-row SELECT access */
-	markVarForSelectPriv(pstate, result, rte);
+	markVarForSelectPriv(pstate, result, nsitem->p_rte);
 
 	return (Node *) result;
 }
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index d9c6dc1..7efea6e 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -1913,13 +1913,15 @@ ParseComplexProjection(ParseState *pstate, const char *funcname, Node *first_arg
 	if (IsA(first_arg, Var) &&
 		((Var *) first_arg)->varattno == InvalidAttrNumber)
 	{
-		RangeTblEntry *rte;
+		ParseNamespaceItem *nsitem;
 
-		rte = GetRTEByRangeTablePosn(pstate,
-									 ((Var *) first_arg)->varno,
-									 ((Var *) first_arg)->varlevelsup);
+		nsitem = GetNSItemByRangeTablePosn(pstate,
+										   ((Var *) first_arg)->varno,
+										   ((Var *) first_arg)->varlevelsup);
 		/* Return a Var if funcname matches a column, else NULL */
-		return scanRTEForColumn(pstate, rte, funcname, location, 0, NULL);
+		return scanNSItemForColumn(pstate, nsitem,
+								   ((Var *) first_arg)->varlevelsup,
+								   funcname, location);
 	}
 
 	/*
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index bc832e7..3936b60 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -181,27 +181,6 @@ pcb_error_callback(void *arg)
 
 
 /*
- * make_var
- *		Build a Var node for an attribute identified by RTE and attrno
- */
-Var *
-make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
-{
-	Var		   *result;
-	int			vnum,
-				sublevels_up;
-	Oid			vartypeid;
-	int32		type_mod;
-	Oid			varcollid;
-
-	vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-	get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
-	result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
-	result->location = location;
-	return result;
-}
-
-/*
  * transformContainerType()
  *		Identify the types involved in a subscripting operation for container
  *
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 47188fc..4888311 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -37,14 +37,37 @@
 #include "utils/syscache.h"
 #include "utils/varlena.h"
 
+
+/*
+ * Support for fuzzily matching columns.
+ *
+ * This is for building diagnostic messages, where non-exact matching
+ * attributes are suggested to the user.  The struct's fields may be facets of
+ * a particular RTE, or of an entire range table, depending on context.
+ */
+typedef struct
+{
+	int			distance;		/* Weighted distance (lowest so far) */
+	RangeTblEntry *rfirst;		/* RTE of first */
+	AttrNumber	first;			/* Closest attribute so far */
+	RangeTblEntry *rsecond;		/* RTE of second */
+	AttrNumber	second;			/* Second closest attribute so far */
+} FuzzyAttrMatchState;
+
 #define MAX_FUZZY_DISTANCE				3
 
-static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
-											  const char *refname, int location);
-static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
-											int location);
+
+static ParseNamespaceItem *scanNameSpaceForRefname(ParseState *pstate,
+												   const char *refname,
+												   int location);
+static ParseNamespaceItem *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
+												 int location);
 static void check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
 								 int location);
+static int	scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+							 const char *colname, int location,
+							 int fuzzy_rte_penalty,
+							 FuzzyAttrMatchState *fuzzystate);
 static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
 								 int rtindex, AttrNumber col);
 static void expandRelation(Oid relid, Alias *eref,
@@ -61,28 +84,28 @@ static bool isQueryUsingTempRelation_walker(Node *node, void *context);
 
 
 /*
- * refnameRangeTblEntry
- *	  Given a possibly-qualified refname, look to see if it matches any RTE.
- *	  If so, return a pointer to the RangeTblEntry; else return NULL.
+ * refnameNamespaceItem
+ *	  Given a possibly-qualified refname, look to see if it matches any visible
+ *	  namespace item.  If so, return a pointer to the nsitem; else return NULL.
  *
- *	  Optionally get RTE's nesting depth (0 = current) into *sublevels_up.
+ *	  Optionally get nsitem's nesting depth (0 = current) into *sublevels_up.
  *	  If sublevels_up is NULL, only consider items at the current nesting
  *	  level.
  *
- * An unqualified refname (schemaname == NULL) can match any RTE with matching
+ * An unqualified refname (schemaname == NULL) can match any item with matching
  * alias, or matching unqualified relname in the case of alias-less relation
- * RTEs.  It is possible that such a refname matches multiple RTEs in the
+ * items.  It is possible that such a refname matches multiple items in the
  * nearest nesting level that has a match; if so, we report an error via
  * ereport().
  *
- * A qualified refname (schemaname != NULL) can only match a relation RTE
+ * A qualified refname (schemaname != NULL) can only match a relation item
  * that (a) has no alias and (b) is for the same relation identified by
  * schemaname.refname.  In this case we convert schemaname.refname to a
  * relation OID and search by relid, rather than by alias name.  This is
  * peculiar, but it's what SQL says to do.
  */
-RangeTblEntry *
-refnameRangeTblEntry(ParseState *pstate,
+ParseNamespaceItem *
+refnameNamespaceItem(ParseState *pstate,
 					 const char *schemaname,
 					 const char *refname,
 					 int location,
@@ -115,7 +138,7 @@ refnameRangeTblEntry(ParseState *pstate,
 
 	while (pstate != NULL)
 	{
-		RangeTblEntry *result;
+		ParseNamespaceItem *result;
 
 		if (OidIsValid(relId))
 			result = scanNameSpaceForRelid(pstate, relId, location);
@@ -136,8 +159,8 @@ refnameRangeTblEntry(ParseState *pstate,
 }
 
 /*
- * Search the query's table namespace for an RTE matching the
- * given unqualified refname.  Return the RTE if a unique match, or NULL
+ * Search the query's table namespace for an item matching the
+ * given unqualified refname.  Return the nsitem if a unique match, or NULL
  * if no match.  Raise error if multiple matches.
  *
  * Note: it might seem that we shouldn't have to worry about the possibility
@@ -152,10 +175,10 @@ refnameRangeTblEntry(ParseState *pstate,
  * this situation, and complain only if there's actually an ambiguous
  * reference to "x".
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
 {
-	RangeTblEntry *result = NULL;
+	ParseNamespaceItem *result = NULL;
 	ListCell   *l;
 
 	foreach(l, pstate->p_namespace)
@@ -179,24 +202,24 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
 								refname),
 						 parser_errposition(pstate, location)));
 			check_lateral_ref_ok(pstate, nsitem, location);
-			result = rte;
+			result = nsitem;
 		}
 	}
 	return result;
 }
 
 /*
- * Search the query's table namespace for a relation RTE matching the
- * given relation OID.  Return the RTE if a unique match, or NULL
+ * Search the query's table namespace for a relation item matching the
+ * given relation OID.  Return the nsitem if a unique match, or NULL
  * if no match.  Raise error if multiple matches.
  *
- * See the comments for refnameRangeTblEntry to understand why this
+ * See the comments for refnameNamespaceItem to understand why this
  * acts the way it does.
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
 {
-	RangeTblEntry *result = NULL;
+	ParseNamespaceItem *result = NULL;
 	ListCell   *l;
 
 	foreach(l, pstate->p_namespace)
@@ -223,7 +246,7 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
 								relid),
 						 parser_errposition(pstate, location)));
 			check_lateral_ref_ok(pstate, nsitem, location);
-			result = rte;
+			result = nsitem;
 		}
 	}
 	return result;
@@ -299,7 +322,7 @@ scanNameSpaceForENR(ParseState *pstate, const char *refname)
  *	  See if any RangeTblEntry could possibly match the RangeVar.
  *	  If so, return a pointer to the RangeTblEntry; else return NULL.
  *
- * This is different from refnameRangeTblEntry in that it considers every
+ * This is different from refnameNamespaceItem in that it considers every
  * entry in the ParseState's rangetable(s), not only those that are currently
  * visible in the p_namespace list(s).  This behavior is invalid per the SQL
  * spec, and it may give ambiguous results (there might be multiple equally
@@ -431,6 +454,8 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
  * referencing the target table of an UPDATE or DELETE as a lateral reference
  * in a FROM/USING clause.
  *
+ * Note: the pstate should be the same query level the nsitem was found in.
+ *
  * Convenience subroutine to avoid multiple copies of a rather ugly ereport.
  */
 static void
@@ -456,43 +481,35 @@ check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
 }
 
 /*
- * given an RTE, return RT index (starting with 1) of the entry,
- * and optionally get its nesting depth (0 = current).  If sublevels_up
- * is NULL, only consider rels at the current nesting level.
- * Raises error if RTE not found.
+ * Given an RT index and nesting depth, find the corresponding
+ * ParseNamespaceItem (there must be one).
  */
-int
-RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
+ParseNamespaceItem *
+GetNSItemByRangeTablePosn(ParseState *pstate,
+						  int varno,
+						  int sublevels_up)
 {
-	int			index;
-	ListCell   *l;
-
-	if (sublevels_up)
-		*sublevels_up = 0;
+	ListCell   *lc;
 
-	while (pstate != NULL)
+	while (sublevels_up-- > 0)
 	{
-		index = 1;
-		foreach(l, pstate->p_rtable)
-		{
-			if (rte == (RangeTblEntry *) lfirst(l))
-				return index;
-			index++;
-		}
 		pstate = pstate->parentParseState;
-		if (sublevels_up)
-			(*sublevels_up)++;
-		else
-			break;
+		Assert(pstate != NULL);
 	}
+	foreach(lc, pstate->p_namespace)
+	{
+		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
 
-	elog(ERROR, "RTE not found (internal error)");
-	return 0;					/* keep compiler quiet */
+		if (nsitem->p_rtindex == varno)
+			return nsitem;
+	}
+	elog(ERROR, "nsitem not found (internal error)");
+	return NULL;				/* keep compiler quiet */
 }
 
 /*
  * Given an RT index and nesting depth, find the corresponding RTE.
- * This is the inverse of RTERangeTablePosn.
+ * (Note that the RTE need not be in the query's namespace.)
  */
 RangeTblEntry *
 GetRTEByRangeTablePosn(ParseState *pstate,
@@ -512,8 +529,7 @@ GetRTEByRangeTablePosn(ParseState *pstate,
  * Fetch the CTE for a CTE-reference RTE.
  *
  * rtelevelsup is the number of query levels above the given pstate that the
- * RTE came from.  Callers that don't have this information readily available
- * may pass -1 instead.
+ * RTE came from.
  */
 CommonTableExpr *
 GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
@@ -521,10 +537,6 @@ GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
 	Index		levelsup;
 	ListCell   *lc;
 
-	/* Determine RTE's levelsup if caller didn't know it */
-	if (rtelevelsup < 0)
-		(void) RTERangeTablePosn(pstate, rte, &rtelevelsup);
-
 	Assert(rte->rtekind == RTE_CTE);
 	levelsup = rte->ctelevelsup + rtelevelsup;
 	while (levelsup-- > 0)
@@ -642,25 +654,94 @@ updateFuzzyAttrMatchState(int fuzzy_rte_penalty,
 }
 
 /*
+ * scanNSItemForColumn
+ *	  Search the column names of a single namespace item for the given name.
+ *	  If found, return an appropriate Var node, else return NULL.
+ *	  If the name proves ambiguous within this nsitem, raise error.
+ *
+ * Side effect: if we find a match, mark the item's RTE as requiring read
+ * access for the column.
+ */
+Node *
+scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
+					int sublevels_up, const char *colname, int location)
+{
+	RangeTblEntry *rte = nsitem->p_rte;
+	int			attnum;
+	Var		   *var;
+	Oid			vartypeid;
+	int32		vartypmod;
+	Oid			varcollid;
+
+	/*
+	 * Scan the RTE's column names (or aliases) for a match.  Complain if
+	 * multiple matches.
+	 */
+	attnum = scanRTEForColumn(pstate, rte,
+							  colname, location,
+							  0, NULL);
+
+	if (attnum == InvalidAttrNumber)
+		return NULL;			/* Return NULL if no match */
+
+	/* In constraint check, no system column is allowed except tableOid */
+	if (pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT &&
+		attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("system column \"%s\" reference in check constraint is invalid",
+						colname),
+				 parser_errposition(pstate, location)));
+
+	/* In generated column, no system column is allowed except tableOid */
+	if (pstate->p_expr_kind == EXPR_KIND_GENERATED_COLUMN &&
+		attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("cannot use system column \"%s\" in column generation expression",
+						colname),
+				 parser_errposition(pstate, location)));
+
+	/* Found a valid match, so build a Var */
+	get_rte_attribute_type(rte, attnum,
+						   &vartypeid, &vartypmod, &varcollid);
+	var = makeVar(nsitem->p_rtindex, attnum,
+				  vartypeid, vartypmod, varcollid,
+				  sublevels_up);
+	var->location = location;
+
+	/* Require read access to the column */
+	markVarForSelectPriv(pstate, var, rte);
+
+	return (Node *) var;
+}
+
+/*
  * scanRTEForColumn
  *	  Search the column names of a single RTE for the given name.
- *	  If found, return an appropriate Var node, else return NULL.
+ *	  If found, return the attnum (possibly negative, for a system column);
+ *	  else return InvalidAttrNumber.
  *	  If the name proves ambiguous within this RTE, raise error.
  *
- * Side effect: if we find a match, mark the RTE as requiring read access
- * for the column.
+ * pstate and location are passed only for error-reporting purposes.
  *
- * Additional side effect: if fuzzystate is non-NULL, check non-system columns
+ * Side effect: if fuzzystate is non-NULL, check non-system columns
  * for an approximate match and update fuzzystate accordingly.
+ *
+ * Note: this is factored out of scanNSItemForColumn because error message
+ * creation may want to check RTEs that are not in the namespace.  To support
+ * that usage, minimize the number of validity checks performed here.  It's
+ * okay to complain about ambiguous-name cases, though, since if we are
+ * working to complain about an invalid name, we've already eliminated that.
  */
-Node *
-scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
-				 int location, int fuzzy_rte_penalty,
+static int
+scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+				 const char *colname, int location,
+				 int fuzzy_rte_penalty,
 				 FuzzyAttrMatchState *fuzzystate)
 {
-	Node	   *result = NULL;
+	int			result = InvalidAttrNumber;
 	int			attnum = 0;
-	Var		   *var;
 	ListCell   *c;
 
 	/*
@@ -673,10 +754,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
 	 *
 	 * Should this somehow go wrong and we try to access a dropped column,
 	 * we'll still catch it by virtue of the checks in
-	 * get_rte_attribute_type(), which is called by make_var().  That routine
-	 * has to do a cache lookup anyway, so the check there is cheap.  Callers
-	 * interested in finding match with shortest distance need to defend
-	 * against this directly, though.
+	 * get_rte_attribute_type(), which is called by scanNSItemForColumn().
+	 * That routine has to do a cache lookup anyway, so the check there is
+	 * cheap.  Callers interested in finding match with shortest distance need
+	 * to defend against this directly, though.
 	 */
 	foreach(c, rte->eref->colnames)
 	{
@@ -691,13 +772,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
 						 errmsg("column reference \"%s\" is ambiguous",
 								colname),
 						 parser_errposition(pstate, location)));
-			var = make_var(pstate, rte, attnum, location);
-			/* Require read access to the column */
-			markVarForSelectPriv(pstate, var, rte);
-			result = (Node *) var;
+			result = attnum;
 		}
 
-		/* Updating fuzzy match state, if provided. */
+		/* Update fuzzy match state, if provided. */
 		if (fuzzystate != NULL)
 			updateFuzzyAttrMatchState(fuzzy_rte_penalty, fuzzystate,
 									  rte, attcolname, colname, attnum);
@@ -720,39 +798,13 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
 	{
 		/* quick check to see if name could be a system column */
 		attnum = specialAttNum(colname);
-
-		/* In constraint check, no system column is allowed except tableOid */
-		if (pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT &&
-			attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-					 errmsg("system column \"%s\" reference in check constraint is invalid",
-							colname),
-					 parser_errposition(pstate, location)));
-
-		/*
-		 * In generated column, no system column is allowed except tableOid.
-		 */
-		if (pstate->p_expr_kind == EXPR_KIND_GENERATED_COLUMN &&
-			attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-					 errmsg("cannot use system column \"%s\" in column generation expression",
-							colname),
-					 parser_errposition(pstate, location)));
-
 		if (attnum != InvalidAttrNumber)
 		{
 			/* now check to see if column actually is defined */
 			if (SearchSysCacheExists2(ATTNUM,
 									  ObjectIdGetDatum(rte->relid),
 									  Int16GetDatum(attnum)))
-			{
-				var = make_var(pstate, rte, attnum, location);
-				/* Require read access to the column */
-				markVarForSelectPriv(pstate, var, rte);
-				result = (Node *) var;
-			}
+				result = attnum;
 		}
 	}
 
@@ -771,6 +823,7 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
 			 int location)
 {
 	Node	   *result = NULL;
+	int			sublevels_up = 0;
 	ParseState *orig_pstate = pstate;
 
 	while (pstate != NULL)
@@ -780,7 +833,6 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
 		foreach(l, pstate->p_namespace)
 		{
 			ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
-			RangeTblEntry *rte = nsitem->p_rte;
 			Node	   *newresult;
 
 			/* Ignore table-only items */
@@ -790,9 +842,9 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
 			if (nsitem->p_lateral_only && !pstate->p_lateral_active)
 				continue;
 
-			/* use orig_pstate here to get the right sublevels_up */
-			newresult = scanRTEForColumn(orig_pstate, rte, colname, location,
-										 0, NULL);
+			/* use orig_pstate here for consistency with other callers */
+			newresult = scanNSItemForColumn(orig_pstate, nsitem, sublevels_up,
+											colname, location);
 
 			if (newresult)
 			{
@@ -811,6 +863,7 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
 			break;				/* found, or don't want to look at parent */
 
 		pstate = pstate->parentParseState;
+		sublevels_up++;
 	}
 
 	return result;
@@ -2182,9 +2235,23 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
 			  bool addToJoinList,
 			  bool addToRelNameSpace, bool addToVarNameSpace)
 {
+	int			rtindex;
+
+	/*
+	 * Most callers have just added the RTE to the rangetable, so it's likely
+	 * to be the last entry.  Hence, it's a good idea to search the rangetable
+	 * back-to-front.
+	 */
+	for (rtindex = list_length(pstate->p_rtable); rtindex > 0; rtindex--)
+	{
+		if (rte == rt_fetch(rtindex, pstate->p_rtable))
+			break;
+	}
+	if (rtindex <= 0)
+		elog(ERROR, "RTE not found (internal error)");
+
 	if (addToJoinList)
 	{
-		int			rtindex = RTERangeTablePosn(pstate, rte, NULL);
 		RangeTblRef *rtr = makeNode(RangeTblRef);
 
 		rtr->rtindex = rtindex;
@@ -2196,6 +2263,7 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
 
 		nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
 		nsitem->p_rte = rte;
+		nsitem->p_rtindex = rtindex;
 		nsitem->p_rel_visible = addToRelNameSpace;
 		nsitem->p_cols_visible = addToVarNameSpace;
 		nsitem->p_lateral_only = false;
@@ -2653,26 +2721,25 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
 }
 
 /*
- * expandRelAttrs -
+ * expandNSItemAttrs -
  *	  Workhorse for "*" expansion: produce a list of targetentries
- *	  for the attributes of the RTE
+ *	  for the attributes of the nsitem
  *
- * As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup
- * fields of the Vars produced, and location sets their location.
  * pstate->p_next_resno determines the resnos assigned to the TLEs.
  * The referenced columns are marked as requiring SELECT access.
  */
 List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte,
-			   int rtindex, int sublevels_up, int location)
+expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
+				  int sublevels_up, int location)
 {
+	RangeTblEntry *rte = nsitem->p_rte;
 	List	   *names,
 			   *vars;
 	ListCell   *name,
 			   *var;
 	List	   *te_list = NIL;
 
-	expandRTE(rte, rtindex, sublevels_up, location, false,
+	expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false,
 			  &names, &vars);
 
 	/*
@@ -3253,7 +3320,6 @@ void
 errorMissingRTE(ParseState *pstate, RangeVar *relation)
 {
 	RangeTblEntry *rte;
-	int			sublevels_up;
 	const char *badAlias = NULL;
 
 	/*
@@ -3274,11 +3340,17 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation)
 	 * MySQL-ism "SELECT ... FROM a, b LEFT JOIN c ON (a.x = c.y)".
 	 */
 	if (rte && rte->alias &&
-		strcmp(rte->eref->aliasname, relation->relname) != 0 &&
-		refnameRangeTblEntry(pstate, NULL, rte->eref->aliasname,
-							 relation->location,
-							 &sublevels_up) == rte)
-		badAlias = rte->eref->aliasname;
+		strcmp(rte->eref->aliasname, relation->relname) != 0)
+	{
+		ParseNamespaceItem *nsitem;
+		int			sublevels_up;
+
+		nsitem = refnameNamespaceItem(pstate, NULL, rte->eref->aliasname,
+									  relation->location,
+									  &sublevels_up);
+		if (nsitem && nsitem->p_rte == rte)
+			badAlias = rte->eref->aliasname;
+	}
 
 	if (rte)
 		ereport(ERROR,
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 30d419e..46fe6c6 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -62,8 +62,9 @@ static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 static List *ExpandAllTables(ParseState *pstate, int location);
 static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
 								   bool make_target_entry, ParseExprKind exprKind);
-static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
-							   int location, bool make_target_entry);
+static List *ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
+							   int sublevels_up, int location,
+							   bool make_target_entry);
 static List *ExpandRowReference(ParseState *pstate, Node *expr,
 								bool make_target_entry);
 static int	FigureColnameInternal(Node *node, char **name);
@@ -548,10 +549,13 @@ transformAssignedExpr(ParseState *pstate,
 			/*
 			 * Build a Var for the column to be updated.
 			 */
-			colVar = (Node *) make_var(pstate,
-									   pstate->p_target_rangetblentry,
-									   attrno,
-									   location);
+			Var		   *var;
+
+			var = makeVar(pstate->p_target_rtindex, attrno,
+						  attrtype, attrtypmod, attrcollation, 0);
+			var->location = location;
+
+			colVar = (Node *) var;
 		}
 
 		expr = (Expr *)
@@ -1127,7 +1131,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 		 */
 		char	   *nspname = NULL;
 		char	   *relname = NULL;
-		RangeTblEntry *rte = NULL;
+		ParseNamespaceItem *nsitem = NULL;
 		int			levels_up;
 		enum
 		{
@@ -1153,16 +1157,16 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 		{
 			case 2:
 				relname = strVal(linitial(fields));
-				rte = refnameRangeTblEntry(pstate, nspname, relname,
-										   cref->location,
-										   &levels_up);
+				nsitem = refnameNamespaceItem(pstate, nspname, relname,
+											  cref->location,
+											  &levels_up);
 				break;
 			case 3:
 				nspname = strVal(linitial(fields));
 				relname = strVal(lsecond(fields));
-				rte = refnameRangeTblEntry(pstate, nspname, relname,
-										   cref->location,
-										   &levels_up);
+				nsitem = refnameNamespaceItem(pstate, nspname, relname,
+											  cref->location,
+											  &levels_up);
 				break;
 			case 4:
 				{
@@ -1178,9 +1182,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 					}
 					nspname = strVal(lsecond(fields));
 					relname = strVal(lthird(fields));
-					rte = refnameRangeTblEntry(pstate, nspname, relname,
-											   cref->location,
-											   &levels_up);
+					nsitem = refnameNamespaceItem(pstate, nspname, relname,
+												  cref->location,
+												  &levels_up);
 					break;
 				}
 			default:
@@ -1193,17 +1197,19 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 		 * bit by passing the RangeTblEntry, not a Var, as the planned
 		 * translation.  (A single Var wouldn't be strictly correct anyway.
 		 * This convention allows hooks that really care to know what is
-		 * happening.)
+		 * happening.  It might be better to pass the nsitem, but we'd have to
+		 * promote that struct to a full-fledged Node type so that callees
+		 * could identify its type.)
 		 */
 		if (pstate->p_post_columnref_hook != NULL)
 		{
 			Node	   *node;
 
 			node = pstate->p_post_columnref_hook(pstate, cref,
-												 (Node *) rte);
+												 (Node *) (nsitem ? nsitem->p_rte : NULL));
 			if (node != NULL)
 			{
-				if (rte != NULL)
+				if (nsitem != NULL)
 					ereport(ERROR,
 							(errcode(ERRCODE_AMBIGUOUS_COLUMN),
 							 errmsg("column reference \"%s\" is ambiguous",
@@ -1216,7 +1222,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 		/*
 		 * Throw error if no translation found.
 		 */
-		if (rte == NULL)
+		if (nsitem == NULL)
 		{
 			switch (crserr)
 			{
@@ -1242,9 +1248,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 		}
 
 		/*
-		 * OK, expand the RTE into fields.
+		 * OK, expand the nsitem into fields.
 		 */
-		return ExpandSingleTable(pstate, rte, cref->location, make_target_entry);
+		return ExpandSingleTable(pstate, nsitem, levels_up, cref->location,
+								 make_target_entry);
 	}
 }
 
@@ -1269,7 +1276,6 @@ ExpandAllTables(ParseState *pstate, int location)
 	foreach(l, pstate->p_namespace)
 	{
 		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
-		RangeTblEntry *rte = nsitem->p_rte;
 
 		/* Ignore table-only items */
 		if (!nsitem->p_cols_visible)
@@ -1280,12 +1286,10 @@ ExpandAllTables(ParseState *pstate, int location)
 		found_table = true;
 
 		target = list_concat(target,
-							 expandRelAttrs(pstate,
-											rte,
-											RTERangeTablePosn(pstate, rte,
-															  NULL),
-											0,
-											location));
+							 expandNSItemAttrs(pstate,
+											   nsitem,
+											   0,
+											   location));
 	}
 
 	/*
@@ -1341,26 +1345,21 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
  * The referenced columns are marked as requiring SELECT access.
  */
 static List *
-ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
-				  int location, bool make_target_entry)
+ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
+				  int sublevels_up, int location, bool make_target_entry)
 {
-	int			sublevels_up;
-	int			rtindex;
-
-	rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
 	if (make_target_entry)
 	{
-		/* expandRelAttrs handles permissions marking */
-		return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
-							  location);
+		/* expandNSItemAttrs handles permissions marking */
+		return expandNSItemAttrs(pstate, nsitem, sublevels_up, location);
 	}
 	else
 	{
+		RangeTblEntry *rte = nsitem->p_rte;
 		List	   *vars;
 		ListCell   *l;
 
-		expandRTE(rte, rtindex, sublevels_up, location, false,
+		expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false,
 				  NULL, &vars);
 
 		/*
@@ -1411,10 +1410,10 @@ ExpandRowReference(ParseState *pstate, Node *expr,
 		((Var *) expr)->varattno == InvalidAttrNumber)
 	{
 		Var		   *var = (Var *) expr;
-		RangeTblEntry *rte;
+		ParseNamespaceItem *nsitem;
 
-		rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
-		return ExpandSingleTable(pstate, rte, var->location, make_target_entry);
+		nsitem = GetNSItemByRangeTablePosn(pstate, var->varno, var->varlevelsup);
+		return ExpandSingleTable(pstate, nsitem, var->varlevelsup, var->location, make_target_entry);
 	}
 
 	/*
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 7c099e7..674acc5 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -134,6 +134,8 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
  *
  * p_target_rangetblentry: target relation's entry in the rtable list.
  *
+ * p_target_rtindex: target relation's index in the rtable list.
+ *
  * p_is_insert: true to process assignment expressions like INSERT, false
  * to process them like UPDATE.  (Note this can change intra-statement, for
  * cases like INSERT ON CONFLICT UPDATE.)
@@ -185,7 +187,8 @@ struct ParseState
 	List	   *p_future_ctes;	/* common table exprs not yet in namespace */
 	CommonTableExpr *p_parent_cte;	/* this query's containing CTE */
 	Relation	p_target_relation;	/* INSERT/UPDATE/DELETE target rel */
-	RangeTblEntry *p_target_rangetblentry;	/* target rel's RTE */
+	RangeTblEntry *p_target_rangetblentry;	/* target rel's RTE, or NULL */
+	int			p_target_rtindex;	/* target rel's RT index, or 0 */
 	bool		p_is_insert;	/* process assignment like INSERT not UPDATE */
 	List	   *p_windowdefs;	/* raw representations of window clauses */
 	ParseExprKind p_expr_kind;	/* what kind of expression we're parsing */
@@ -249,6 +252,7 @@ struct ParseState
 typedef struct ParseNamespaceItem
 {
 	RangeTblEntry *p_rte;		/* The relation's rangetable entry */
+	int			p_rtindex;		/* The relation's index in the rangetable */
 	bool		p_rel_visible;	/* Relation name is visible? */
 	bool		p_cols_visible; /* Column names visible as unqualified refs? */
 	bool		p_lateral_only; /* Is only visible to LATERAL expressions? */
@@ -272,8 +276,6 @@ extern void setup_parser_errposition_callback(ParseCallbackState *pcbstate,
 											  ParseState *pstate, int location);
 extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
 
-extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
-					 int location);
 extern Oid	transformContainerType(Oid *containerType, int32 *containerTypmod);
 
 extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index f7e0781..b09a71e 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -17,45 +17,28 @@
 #include "parser/parse_node.h"
 
 
-/*
- * Support for fuzzily matching column.
- *
- * This is for building diagnostic messages, where non-exact matching
- * attributes are suggested to the user.  The struct's fields may be facets of
- * a particular RTE, or of an entire range table, depending on context.
- */
-typedef struct
-{
-	int			distance;		/* Weighted distance (lowest so far) */
-	RangeTblEntry *rfirst;		/* RTE of first */
-	AttrNumber	first;			/* Closest attribute so far */
-	RangeTblEntry *rsecond;		/* RTE of second */
-	AttrNumber	second;			/* Second closest attribute so far */
-} FuzzyAttrMatchState;
-
-
-extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
-										   const char *schemaname,
-										   const char *refname,
-										   int location,
-										   int *sublevels_up);
+extern ParseNamespaceItem *refnameNamespaceItem(ParseState *pstate,
+												const char *schemaname,
+												const char *refname,
+												int location,
+												int *sublevels_up);
 extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
 											const char *refname,
 											Index *ctelevelsup);
 extern bool scanNameSpaceForENR(ParseState *pstate, const char *refname);
 extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
 									List *namespace2);
-extern int	RTERangeTablePosn(ParseState *pstate,
-							  RangeTblEntry *rte,
-							  int *sublevels_up);
+extern ParseNamespaceItem *GetNSItemByRangeTablePosn(ParseState *pstate,
+													 int varno,
+													 int sublevels_up);
 extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
 											 int varno,
 											 int sublevels_up);
 extern CommonTableExpr *GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte,
 									 int rtelevelsup);
-extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
-							  const char *colname, int location,
-							  int fuzzy_rte_penalty, FuzzyAttrMatchState *fuzzystate);
+extern Node *scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
+								 int sublevels_up, const char *colname,
+								 int location);
 extern Node *colNameToVar(ParseState *pstate, const char *colname, bool localonly,
 						  int location);
 extern void markVarForSelectPriv(ParseState *pstate, Var *var,
@@ -122,8 +105,8 @@ extern void errorMissingColumn(ParseState *pstate,
 extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
 					  int location, bool include_dropped,
 					  List **colnames, List **colvars);
-extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte,
-							int rtindex, int sublevels_up, int location);
+extern List *expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
+							   int sublevels_up, int location);
 extern int	attnameAttNum(Relation rd, const char *attname, bool sysColOK);
 extern const NameData *attnumAttName(Relation rd, int attid);
 extern Oid	attnumTypeId(Relation rd, int attid);
