Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno - Mailing list pgsql-hackers

I wrote:
> I started to think a little harder about the rough ideas I sketched
> yesterday in [1] about making the planner deal with outer joins in
> a less ad-hoc manner.
> [1] https://www.postgresql.org/message-id/7771.1576452845%40sss.pgh.pa.us

After further study, the idea of using join alias vars to track
outer-join semantics basically doesn't work at all.  Join alias vars in
their current form (ie, references to the output columns of a JOIN RTE)
aren't suitable for the purpose of representing possibly-nulled inputs
to that same RTE.  There are two big stumbling blocks:

* In the presence of JOIN USING, we don't necessarily have a JOIN output
column that is equivalent to either input column.  The output is
definitely not equal to the nullable side of an OJ, since it won't go to
NULL; and it might not be equivalent to the non-nullable side either,
because JOIN USING might've coerced it to some common datatype.

* We also don't have any output column that could represent a whole-row
reference to either input table.  I thought about representing that with
a RowExpr of join output Vars, but that fails to preserve the existing
semantics: a whole-row reference to the nullable side goes to NULL, not
to a row of NULLs, when we're null-extending the join.

So that kind of crashed and burned.  We could maybe fake it by inventing
some new conventions about magic attnums of the join RTE that correspond
to the values we want, but that seems really messy and error-prone.

The alternatives that seem plausible at this point are

(1) Create some sort of wrapper node indicating "the contents of this
expression might be replaced by NULL".  This is basically what the
planner's PlaceHolderVars do, so maybe we'd just be talking about
introducing those at some earlier stage.

(2) Explicitly mark Vars as being nullable by some outer join.  I think
we could probably get this down to one additional integer field in
struct Var, so it wouldn't be too much of a penalty.

The wrapper approach is more general since you can wrap something
that's not necessarily a plain Var; but it's also bulkier and so
probably a bit less efficient.  I'm not sure which idea I like better.

With either approach, we could either make parse analysis inject the
nullability markings, or wait to do it in the planner.  On a purely
abstract system structural level, I like the former better: it is
exactly the province of parse analysis to decide what are the semantics
of what the user typed, and surely what a Var means is part of that.
OTOH, if we do it that way, the planner potentially has to rearrange the
markings after it does join strength reduction; so maybe it's best to
just wait till after that planning phase to address this at all.

Any thoughts about that?

Anyway, I had started to work on getting parse analysis to label
outer-join-nullable Vars properly, and soon decided that no matter how
we do it, there's not enough information available at the point where
parse analysis makes a Var.  The referenced RTE is not, in itself,
enough info, and I don't think we want to decorate RTEs with more info
that's only needed during parse analysis.  What would be saner is to add
any extra info to the ParseNamespaceItem structs.  But that requires
some refactoring to allow the ParseNamespaceItems, not just the
referenced RTEs, to be passed down through Var lookup/construction.
So attached is a patch that refactors things that way.  As proof of
concept, I added the rangetable index to ParseNamespaceItem, and used
that to get rid of the RTERangeTablePosn() searches that we formerly had
in a bunch of places.  Now, RTERangeTablePosn() isn't likely to be all
that expensive, but still this should be a little faster and cleaner.
Also, I was able to confine the fuzzy-lookup heuristic stuff to within
parse_relation.c instead of letting it bleed out to the rest of the
parser.

This seems to me to be good cleanup regardless of whether we ever
ask parse analysis to label outer-join-nullable Vars.  So, barring
objection, I'd like to push it soon.

            regards, tom lane

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);

pgsql-hackers by date:

Previous
From: Mark Lorenz
Date:
Subject: Re: Created feature for to_date() conversion using patterns'YYYY-WW', 'YYYY-WW-D', 'YYYY-MM-W' and 'YYYY-MM-W-D'
Next
From: Robert Haas
Date:
Subject: Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno