Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno
Дата
Msg-id 2461.1577764221@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Clarifying/rationalizing Vars' varno/varattno/varnoold/varoattno  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> 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.

Here is a further step on this journey.  It's still just parser
refactoring, and doesn't (AFAICT) result in any change in generated
parse trees, but it seems worth posting and committing separately.

The two key ideas here are:

1. Integrate ParseNamespaceItems a bit further into the parser's
relevant APIs.  In particular, the addRangeTableEntryXXX functions
no longer return just a bare RTE, but a ParseNamespaceItem wrapper
for it.  This gets rid of a number of kluges we had for finding out
the RT index of the new RTE, since that's now carried along in the
nsitem --- we no longer need fragile assumptions about how the new
RTE is still the last one in the rangetable, at some point rather
distant from where it was actually appended to the list.

Most of the callers of addRangeTableEntryXXX functions just turn
around and pass the result to addRTEtoQuery, which I've renamed
to addNSItemtoQuery; it doesn't gin up a new nsitem anymore but
just installs the one it's passed.  It's perhaps a bit inconsistent
that I renamed that function but not addRangeTableEntryXXX.
I considered making those addNamespaceItemXXX, but desisted on the
perhaps-thin grounds that they don't link the new nsitem into the
parse state, only the RTE.  This could be argued of course.

2. Add per-column information to the ParseNamespaceItems.  As of
this patch, the useful part of that is column type/typmod/collation
info which can be used to generate Vars referencing this RTE.
I envision that the next step will involve generating the Vars'
identity (varno/varattno columns) from that as well, and this
patch includes logic to set up some associated per-column fields.
But those are not actually getting fed into the Vars quite yet.
(The step after that will be to add outer-join-nullability info.)

But independently of those future improvements, this patch is
a win because it allows carrying forward column-type info that's
known at the time we do addRangeTableEntryXXX, and using that
when we make a Var, instead of having to do the rather expensive
computations involved in expandRTE() or get_rte_attribute_type().
get_rte_attribute_type() is indeed gone altogether, and while
expandRTE() is still needed, it's not used in any performance-critical
parse analysis code paths.

On a complex-query test case that I've used before [1], microbenchmarking
just raw parsing plus parse analysis shows a full 20% speedup over HEAD,
which I think can mostly be attributed to getting rid of the syscache
lookups that get_rte_attribute_type() did for Vars referencing base
relations.  The total impact over a complete query execution cycle
is a lot less of course.  Still, it's pretty clearly a performance win,
and to my mind the code is also cleaner --- this is paying down some
technical debt from when we bolted JOIN syntax onto pre-existing
parsing code.

Barring objections, I plan to commit this fairly soon and get onto the
next step, which will start to have ramifications outside the parser.

            regards, tom lane

[1] https://www.postgresql.org/message-id/6970.1545327857%40sss.pgh.pa.us

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8b68fb7..07533f6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2531,7 +2531,7 @@ AddRelationNewConstraints(Relation rel,
     TupleConstr *oldconstr;
     int            numoldchecks;
     ParseState *pstate;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;
     int            numchecks;
     List       *checknames;
     ListCell   *cell;
@@ -2554,13 +2554,13 @@ AddRelationNewConstraints(Relation rel,
      */
     pstate = make_parsestate(NULL);
     pstate->p_sourcetext = queryString;
-    rte = addRangeTableEntryForRelation(pstate,
-                                        rel,
-                                        AccessShareLock,
-                                        NULL,
-                                        false,
-                                        true);
-    addRTEtoQuery(pstate, rte, true, true, true);
+    nsitem = addRangeTableEntryForRelation(pstate,
+                                           rel,
+                                           AccessShareLock,
+                                           NULL,
+                                           false,
+                                           true);
+    addNSItemtoQuery(pstate, nsitem, true, true, true);

     /*
      * Process column default expressions.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 42a147b..04d5bde 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -882,6 +882,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
     if (stmt->relation)
     {
         LOCKMODE    lockmode = is_from ? RowExclusiveLock : AccessShareLock;
+        ParseNamespaceItem *nsitem;
         RangeTblEntry *rte;
         TupleDesc    tupDesc;
         List       *attnums;
@@ -894,14 +895,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,

         relid = RelationGetRelid(rel);

-        rte = addRangeTableEntryForRelation(pstate, rel, lockmode,
-                                            NULL, false, false);
+        nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode,
+                                               NULL, false, false);
+        rte = nsitem->p_rte;
         rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);

         if (stmt->whereClause)
         {
-            /* add rte to column namespace  */
-            addRTEtoQuery(pstate, rte, false, true, true);
+            /* add nsitem to column namespace  */
+            addNSItemtoQuery(pstate, nsitem, false, true, true);

             /* Transform the raw expression tree */
             whereClause = transformExpr(pstate, stmt->whereClause, EXPR_KIND_COPY_WHERE);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 36093dc..1e538a5 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -568,9 +568,9 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
             qual_expr = stringToNode(qual_value);

             /* Add this rel to the parsestate's rangetable, for dependencies */
-            addRangeTableEntryForRelation(qual_pstate, rel,
-                                          AccessShareLock,
-                                          NULL, false, false);
+            (void) addRangeTableEntryForRelation(qual_pstate, rel,
+                                                 AccessShareLock,
+                                                 NULL, false, false);

             qual_parse_rtable = qual_pstate->p_rtable;
             free_parsestate(qual_pstate);
@@ -592,9 +592,9 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
             with_check_qual = stringToNode(with_check_value);

             /* Add this rel to the parsestate's rangetable, for dependencies */
-            addRangeTableEntryForRelation(with_check_pstate, rel,
-                                          AccessShareLock,
-                                          NULL, false, false);
+            (void) addRangeTableEntryForRelation(with_check_pstate, rel,
+                                                 AccessShareLock,
+                                                 NULL, false, false);

             with_check_parse_rtable = with_check_pstate->p_rtable;
             free_parsestate(with_check_pstate);
@@ -699,7 +699,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
     ArrayType  *role_ids;
     ParseState *qual_pstate;
     ParseState *with_check_pstate;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;
     Node       *qual;
     Node       *with_check_qual;
     ScanKeyData skey[2];
@@ -755,16 +755,16 @@ CreatePolicy(CreatePolicyStmt *stmt)
     target_table = relation_open(table_id, NoLock);

     /* Add for the regular security quals */
-    rte = addRangeTableEntryForRelation(qual_pstate, target_table,
-                                        AccessShareLock,
-                                        NULL, false, false);
-    addRTEtoQuery(qual_pstate, rte, false, true, true);
+    nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
+                                           AccessShareLock,
+                                           NULL, false, false);
+    addNSItemtoQuery(qual_pstate, nsitem, false, true, true);

     /* Add for the with-check quals */
-    rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
-                                        AccessShareLock,
-                                        NULL, false, false);
-    addRTEtoQuery(with_check_pstate, rte, false, true, true);
+    nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
+                                           AccessShareLock,
+                                           NULL, false, false);
+    addNSItemtoQuery(with_check_pstate, nsitem, false, true, true);

     qual = transformWhereClause(qual_pstate,
                                 copyObject(stmt->qual),
@@ -933,14 +933,14 @@ AlterPolicy(AlterPolicyStmt *stmt)
     /* Parse the using policy clause */
     if (stmt->qual)
     {
-        RangeTblEntry *rte;
+        ParseNamespaceItem *nsitem;
         ParseState *qual_pstate = make_parsestate(NULL);

-        rte = addRangeTableEntryForRelation(qual_pstate, target_table,
-                                            AccessShareLock,
-                                            NULL, false, false);
+        nsitem = addRangeTableEntryForRelation(qual_pstate, target_table,
+                                               AccessShareLock,
+                                               NULL, false, false);

-        addRTEtoQuery(qual_pstate, rte, false, true, true);
+        addNSItemtoQuery(qual_pstate, nsitem, false, true, true);

         qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
                                     EXPR_KIND_POLICY,
@@ -956,14 +956,14 @@ AlterPolicy(AlterPolicyStmt *stmt)
     /* Parse the with-check policy clause */
     if (stmt->with_check)
     {
-        RangeTblEntry *rte;
+        ParseNamespaceItem *nsitem;
         ParseState *with_check_pstate = make_parsestate(NULL);

-        rte = addRangeTableEntryForRelation(with_check_pstate, target_table,
-                                            AccessShareLock,
-                                            NULL, false, false);
+        nsitem = addRangeTableEntryForRelation(with_check_pstate, target_table,
+                                               AccessShareLock,
+                                               NULL, false, false);

-        addRTEtoQuery(with_check_pstate, rte, false, true, true);
+        addNSItemtoQuery(with_check_pstate, nsitem, false, true, true);

         with_check_qual = transformWhereClause(with_check_pstate,
                                                copyObject(stmt->with_check),
@@ -1107,9 +1107,9 @@ AlterPolicy(AlterPolicyStmt *stmt)
             qual = stringToNode(qual_value);

             /* Add this rel to the parsestate's rangetable, for dependencies */
-            addRangeTableEntryForRelation(qual_pstate, target_table,
-                                          AccessShareLock,
-                                          NULL, false, false);
+            (void) addRangeTableEntryForRelation(qual_pstate, target_table,
+                                                 AccessShareLock,
+                                                 NULL, false, false);

             qual_parse_rtable = qual_pstate->p_rtable;
             free_parsestate(qual_pstate);
@@ -1149,9 +1149,10 @@ AlterPolicy(AlterPolicyStmt *stmt)
             with_check_qual = stringToNode(with_check_value);

             /* Add this rel to the parsestate's rangetable, for dependencies */
-            addRangeTableEntryForRelation(with_check_pstate, target_table,
-                                          AccessShareLock,
-                                          NULL, false, false);
+            (void) addRangeTableEntryForRelation(with_check_pstate,
+                                                 target_table,
+                                                 AccessShareLock,
+                                                 NULL, false, false);

             with_check_parse_rtable = with_check_pstate->p_rtable;
             free_parsestate(with_check_pstate);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5b882f8..e21cdd7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -918,7 +918,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
                     defaultPartOid;
         Relation    parent,
                     defaultRel = NULL;
-        RangeTblEntry *rte;
+        ParseNamespaceItem *nsitem;

         /* Already have strong enough lock on the parent */
         parent = table_open(parentId, NoLock);
@@ -962,13 +962,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
         pstate->p_sourcetext = queryString;

         /*
-         * Add an RTE containing this relation, so that transformExpr called
-         * on partition bound expressions is able to report errors using a
-         * proper context.
+         * Add an nsitem containing this relation, so that transformExpr
+         * called on partition bound expressions is able to report errors
+         * using a proper context.
          */
-        rte = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
-                                            NULL, false, false);
-        addRTEtoQuery(pstate, rte, false, true, true);
+        nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
+                                               NULL, false, false);
+        addNSItemtoQuery(pstate, nsitem, false, true, true);
+
         bound = transformPartitionBound(pstate, parent, stmt->partbound);

         /*
@@ -14970,7 +14971,7 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 {
     PartitionSpec *newspec;
     ParseState *pstate;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;
     ListCell   *l;

     newspec = makeNode(PartitionSpec);
@@ -15004,9 +15005,9 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
      * rangetable entry.  We need a ParseState for transformExpr.
      */
     pstate = make_parsestate(NULL);
-    rte = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
-                                        NULL, false, true);
-    addRTEtoQuery(pstate, rte, true, true, true);
+    nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
+                                           NULL, false, true);
+    addNSItemtoQuery(pstate, nsitem, true, true, true);

     /* take care of any partition expressions */
     foreach(l, partspec->partParams)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 36093a2..18eda58 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -565,7 +565,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
     if (!whenClause && stmt->whenClause)
     {
         ParseState *pstate;
-        RangeTblEntry *rte;
+        ParseNamespaceItem *nsitem;
         List       *varList;
         ListCell   *lc;

@@ -574,20 +574,20 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
         pstate->p_sourcetext = queryString;

         /*
-         * Set up RTEs for OLD and NEW references.
+         * Set up nsitems for OLD and NEW references.
          *
          * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
          */
-        rte = addRangeTableEntryForRelation(pstate, rel,
-                                            AccessShareLock,
-                                            makeAlias("old", NIL),
-                                            false, false);
-        addRTEtoQuery(pstate, rte, false, true, true);
-        rte = addRangeTableEntryForRelation(pstate, rel,
-                                            AccessShareLock,
-                                            makeAlias("new", NIL),
-                                            false, false);
-        addRTEtoQuery(pstate, rte, false, true, true);
+        nsitem = addRangeTableEntryForRelation(pstate, rel,
+                                               AccessShareLock,
+                                               makeAlias("old", NIL),
+                                               false, false);
+        addNSItemtoQuery(pstate, nsitem, false, true, true);
+        nsitem = addRangeTableEntryForRelation(pstate, rel,
+                                               AccessShareLock,
+                                               makeAlias("new", NIL),
+                                               false, false);
+        addNSItemtoQuery(pstate, nsitem, false, true, true);

         /* Transform expression.  Copy to be sure we don't modify original */
         whenClause = transformWhereClause(pstate,
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 9b51480..5fc02b0 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -341,6 +341,7 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 {
     Relation    viewRel;
     List       *new_rt;
+    ParseNamespaceItem *nsitem;
     RangeTblEntry *rt_entry1,
                *rt_entry2;
     ParseState *pstate;
@@ -365,14 +366,17 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
      * Create the 2 new range table entries and form the new range table...
      * OLD first, then NEW....
      */
-    rt_entry1 = addRangeTableEntryForRelation(pstate, viewRel,
-                                              AccessShareLock,
-                                              makeAlias("old", NIL),
-                                              false, false);
-    rt_entry2 = addRangeTableEntryForRelation(pstate, viewRel,
-                                              AccessShareLock,
-                                              makeAlias("new", NIL),
-                                              false, false);
+    nsitem = addRangeTableEntryForRelation(pstate, viewRel,
+                                           AccessShareLock,
+                                           makeAlias("old", NIL),
+                                           false, false);
+    rt_entry1 = nsitem->p_rte;
+    nsitem = addRangeTableEntryForRelation(pstate, viewRel,
+                                           AccessShareLock,
+                                           makeAlias("new", NIL),
+                                           false, false);
+    rt_entry2 = nsitem->p_rte;
+
     /* Must override addRangeTableEntry's default access-check flags */
     rt_entry1->requiredPerms = 0;
     rt_entry2->requiredPerms = 0;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 48b62a5..615309a 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1217,6 +1217,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
     Query       *subselect = (Query *) sublink->subselect;
     Relids        upper_varnos;
     int            rtindex;
+    ParseNamespaceItem *nsitem;
     RangeTblEntry *rte;
     RangeTblRef *rtr;
     List       *subquery_vars;
@@ -1264,11 +1265,12 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
      * below). Therefore this is a lot easier than what pull_up_subqueries has
      * to go through.
      */
-    rte = addRangeTableEntryForSubquery(pstate,
-                                        subselect,
-                                        makeAlias("ANY_subquery", NIL),
-                                        false,
-                                        false);
+    nsitem = addRangeTableEntryForSubquery(pstate,
+                                           subselect,
+                                           makeAlias("ANY_subquery", NIL),
+                                           false,
+                                           false);
+    rte = nsitem->p_rte;
     parse->rtable = lappend(parse->rtable, rte);
     rtindex = list_length(parse->rtable);

diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 0656279..2057bc4 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -415,9 +415,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
                                          stmt->relation->inh,
                                          true,
                                          ACL_DELETE);
-
-    /* grab the namespace item made by setTargetTable */
-    nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
+    nsitem = pstate->p_target_nsitem;
+    Assert(nsitem->p_rtindex == qry->resultRelation);

     /* there's no DISTINCT in DELETE */
     qry->distinctClause = NIL;
@@ -476,8 +475,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
     List       *sub_namespace;
     List       *icolumns;
     List       *attrnos;
+    ParseNamespaceItem *nsitem;
     RangeTblEntry *rte;
-    RangeTblRef *rtr;
     ListCell   *icols;
     ListCell   *attnos;
     ListCell   *lc;
@@ -613,16 +612,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
          * Make the source be a subquery in the INSERT's rangetable, and add
          * it to the INSERT's joinlist.
          */
-        rte = addRangeTableEntryForSubquery(pstate,
-                                            selectQuery,
-                                            makeAlias("*SELECT*", NIL),
-                                            false,
-                                            false);
-        rtr = makeNode(RangeTblRef);
-        /* assume new rte is at end */
-        rtr->rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
-        pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+        nsitem = addRangeTableEntryForSubquery(pstate,
+                                               selectQuery,
+                                               makeAlias("*SELECT*", NIL),
+                                               false,
+                                               false);
+        addNSItemtoQuery(pstate, nsitem, true, false, false);

         /*----------
          * Generate an expression list for the INSERT that selects all the
@@ -652,7 +647,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                 expr = tle->expr;
             else
             {
-                Var           *var = makeVarFromTargetEntry(rtr->rtindex, tle);
+                Var           *var = makeVarFromTargetEntry(nsitem->p_rtindex, tle);

                 var->location = exprLocation((Node *) tle->expr);
                 expr = (Expr *) var;
@@ -774,19 +769,15 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
         /*
          * Generate the VALUES RTE
          */
-        rte = addRangeTableEntryForValues(pstate, exprsLists,
-                                          coltypes, coltypmods, colcollations,
-                                          NULL, lateral, true);
-        rtr = makeNode(RangeTblRef);
-        /* assume new rte is at end */
-        rtr->rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
-        pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+        nsitem = addRangeTableEntryForValues(pstate, exprsLists,
+                                             coltypes, coltypmods, colcollations,
+                                             NULL, lateral, true);
+        addNSItemtoQuery(pstate, nsitem, true, false, false);

         /*
          * Generate list of Vars referencing the RTE
          */
-        expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList);
+        exprList = expandNSItemVars(nsitem, 0, -1, NULL);

         /*
          * Re-apply any indirection on the target column specs to the Vars
@@ -829,7 +820,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
      * Generate query's target list using the computed list of expressions.
      * Also, mark all the target columns as needing insert permissions.
      */
-    rte = pstate->p_target_rangetblentry;
+    rte = pstate->p_target_nsitem->p_rte;
     qry->targetList = NIL;
     Assert(list_length(exprList) <= list_length(icolumns));
     forthree(lc, exprList, icols, icolumns, attnos, attrnos)
@@ -863,8 +854,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
     if (stmt->returningList)
     {
         pstate->p_namespace = NIL;
-        addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
-                      false, true, true);
+        addNSItemtoQuery(pstate, pstate->p_target_nsitem,
+                         false, true, true);
         qry->returningList = transformReturningList(pstate,
                                                     stmt->returningList);
     }
@@ -999,7 +990,6 @@ transformOnConflictClause(ParseState *pstate,
     Oid            arbiterConstraint;
     List       *onConflictSet = NIL;
     Node       *onConflictWhere = NULL;
-    RangeTblEntry *exclRte = NULL;
     int            exclRelIndex = 0;
     List       *exclRelTlist = NIL;
     OnConflictExpr *result;
@@ -1012,6 +1002,8 @@ transformOnConflictClause(ParseState *pstate,
     if (onConflictClause->action == ONCONFLICT_UPDATE)
     {
         Relation    targetrel = pstate->p_target_relation;
+        ParseNamespaceItem *exclNSItem;
+        RangeTblEntry *exclRte;

         /*
          * All INSERT expressions have been parsed, get ready for potentially
@@ -1025,17 +1017,18 @@ transformOnConflictClause(ParseState *pstate,
          * relation, and no permission checks are required on it.  (We'll
          * check the actual target relation, instead.)
          */
-        exclRte = addRangeTableEntryForRelation(pstate,
-                                                targetrel,
-                                                RowExclusiveLock,
-                                                makeAlias("excluded", NIL),
-                                                false, false);
+        exclNSItem = addRangeTableEntryForRelation(pstate,
+                                                   targetrel,
+                                                   RowExclusiveLock,
+                                                   makeAlias("excluded", NIL),
+                                                   false, false);
+        exclRte = exclNSItem->p_rte;
+        exclRelIndex = exclNSItem->p_rtindex;
+
         exclRte->relkind = RELKIND_COMPOSITE_TYPE;
         exclRte->requiredPerms = 0;
         /* other permissions fields in exclRte are already empty */

-        exclRelIndex = list_length(pstate->p_rtable);
-
         /* Create EXCLUDED rel's targetlist for use by EXPLAIN */
         exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel,
                                                          exclRelIndex);
@@ -1044,9 +1037,9 @@ transformOnConflictClause(ParseState *pstate,
          * Add EXCLUDED and the target RTE to the namespace, so that they can
          * be used in the UPDATE subexpressions.
          */
-        addRTEtoQuery(pstate, exclRte, false, true, true);
-        addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
-                      false, true, true);
+        addNSItemtoQuery(pstate, exclNSItem, false, true, true);
+        addNSItemtoQuery(pstate, pstate->p_target_nsitem,
+                         false, true, true);

         /*
          * Now transform the UPDATE subexpressions.
@@ -1343,7 +1336,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
     List      **colexprs = NULL;
     int            sublist_length = -1;
     bool        lateral = false;
-    RangeTblEntry *rte;
     ParseNamespaceItem *nsitem;
     ListCell   *lc;
     ListCell   *lc2;
@@ -1511,14 +1503,10 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
     /*
      * Generate the VALUES RTE
      */
-    rte = addRangeTableEntryForValues(pstate, exprsLists,
-                                      coltypes, coltypmods, colcollations,
-                                      NULL, lateral, true);
-    addRTEtoQuery(pstate, rte, true, true, true);
-
-    /* grab the namespace item made by addRTEtoQuery */
-    nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
-    Assert(rte == nsitem->p_rte);
+    nsitem = addRangeTableEntryForValues(pstate, exprsLists,
+                                         coltypes, coltypmods, colcollations,
+                                         NULL, lateral, true);
+    addNSItemtoQuery(pstate, nsitem, true, true, true);

     /*
      * Generate a targetlist as though expanding "*"
@@ -1593,7 +1581,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                *targetnames,
                *sv_namespace;
     int            sv_rtable_length;
-    RangeTblEntry *jrte;
+    ParseNamespaceItem *jnsitem;
+    ParseNamespaceColumn *sortnscolumns;
+    int            sortcolindex;
     int            tllen;

     qry->commandType = CMD_SELECT;
@@ -1686,6 +1676,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
     qry->targetList = NIL;
     targetvars = NIL;
     targetnames = NIL;
+    sortnscolumns = (ParseNamespaceColumn *)
+        palloc0(list_length(sostmt->colTypes) * sizeof(ParseNamespaceColumn));
+    sortcolindex = 0;

     forfour(lct, sostmt->colTypes,
             lcm, sostmt->colTypmods,
@@ -1716,6 +1709,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
         qry->targetList = lappend(qry->targetList, tle);
         targetvars = lappend(targetvars, var);
         targetnames = lappend(targetnames, makeString(colName));
+        sortnscolumns[sortcolindex].p_varno = leftmostRTI;
+        sortnscolumns[sortcolindex].p_varattno = lefttle->resno;
+        sortnscolumns[sortcolindex].p_vartype = colType;
+        sortnscolumns[sortcolindex].p_vartypmod = colTypmod;
+        sortnscolumns[sortcolindex].p_varcollid = colCollation;
+        sortnscolumns[sortcolindex].p_varnosyn = leftmostRTI;
+        sortnscolumns[sortcolindex].p_varattnosyn = lefttle->resno;
+        sortcolindex++;
     }

     /*
@@ -1730,18 +1731,19 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
      */
     sv_rtable_length = list_length(pstate->p_rtable);

-    jrte = addRangeTableEntryForJoin(pstate,
-                                     targetnames,
-                                     JOIN_INNER,
-                                     targetvars,
-                                     NULL,
-                                     false);
+    jnsitem = addRangeTableEntryForJoin(pstate,
+                                        targetnames,
+                                        sortnscolumns,
+                                        JOIN_INNER,
+                                        targetvars,
+                                        NULL,
+                                        false);

     sv_namespace = pstate->p_namespace;
     pstate->p_namespace = NIL;

-    /* add jrte to column namespace only */
-    addRTEtoQuery(pstate, jrte, false, false, true);
+    /* add jnsitem to column namespace only */
+    addNSItemtoQuery(pstate, jnsitem, false, false, true);

     /*
      * For now, we don't support resjunk sort clauses on the output of a
@@ -1757,7 +1759,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                           EXPR_KIND_ORDER_BY,
                                           false /* allow SQL92 rules */ );

-    /* restore namespace, remove jrte from rtable */
+    /* restore namespace, remove join RTE from rtable */
     pstate->p_namespace = sv_namespace;
     pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length);

@@ -1869,7 +1871,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
         /* Process leaf SELECT */
         Query       *selectQuery;
         char        selectName[32];
-        RangeTblEntry *rte PG_USED_FOR_ASSERTS_ONLY;
+        ParseNamespaceItem *nsitem;
         RangeTblRef *rtr;
         ListCell   *tl;

@@ -1926,19 +1928,17 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
          */
         snprintf(selectName, sizeof(selectName), "*SELECT* %d",
                  list_length(pstate->p_rtable) + 1);
-        rte = addRangeTableEntryForSubquery(pstate,
-                                            selectQuery,
-                                            makeAlias(selectName, NIL),
-                                            false,
-                                            false);
+        nsitem = addRangeTableEntryForSubquery(pstate,
+                                               selectQuery,
+                                               makeAlias(selectName, NIL),
+                                               false,
+                                               false);

         /*
          * Return a RangeTblRef to replace the SelectStmt in the set-op tree.
          */
         rtr = makeNode(RangeTblRef);
-        /* assume new rte is at end */
-        rtr->rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+        rtr->rtindex = nsitem->p_rtindex;
         return (Node *) rtr;
     }
     else
@@ -2236,9 +2236,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                                          stmt->relation->inh,
                                          true,
                                          ACL_UPDATE);
-
-    /* grab the namespace item made by setTargetTable */
-    nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
+    nsitem = pstate->p_target_nsitem;
+    Assert(nsitem->p_rtindex == qry->resultRelation);

     /* subqueries in FROM cannot access the result relation */
     nsitem->p_lateral_only = true;
@@ -2297,7 +2296,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist)
         pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1;

     /* Prepare non-junk columns for assignment to target table */
-    target_rte = pstate->p_target_rangetblentry;
+    target_rte = pstate->p_target_nsitem->p_rte;
     orig_tl = list_head(origTlist);

     foreach(tl, tlist)
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ebbba2d..80d27f5 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -51,37 +51,33 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"

-/* Convenience macro for the most common makeNamespaceItem() case */
-#define makeDefaultNSItem(rte, rti) \
-    makeNamespaceItem(rte, rti, true, true, false, true)

 static void extractRemainingColumns(List *common_colnames,
                                     List *src_colnames, List *src_colvars,
-                                    List **res_colnames, List **res_colvars);
+                                    ParseNamespaceColumn *src_nscolumns,
+                                    List **res_colnames, List **res_colvars,
+                                    ParseNamespaceColumn *res_nscolumns);
 static Node *transformJoinUsingClause(ParseState *pstate,
                                       RangeTblEntry *leftRTE, RangeTblEntry *rightRTE,
                                       List *leftVars, List *rightVars);
 static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
                                    List *namespace);
-static RangeTblEntry *getRTEForSpecialRelationTypes(ParseState *pstate,
-                                                    RangeVar *rv);
-static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
-static RangeTblEntry *transformRangeSubselect(ParseState *pstate,
-                                              RangeSubselect *r);
-static RangeTblEntry *transformRangeFunction(ParseState *pstate,
-                                             RangeFunction *r);
-static RangeTblEntry *transformRangeTableFunc(ParseState *pstate,
-                                              RangeTableFunc *t);
+static ParseNamespaceItem *transformTableEntry(ParseState *pstate, RangeVar *r);
+static ParseNamespaceItem *transformRangeSubselect(ParseState *pstate,
+                                                   RangeSubselect *r);
+static ParseNamespaceItem *transformRangeFunction(ParseState *pstate,
+                                                  RangeFunction *r);
+static ParseNamespaceItem *transformRangeTableFunc(ParseState *pstate,
+                                                   RangeTableFunc *t);
 static TableSampleClause *transformRangeTableSample(ParseState *pstate,
                                                     RangeTableSample *rts);
+static ParseNamespaceItem *getNSItemForSpecialRelationTypes(ParseState *pstate,
+                                                            RangeVar *rv);
 static Node *transformFromClauseItem(ParseState *pstate, Node *n,
-                                     RangeTblEntry **top_rte, int *top_rti,
+                                     ParseNamespaceItem **top_nsitem,
                                      List **namespace);
 static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
                                 Var *l_colvar, Var *r_colvar);
-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);
 static void setNamespaceLateralState(List *namespace,
                                      bool lateral_only, bool lateral_ok);
@@ -130,13 +126,11 @@ transformFromClause(ParseState *pstate, List *frmList)
     foreach(fl, frmList)
     {
         Node       *n = lfirst(fl);
-        RangeTblEntry *rte;
-        int            rtindex;
+        ParseNamespaceItem *nsitem;
         List       *namespace;

         n = transformFromClauseItem(pstate, n,
-                                    &rte,
-                                    &rtindex,
+                                    &nsitem,
                                     &namespace);

         checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace);
@@ -183,8 +177,7 @@ int
 setTargetTable(ParseState *pstate, RangeVar *relation,
                bool inh, bool alsoSource, AclMode requiredPerms)
 {
-    RangeTblEntry *rte;
-    int            rtindex;
+    ParseNamespaceItem *nsitem;

     /*
      * ENRs hide tables of the same name, so we need to check for them first.
@@ -212,19 +205,14 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
                                                 RowExclusiveLock);

     /*
-     * Now build an RTE.
+     * Now build an RTE and a ParseNamespaceItem.
      */
-    rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
-                                        RowExclusiveLock,
-                                        relation->alias, inh, false);
+    nsitem = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
+                                           RowExclusiveLock,
+                                           relation->alias, inh, false);

-    /* 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;
+    /* remember the RTE/nsitem as being the query target */
+    pstate->p_target_nsitem = nsitem;

     /*
      * Override addRangeTableEntry's default ACL_SELECT permissions check, and
@@ -235,28 +223,30 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
      * analysis, we will add the ACL_SELECT bit back again; see
      * markVarForSelectPriv and its callers.
      */
-    rte->requiredPerms = requiredPerms;
+    nsitem->p_rte->requiredPerms = requiredPerms;

     /*
      * If UPDATE/DELETE, add table to joinlist and namespace.
-     *
-     * Note: some callers know that they can find the new ParseNamespaceItem
-     * at the end of the pstate->p_namespace list.  This is a bit ugly but not
-     * worth complicating this function's signature for.
      */
     if (alsoSource)
-        addRTEtoQuery(pstate, rte, true, true, true);
+        addNSItemtoQuery(pstate, nsitem, true, true, true);

-    return rtindex;
+    return nsitem->p_rtindex;
 }

 /*
  * Extract all not-in-common columns from column lists of a source table
+ *
+ * We hand back new lists in *res_colnames and *res_colvars.  But
+ * res_nscolumns points to a caller-allocated array that we fill in
+ * the next few entries in.
  */
 static void
 extractRemainingColumns(List *common_colnames,
                         List *src_colnames, List *src_colvars,
-                        List **res_colnames, List **res_colvars)
+                        ParseNamespaceColumn *src_nscolumns,
+                        List **res_colnames, List **res_colvars,
+                        ParseNamespaceColumn *res_nscolumns)
 {
     List       *new_colnames = NIL;
     List       *new_colvars = NIL;
@@ -271,6 +261,14 @@ extractRemainingColumns(List *common_colnames,
         bool        match = false;
         ListCell   *cnames;

+        /*
+         * Ignore any dropped columns in the src_nscolumns input.  (The list
+         * inputs won't contain entries for dropped columns.)
+         */
+        while (src_nscolumns->p_varno == 0)
+            src_nscolumns++;
+
+        /* is current src column already accounted for in common_colnames? */
         foreach(cnames, common_colnames)
         {
             char       *ccolname = strVal(lfirst(cnames));
@@ -284,9 +282,15 @@ extractRemainingColumns(List *common_colnames,

         if (!match)
         {
+            /* Nope, so emit it as next output column */
             new_colnames = lappend(new_colnames, lfirst(lnames));
             new_colvars = lappend(new_colvars, lfirst(lvars));
+            /* Copy the input relation's nscolumn data for this column */
+            *res_nscolumns = *src_nscolumns;
+            res_nscolumns++;
         }
+
+        src_nscolumns++;
     }

     *res_colnames = new_colnames;
@@ -388,25 +392,20 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace)
 /*
  * transformTableEntry --- transform a RangeVar (simple relation reference)
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 transformTableEntry(ParseState *pstate, RangeVar *r)
 {
-    RangeTblEntry *rte;
-
-    /* We need only build a range table entry */
-    rte = addRangeTableEntry(pstate, r, r->alias, r->inh, true);
-
-    return rte;
+    /* addRangeTableEntry does all the work */
+    return addRangeTableEntry(pstate, r, r->alias, r->inh, true);
 }

 /*
  * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 {
     Query       *query;
-    RangeTblEntry *rte;

     /*
      * We require user to supply an alias for a subselect, per SQL92. To relax
@@ -454,29 +453,26 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
         elog(ERROR, "unexpected non-SELECT command in subquery in FROM");

     /*
-     * OK, build an RTE for the subquery.
+     * OK, build an RTE and nsitem for the subquery.
      */
-    rte = addRangeTableEntryForSubquery(pstate,
-                                        query,
-                                        r->alias,
-                                        r->lateral,
-                                        true);
-
-    return rte;
+    return addRangeTableEntryForSubquery(pstate,
+                                         query,
+                                         r->alias,
+                                         r->lateral,
+                                         true);
 }


 /*
  * transformRangeFunction --- transform a function call appearing in FROM
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 transformRangeFunction(ParseState *pstate, RangeFunction *r)
 {
     List       *funcexprs = NIL;
     List       *funcnames = NIL;
     List       *coldeflists = NIL;
     bool        is_lateral;
-    RangeTblEntry *rte;
     ListCell   *lc;

     /*
@@ -677,13 +673,11 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
     is_lateral = r->lateral || contain_vars_of_level((Node *) funcexprs, 0);

     /*
-     * OK, build an RTE for the function.
+     * OK, build an RTE and nsitem for the function.
      */
-    rte = addRangeTableEntryForFunction(pstate,
-                                        funcnames, funcexprs, coldeflists,
-                                        r, is_lateral, true);
-
-    return rte;
+    return addRangeTableEntryForFunction(pstate,
+                                         funcnames, funcexprs, coldeflists,
+                                         r, is_lateral, true);
 }

 /*
@@ -694,13 +688,12 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
  * row-generating expression, the column-generating expressions, and the
  * default value expressions.
  */
-static RangeTblEntry *
+static ParseNamespaceItem *
 transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 {
     TableFunc  *tf = makeNode(TableFunc);
     const char *constructName;
     Oid            docType;
-    RangeTblEntry *rte;
     bool        is_lateral;
     ListCell   *col;
     char      **names;
@@ -903,10 +896,8 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
      */
     is_lateral = rtf->lateral || contain_vars_of_level((Node *) tf, 0);

-    rte = addRangeTableEntryForTableFunc(pstate,
-                                         tf, rtf->alias, is_lateral, true);
-
-    return rte;
+    return addRangeTableEntryForTableFunc(pstate,
+                                          tf, rtf->alias, is_lateral, true);
 }

 /*
@@ -1013,17 +1004,17 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
 }

 /*
- * getRTEForSpecialRelationTypes
+ * getNSItemForSpecialRelationTypes
  *
  * If given RangeVar refers to a CTE or an EphemeralNamedRelation,
- * build and return an appropriate RTE, otherwise return NULL
+ * build and return an appropriate ParseNamespaceItem, otherwise return NULL
  */
-static RangeTblEntry *
-getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)
+static ParseNamespaceItem *
+getNSItemForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)
 {
+    ParseNamespaceItem *nsitem;
     CommonTableExpr *cte;
     Index        levelsup;
-    RangeTblEntry *rte;

     /*
      * if it is a qualified name, it can't be a CTE or tuplestore reference
@@ -1033,13 +1024,13 @@ getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)

     cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
     if (cte)
-        rte = addRangeTableEntryForCTE(pstate, cte, levelsup, rv, true);
+        nsitem = addRangeTableEntryForCTE(pstate, cte, levelsup, rv, true);
     else if (scanNameSpaceForENR(pstate, rv->relname))
-        rte = addRangeTableEntryForENR(pstate, rv, true);
+        nsitem = addRangeTableEntryForENR(pstate, rv, true);
     else
-        rte = NULL;
+        nsitem = NULL;

-    return rte;
+    return nsitem;
 }

 /*
@@ -1053,11 +1044,9 @@ getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)
  * The function return value is the node to add to the jointree (a
  * RangeTblRef or JoinExpr).  Additional output parameters are:
  *
- * *top_rte: receives the RTE corresponding to the jointree item.
- * (We could extract this from the function return node, but it saves cycles
- * to pass it back separately.)
- *
- * *top_rti: receives the rangetable index of top_rte.  (Ditto.)
+ * *top_nsitem: receives the ParseNamespaceItem directly corresponding to the
+ * jointree item.  (This is only used during internal recursion, not by
+ * outside callers.)
  *
  * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed
  * as table/column names by this item.  (The lateral_only flags in these items
@@ -1065,7 +1054,7 @@ getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)
  */
 static Node *
 transformFromClauseItem(ParseState *pstate, Node *n,
-                        RangeTblEntry **top_rte, int *top_rti,
+                        ParseNamespaceItem **top_nsitem,
                         List **namespace)
 {
     if (IsA(n, RangeVar))
@@ -1073,78 +1062,58 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         /* Plain relation reference, or perhaps a CTE reference */
         RangeVar   *rv = (RangeVar *) n;
         RangeTblRef *rtr;
-        RangeTblEntry *rte;
-        int            rtindex;
+        ParseNamespaceItem *nsitem;

         /* Check if it's a CTE or tuplestore reference */
-        rte = getRTEForSpecialRelationTypes(pstate, rv);
+        nsitem = getNSItemForSpecialRelationTypes(pstate, rv);

         /* if not found above, must be a table reference */
-        if (!rte)
-            rte = transformTableEntry(pstate, rv);
-
-        /* assume new rte is at end */
-        rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
-        *top_rte = rte;
-        *top_rti = rtindex;
-        *namespace = list_make1(makeDefaultNSItem(rte, rtindex));
+        if (!nsitem)
+            nsitem = transformTableEntry(pstate, rv);
+
+        *top_nsitem = nsitem;
+        *namespace = list_make1(nsitem);
         rtr = makeNode(RangeTblRef);
-        rtr->rtindex = rtindex;
+        rtr->rtindex = nsitem->p_rtindex;
         return (Node *) rtr;
     }
     else if (IsA(n, RangeSubselect))
     {
         /* sub-SELECT is like a plain relation */
         RangeTblRef *rtr;
-        RangeTblEntry *rte;
-        int            rtindex;
-
-        rte = transformRangeSubselect(pstate, (RangeSubselect *) n);
-        /* assume new rte is at end */
-        rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
-        *top_rte = rte;
-        *top_rti = rtindex;
-        *namespace = list_make1(makeDefaultNSItem(rte, rtindex));
+        ParseNamespaceItem *nsitem;
+
+        nsitem = transformRangeSubselect(pstate, (RangeSubselect *) n);
+        *top_nsitem = nsitem;
+        *namespace = list_make1(nsitem);
         rtr = makeNode(RangeTblRef);
-        rtr->rtindex = rtindex;
+        rtr->rtindex = nsitem->p_rtindex;
         return (Node *) rtr;
     }
     else if (IsA(n, RangeFunction))
     {
         /* function is like a plain relation */
         RangeTblRef *rtr;
-        RangeTblEntry *rte;
-        int            rtindex;
-
-        rte = transformRangeFunction(pstate, (RangeFunction *) n);
-        /* assume new rte is at end */
-        rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
-        *top_rte = rte;
-        *top_rti = rtindex;
-        *namespace = list_make1(makeDefaultNSItem(rte, rtindex));
+        ParseNamespaceItem *nsitem;
+
+        nsitem = transformRangeFunction(pstate, (RangeFunction *) n);
+        *top_nsitem = nsitem;
+        *namespace = list_make1(nsitem);
         rtr = makeNode(RangeTblRef);
-        rtr->rtindex = rtindex;
+        rtr->rtindex = nsitem->p_rtindex;
         return (Node *) rtr;
     }
     else if (IsA(n, RangeTableFunc))
     {
         /* table function is like a plain relation */
         RangeTblRef *rtr;
-        RangeTblEntry *rte;
-        int            rtindex;
-
-        rte = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
-        /* assume new rte is at end */
-        rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
-        *top_rte = rte;
-        *top_rti = rtindex;
-        *namespace = list_make1(makeDefaultNSItem(rte, rtindex));
+        ParseNamespaceItem *nsitem;
+
+        nsitem = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
+        *top_nsitem = nsitem;
+        *namespace = list_make1(nsitem);
         rtr = makeNode(RangeTblRef);
-        rtr->rtindex = rtindex;
+        rtr->rtindex = nsitem->p_rtindex;
         return (Node *) rtr;
     }
     else if (IsA(n, RangeTableSample))
@@ -1152,19 +1121,17 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         /* TABLESAMPLE clause (wrapping some other valid FROM node) */
         RangeTableSample *rts = (RangeTableSample *) n;
         Node       *rel;
-        RangeTblRef *rtr;
         RangeTblEntry *rte;

         /* Recursively transform the contained relation */
         rel = transformFromClauseItem(pstate, rts->relation,
-                                      top_rte, top_rti, namespace);
-        /* Currently, grammar could only return a RangeVar as contained rel */
-        rtr = castNode(RangeTblRef, rel);
-        rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
+                                      top_nsitem, namespace);
+        rte = (*top_nsitem)->p_rte;
         /* We only support this on plain relations and matviews */
-        if (rte->relkind != RELKIND_RELATION &&
-            rte->relkind != RELKIND_MATVIEW &&
-            rte->relkind != RELKIND_PARTITIONED_TABLE)
+        if (rte->rtekind != RTE_RELATION ||
+            (rte->relkind != RELKIND_RELATION &&
+             rte->relkind != RELKIND_MATVIEW &&
+             rte->relkind != RELKIND_PARTITIONED_TABLE))
             ereport(ERROR,
                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                      errmsg("TABLESAMPLE clause can only be applied to tables and materialized views"),
@@ -1172,16 +1139,15 @@ transformFromClauseItem(ParseState *pstate, Node *n,

         /* Transform TABLESAMPLE details and attach to the RTE */
         rte->tablesample = transformRangeTableSample(pstate, rts);
-        return (Node *) rtr;
+        return rel;
     }
     else if (IsA(n, JoinExpr))
     {
         /* A newfangled join expression */
         JoinExpr   *j = (JoinExpr *) n;
-        RangeTblEntry *l_rte;
-        RangeTblEntry *r_rte;
-        int            l_rtindex;
-        int            r_rtindex;
+        ParseNamespaceItem *nsitem;
+        ParseNamespaceItem *l_nsitem;
+        ParseNamespaceItem *r_nsitem;
         List       *l_namespace,
                    *r_namespace,
                    *my_namespace,
@@ -1191,9 +1157,10 @@ transformFromClauseItem(ParseState *pstate, Node *n,
                    *l_colvars,
                    *r_colvars,
                    *res_colvars;
+        ParseNamespaceColumn *res_nscolumns;
+        int            res_colindex;
         bool        lateral_ok;
         int            sv_namespace_length;
-        RangeTblEntry *rte;
         int            k;

         /*
@@ -1201,8 +1168,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
          * it in this order for correct visibility of LATERAL references.
          */
         j->larg = transformFromClauseItem(pstate, j->larg,
-                                          &l_rte,
-                                          &l_rtindex,
+                                          &l_nsitem,
                                           &l_namespace);

         /*
@@ -1225,8 +1191,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,

         /* And now we can process the RHS */
         j->rarg = transformFromClauseItem(pstate, j->rarg,
-                                          &r_rte,
-                                          &r_rtindex,
+                                          &r_nsitem,
                                           &r_namespace);

         /* Remove the left-side RTEs from the namespace list again */
@@ -1248,12 +1213,10 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         /*
          * Extract column name and var lists from both subtrees
          *
-         * Note: expandRTE returns new lists, safe for me to modify
+         * Note: expandNSItemVars returns new lists, safe for me to modify
          */
-        expandRTE(l_rte, l_rtindex, 0, -1, false,
-                  &l_colnames, &l_colvars);
-        expandRTE(r_rte, r_rtindex, 0, -1, false,
-                  &r_colnames, &r_colvars);
+        l_colvars = expandNSItemVars(l_nsitem, 0, -1, &l_colnames);
+        r_colvars = expandNSItemVars(r_nsitem, 0, -1, &r_colnames);

         /*
          * Natural join does not explicitly specify columns; must generate
@@ -1302,6 +1265,12 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         res_colnames = NIL;
         res_colvars = NIL;

+        /* this may be larger than needed */
+        res_nscolumns = (ParseNamespaceColumn *)
+            palloc0((list_length(l_colnames) + list_length(r_colnames)) *
+                    sizeof(ParseNamespaceColumn));
+        res_colindex = 0;
+
         if (j->usingClause)
         {
             /*
@@ -1325,6 +1294,8 @@ transformFromClauseItem(ParseState *pstate, Node *n,
                 int            r_index = -1;
                 Var           *l_colvar,
                            *r_colvar;
+                Node       *u_colvar;
+                ParseNamespaceColumn *res_nscolumn;

                 /* Check for USING(foo,foo) */
                 foreach(col, res_colnames)
@@ -1390,16 +1361,58 @@ transformFromClauseItem(ParseState *pstate, Node *n,
                 r_usingvars = lappend(r_usingvars, r_colvar);

                 res_colnames = lappend(res_colnames, lfirst(ucol));
-                res_colvars = lappend(res_colvars,
-                                      buildMergedJoinVar(pstate,
-                                                         j->jointype,
-                                                         l_colvar,
-                                                         r_colvar));
+                u_colvar = buildMergedJoinVar(pstate,
+                                              j->jointype,
+                                              l_colvar,
+                                              r_colvar);
+                res_colvars = lappend(res_colvars, u_colvar);
+                res_nscolumn = res_nscolumns + res_colindex;
+                res_colindex++;
+                if (u_colvar == (Node *) l_colvar)
+                {
+                    /* Merged column is equivalent to left input */
+                    res_nscolumn->p_varno = l_colvar->varno;
+                    res_nscolumn->p_varattno = l_colvar->varattno;
+                    res_nscolumn->p_vartype = l_colvar->vartype;
+                    res_nscolumn->p_vartypmod = l_colvar->vartypmod;
+                    res_nscolumn->p_varcollid = l_colvar->varcollid;
+                    /* XXX these are not quite right, but doesn't matter yet */
+                    res_nscolumn->p_varnosyn = l_colvar->varno;
+                    res_nscolumn->p_varattnosyn = l_colvar->varattno;
+                }
+                else if (u_colvar == (Node *) r_colvar)
+                {
+                    /* Merged column is equivalent to right input */
+                    res_nscolumn->p_varno = r_colvar->varno;
+                    res_nscolumn->p_varattno = r_colvar->varattno;
+                    res_nscolumn->p_vartype = r_colvar->vartype;
+                    res_nscolumn->p_vartypmod = r_colvar->vartypmod;
+                    res_nscolumn->p_varcollid = r_colvar->varcollid;
+                    /* XXX these are not quite right, but doesn't matter yet */
+                    res_nscolumn->p_varnosyn = r_colvar->varno;
+                    res_nscolumn->p_varattnosyn = r_colvar->varattno;
+                }
+                else
+                {
+                    /*
+                     * Merged column is not semantically equivalent to either
+                     * input, so it needs to be referenced as the join output
+                     * column.  We don't know the join's varno yet, so we'll
+                     * replace these zeroes below.
+                     */
+                    res_nscolumn->p_varno = 0;
+                    res_nscolumn->p_varattno = res_colindex;
+                    res_nscolumn->p_vartype = exprType(u_colvar);
+                    res_nscolumn->p_vartypmod = exprTypmod(u_colvar);
+                    res_nscolumn->p_varcollid = exprCollation(u_colvar);
+                    res_nscolumn->p_varnosyn = 0;
+                    res_nscolumn->p_varattnosyn = res_colindex;
+                }
             }

             j->quals = transformJoinUsingClause(pstate,
-                                                l_rte,
-                                                r_rte,
+                                                l_nsitem->p_rte,
+                                                r_nsitem->p_rte,
                                                 l_usingvars,
                                                 r_usingvars);
         }
@@ -1416,10 +1429,16 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         /* Add remaining columns from each side to the output columns */
         extractRemainingColumns(res_colnames,
                                 l_colnames, l_colvars,
-                                &l_colnames, &l_colvars);
+                                l_nsitem->p_nscolumns,
+                                &l_colnames, &l_colvars,
+                                res_nscolumns + res_colindex);
+        res_colindex += list_length(l_colvars);
         extractRemainingColumns(res_colnames,
                                 r_colnames, r_colvars,
-                                &r_colnames, &r_colvars);
+                                r_nsitem->p_nscolumns,
+                                &r_colnames, &r_colvars,
+                                res_nscolumns + res_colindex);
+        res_colindex += list_length(r_colvars);
         res_colnames = list_concat(res_colnames, l_colnames);
         res_colvars = list_concat(res_colvars, l_colvars);
         res_colnames = list_concat(res_colnames, r_colnames);
@@ -1441,21 +1460,41 @@ transformFromClauseItem(ParseState *pstate, Node *n,
         }

         /*
-         * Now build an RTE for the result of the join
+         * Now build an RTE and nsitem for the result of the join.
+         * res_nscolumns isn't totally done yet, but that's OK because
+         * addRangeTableEntryForJoin doesn't examine it, only store a pointer.
          */
-        rte = addRangeTableEntryForJoin(pstate,
-                                        res_colnames,
-                                        j->jointype,
-                                        res_colvars,
-                                        j->alias,
-                                        true);
+        nsitem = addRangeTableEntryForJoin(pstate,
+                                           res_colnames,
+                                           res_nscolumns,
+                                           j->jointype,
+                                           res_colvars,
+                                           j->alias,
+                                           true);

-        /* assume new rte is at end */
-        j->rtindex = list_length(pstate->p_rtable);
-        Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
+        j->rtindex = nsitem->p_rtindex;

-        *top_rte = rte;
-        *top_rti = j->rtindex;
+        /*
+         * Now that we know the join RTE's rangetable index, we can fix up the
+         * res_nscolumns data in places where it should contain that.
+         */
+        Assert(res_colindex == list_length(nsitem->p_rte->eref->colnames));
+        for (k = 0; k < res_colindex; k++)
+        {
+            ParseNamespaceColumn *nscol = res_nscolumns + k;
+
+            /* fill in join RTI for merged columns */
+            if (nscol->p_varno == 0)
+                nscol->p_varno = j->rtindex;
+            if (nscol->p_varnosyn == 0)
+                nscol->p_varnosyn = j->rtindex;
+            /* if join has an alias, it syntactically hides all inputs */
+            if (j->alias)
+            {
+                nscol->p_varnosyn = j->rtindex;
+                nscol->p_varattnosyn = k + 1;
+            }
+        }

         /* make a matching link to the JoinExpr for later use */
         for (k = list_length(pstate->p_joinexprs) + 1; k < j->rtindex; k++)
@@ -1483,13 +1522,13 @@ transformFromClauseItem(ParseState *pstate, Node *n,
          * The join RTE itself is always made visible for unqualified column
          * names.  It's visible as a relation name only if it has an alias.
          */
-        *namespace = lappend(my_namespace,
-                             makeNamespaceItem(rte,
-                                               j->rtindex,
-                                               (j->alias != NULL),
-                                               true,
-                                               false,
-                                               true));
+        nsitem->p_rel_visible = (j->alias != NULL);
+        nsitem->p_cols_visible = true;
+        nsitem->p_lateral_only = false;
+        nsitem->p_lateral_ok = true;
+
+        *top_nsitem = nsitem;
+        *namespace = lappend(my_namespace, nsitem);

         return (Node *) j;
     }
@@ -1618,27 +1657,6 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 }

 /*
- * makeNamespaceItem -
- *      Convenience subroutine to construct a ParseNamespaceItem.
- */
-static ParseNamespaceItem *
-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;
-    nsitem->p_lateral_ok = lateral_ok;
-    return nsitem;
-}
-
-/*
  * setNamespaceColumnVisibility -
  *      Convenience subroutine to update cols_visible flags in a namespace list.
  */
@@ -3163,8 +3181,8 @@ transformOnConflictArbiter(ParseState *pstate,
          */
         save_namespace = pstate->p_namespace;
         pstate->p_namespace = NIL;
-        addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
-                      false, false, true);
+        addNSItemtoQuery(pstate, pstate->p_target_nsitem,
+                         false, false, true);

         if (infer->indexElems)
             *arbiterExpr = resolve_unique_index_expr(pstate, infer,
@@ -3189,7 +3207,7 @@ transformOnConflictArbiter(ParseState *pstate,
         if (infer->conname)
         {
             Oid            relid = RelationGetRelid(pstate->p_target_relation);
-            RangeTblEntry *rte = pstate->p_target_rangetblentry;
+            RangeTblEntry *rte = pstate->p_target_nsitem->p_rte;
             Bitmapset  *conattnos;

             conattnos = get_relation_constraint_attnos(relid, infer->conname,
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a9..484298d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1010,11 +1010,10 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
         int            rtindex = ((Var *) node)->varno;
         int            sublevels_up = ((Var *) node)->varlevelsup;
         int            vlocation = ((Var *) node)->location;
-        RangeTblEntry *rte;
+        ParseNamespaceItem *nsitem;

-        rte = GetRTEByRangeTablePosn(pstate, rtindex, sublevels_up);
-        expandRTE(rte, rtindex, sublevels_up, vlocation, false,
-                  NULL, &args);
+        nsitem = GetNSItemByRangeTablePosn(pstate, rtindex, sublevels_up);
+        args = expandNSItemVars(nsitem, sublevels_up, vlocation, NULL);
     }
     else
         ereport(ERROR,
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 25e92de..06511df 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2656,8 +2656,8 @@ static Node *
 transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
 {
     /* CURRENT OF can only appear at top level of UPDATE/DELETE */
-    Assert(pstate->p_target_rtindex > 0);
-    cexpr->cvarno = pstate->p_target_rtindex;
+    Assert(pstate->p_target_nsitem != NULL);
+    cexpr->cvarno = pstate->p_target_nsitem->p_rtindex;

     /*
      * Check to see if the cursor name matches a parameter of type REFCURSOR.
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 4888311..113bb51 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -472,7 +472,8 @@ check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                  errmsg("invalid reference to FROM-clause entry for table \"%s\"",
                         refname),
-                 (rte == pstate->p_target_rangetblentry) ?
+                 (pstate->p_target_nsitem != NULL &&
+                  rte == pstate->p_target_nsitem->p_rte) ?
                  errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the
query.",
                          refname) :
                  errdetail("The combining JOIN type must be INNER or LEFT for a LATERAL reference."),
@@ -669,9 +670,6 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
     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
@@ -703,11 +701,39 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
                  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);
+    if (attnum > InvalidAttrNumber)
+    {
+        /* Get attribute data from the ParseNamespaceColumn array */
+        ParseNamespaceColumn *nscol = &nsitem->p_nscolumns[attnum - 1];
+
+        /* Complain if dropped column.  See notes in scanRTEForColumn. */
+        if (nscol->p_varno == 0)
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_COLUMN),
+                     errmsg("column \"%s\" of relation \"%s\" does not exist",
+                            colname,
+                            rte->eref->aliasname)));
+
+        var = makeVar(nsitem->p_rtindex,
+                      attnum,
+                      nscol->p_vartype,
+                      nscol->p_vartypmod,
+                      nscol->p_varcollid,
+                      sublevels_up);
+    }
+    else
+    {
+        /* System column, so use predetermined type data */
+        const FormData_pg_attribute *sysatt;
+
+        sysatt = SystemAttributeDefinition(attnum);
+        var = makeVar(nsitem->p_rtindex,
+                      attnum,
+                      sysatt->atttypid,
+                      sysatt->atttypmod,
+                      sysatt->attcollation,
+                      sublevels_up);
+    }
     var->location = location;

     /* Require read access to the column */
@@ -753,11 +779,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
      * don't bother to test for that case here.
      *
      * 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 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.
+     * we'll still catch it by virtue of the check in scanNSItemForColumn().
+     * Callers interested in finding match with shortest distance need to
+     * defend against this directly, though.
      */
     foreach(c, rte->eref->colnames)
     {
@@ -1201,6 +1225,121 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname,
 }

 /*
+ * buildNSItemFromTupleDesc
+ *        Build a ParseNamespaceItem, given a tupdesc describing the columns.
+ *
+ * rte: the new RangeTblEntry for the rel
+ * rtindex: its index in the rangetable list
+ * tupdesc: the physical column information
+ */
+static ParseNamespaceItem *
+buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc)
+{
+    ParseNamespaceItem *nsitem;
+    ParseNamespaceColumn *nscolumns;
+    int            maxattrs = tupdesc->natts;
+    int            varattno;
+
+    /* colnames must have the same number of entries as the nsitem */
+    Assert(maxattrs == list_length(rte->eref->colnames));
+
+    /* extract per-column data from the tupdesc */
+    nscolumns = (ParseNamespaceColumn *)
+        palloc0(maxattrs * sizeof(ParseNamespaceColumn));
+
+    for (varattno = 0; varattno < maxattrs; varattno++)
+    {
+        Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
+
+        /* For a dropped column, just leave the entry as zeroes */
+        if (attr->attisdropped)
+            continue;
+
+        nscolumns[varattno].p_varno = rtindex;
+        nscolumns[varattno].p_varattno = varattno + 1;
+        nscolumns[varattno].p_vartype = attr->atttypid;
+        nscolumns[varattno].p_vartypmod = attr->atttypmod;
+        nscolumns[varattno].p_varcollid = attr->attcollation;
+        nscolumns[varattno].p_varnosyn = rtindex;
+        nscolumns[varattno].p_varattnosyn = varattno + 1;
+    }
+
+    /* ... and build the nsitem */
+    nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+    nsitem->p_rte = rte;
+    nsitem->p_rtindex = rtindex;
+    nsitem->p_nscolumns = nscolumns;
+    /* set default visibility flags; might get changed later */
+    nsitem->p_rel_visible = true;
+    nsitem->p_cols_visible = true;
+    nsitem->p_lateral_only = false;
+    nsitem->p_lateral_ok = true;
+
+    return nsitem;
+}
+
+/*
+ * buildNSItemFromLists
+ *        Build a ParseNamespaceItem, given column type information in lists.
+ *
+ * rte: the new RangeTblEntry for the rel
+ * rtindex: its index in the rangetable list
+ * coltypes: per-column datatype OIDs
+ * coltypmods: per-column type modifiers
+ * colcollation: per-column collation OIDs
+ */
+static ParseNamespaceItem *
+buildNSItemFromLists(RangeTblEntry *rte, Index rtindex,
+                     List *coltypes, List *coltypmods, List *colcollations)
+{
+    ParseNamespaceItem *nsitem;
+    ParseNamespaceColumn *nscolumns;
+    int            maxattrs = list_length(coltypes);
+    int            varattno;
+    ListCell   *lct;
+    ListCell   *lcm;
+    ListCell   *lcc;
+
+    /* colnames must have the same number of entries as the nsitem */
+    Assert(maxattrs == list_length(rte->eref->colnames));
+
+    Assert(maxattrs == list_length(coltypmods));
+    Assert(maxattrs == list_length(colcollations));
+
+    /* extract per-column data from the lists */
+    nscolumns = (ParseNamespaceColumn *)
+        palloc0(maxattrs * sizeof(ParseNamespaceColumn));
+
+    varattno = 0;
+    forthree(lct, coltypes,
+             lcm, coltypmods,
+             lcc, colcollations)
+    {
+        nscolumns[varattno].p_varno = rtindex;
+        nscolumns[varattno].p_varattno = varattno + 1;
+        nscolumns[varattno].p_vartype = lfirst_oid(lct);
+        nscolumns[varattno].p_vartypmod = lfirst_int(lcm);
+        nscolumns[varattno].p_varcollid = lfirst_oid(lcc);
+        nscolumns[varattno].p_varnosyn = rtindex;
+        nscolumns[varattno].p_varattnosyn = varattno + 1;
+        varattno++;
+    }
+
+    /* ... and build the nsitem */
+    nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+    nsitem->p_rte = rte;
+    nsitem->p_rtindex = rtindex;
+    nsitem->p_nscolumns = nscolumns;
+    /* set default visibility flags; might get changed later */
+    nsitem->p_rel_visible = true;
+    nsitem->p_cols_visible = true;
+    nsitem->p_lateral_only = false;
+    nsitem->p_lateral_ok = true;
+
+    return nsitem;
+}
+
+/*
  * Open a table during parse analysis
  *
  * This is essentially just the same as table_openrv(), except that it caters
@@ -1255,11 +1394,15 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)

 /*
  * Add an entry for a relation to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * We do not link the ParseNamespaceItem into the pstate here; it's the
+ * caller's job to do that in the appropriate way.
  *
  * Note: formerly this checked for refname conflicts, but that's wrong.
  * Caller is responsible for checking for conflicts in the appropriate scope.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntry(ParseState *pstate,
                    RangeVar *relation,
                    Alias *alias,
@@ -1270,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate,
     char       *refname = alias ? alias->aliasname : relation->relname;
     LOCKMODE    lockmode;
     Relation    rel;
+    ParseNamespaceItem *nsitem;

     Assert(pstate != NULL);

@@ -1302,13 +1446,6 @@ addRangeTableEntry(ParseState *pstate,
     buildRelationAliases(rel->rd_att, alias, rte->eref);

     /*
-     * Drop the rel refcount, but keep the access lock till end of transaction
-     * so that the table can't be deleted or have its schema modified
-     * underneath us.
-     */
-    table_close(rel, NoLock);
-
-    /*
      * Set flags and access permissions.
      *
      * The initial default on access checks is always check-for-READ-access,
@@ -1326,16 +1463,32 @@ addRangeTableEntry(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+                                      rel->rd_att);
+
+    /*
+     * Drop the rel refcount, but keep the access lock till end of transaction
+     * so that the table can't be deleted or have its schema modified
+     * underneath us.
+     */
+    table_close(rel, NoLock);
+
+    return nsitem;
 }

 /*
  * Add an entry for a relation to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is just like addRangeTableEntry() except that it makes an RTE
  * given an already-open relation instead of a RangeVar reference.
@@ -1349,7 +1502,7 @@ addRangeTableEntry(ParseState *pstate,
  * would require importing storage/lock.h into parse_relation.h.  Since
  * LOCKMODE is typedef'd as int anyway, that seems like overkill.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForRelation(ParseState *pstate,
                               Relation rel,
                               int lockmode,
@@ -1398,21 +1551,28 @@ addRangeTableEntryForRelation(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+                                    rel->rd_att);
 }

 /*
  * Add an entry for a subquery to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is just like addRangeTableEntry() except that it makes a subquery RTE.
  * Note that an alias clause *must* be supplied.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForSubquery(ParseState *pstate,
                               Query *subquery,
                               Alias *alias,
@@ -1423,6 +1583,9 @@ addRangeTableEntryForSubquery(ParseState *pstate,
     char       *refname = alias->aliasname;
     Alias       *eref;
     int            numaliases;
+    List       *coltypes,
+               *coltypmods,
+               *colcollations;
     int            varattno;
     ListCell   *tlistitem;

@@ -1435,7 +1598,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
     eref = copyObject(alias);
     numaliases = list_length(eref->colnames);

-    /* fill in any unspecified alias columns */
+    /* fill in any unspecified alias columns, and extract column type info */
+    coltypes = coltypmods = colcollations = NIL;
     varattno = 0;
     foreach(tlistitem, subquery->targetList)
     {
@@ -1452,6 +1616,12 @@ addRangeTableEntryForSubquery(ParseState *pstate,
             attrname = pstrdup(te->resname);
             eref->colnames = lappend(eref->colnames, makeString(attrname));
         }
+        coltypes = lappend_oid(coltypes,
+                               exprType((Node *) te->expr));
+        coltypmods = lappend_int(coltypmods,
+                                 exprTypmod((Node *) te->expr));
+        colcollations = lappend_oid(colcollations,
+                                    exprCollation((Node *) te->expr));
     }
     if (varattno < numaliases)
         ereport(ERROR,
@@ -1478,21 +1648,27 @@ addRangeTableEntryForSubquery(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+                                coltypes, coltypmods, colcollations);
 }

 /*
  * Add an entry for a function (or functions) to the pstate's range table
- * (p_rtable).
+ * (p_rtable).  Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is just like addRangeTableEntry() except that it makes a function RTE.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForFunction(ParseState *pstate,
                               List *funcnames,
                               List *funcexprs,
@@ -1742,20 +1918,27 @@ addRangeTableEntryForFunction(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+                                    tupdesc);
 }

 /*
  * Add an entry for a table function to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is much like addRangeTableEntry() except that it makes a tablefunc RTE.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForTableFunc(ParseState *pstate,
                                TableFunc *tf,
                                Alias *alias,
@@ -1806,20 +1989,28 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+                                rte->coltypes, rte->coltypmods,
+                                rte->colcollations);
 }

 /*
  * Add an entry for a VALUES list to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is much like addRangeTableEntry() except that it makes a values RTE.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForValues(ParseState *pstate,
                             List *exprs,
                             List *coltypes,
@@ -1885,22 +2076,33 @@ addRangeTableEntryForValues(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+                                rte->coltypes, rte->coltypmods,
+                                rte->colcollations);
 }

 /*
  * Add an entry for a join to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is much like addRangeTableEntry() except that it makes a join RTE.
+ * Also, it's more convenient for the caller to construct the
+ * ParseNamespaceColumn array, so we pass that in.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForJoin(ParseState *pstate,
                           List *colnames,
+                          ParseNamespaceColumn *nscolumns,
                           JoinType jointype,
                           List *aliasvars,
                           Alias *alias,
@@ -1909,6 +2111,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
     RangeTblEntry *rte = makeNode(RangeTblEntry);
     Alias       *eref;
     int            numaliases;
+    ParseNamespaceItem *nsitem;

     Assert(pstate != NULL);

@@ -1956,20 +2159,36 @@ addRangeTableEntryForJoin(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+    nsitem->p_rte = rte;
+    nsitem->p_rtindex = list_length(pstate->p_rtable);
+    nsitem->p_nscolumns = nscolumns;
+    /* set default visibility flags; might get changed later */
+    nsitem->p_rel_visible = true;
+    nsitem->p_cols_visible = true;
+    nsitem->p_lateral_only = false;
+    nsitem->p_lateral_ok = true;
+
+    return nsitem;
 }

 /*
  * Add an entry for a CTE reference to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * This is much like addRangeTableEntry() except that it makes a CTE RTE.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForCTE(ParseState *pstate,
                          CommonTableExpr *cte,
                          Index levelsup,
@@ -2059,17 +2278,25 @@ addRangeTableEntryForCTE(ParseState *pstate,
     rte->extraUpdatedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+                                rte->coltypes, rte->coltypmods,
+                                rte->colcollations);
 }

 /*
  * Add an entry for an ephemeral named relation reference to the pstate's
  * range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
  *
  * It is expected that the RangeVar, which up until now is only known to be an
  * ephemeral named relation, will (in conjunction with the QueryEnvironment in
@@ -2079,7 +2306,7 @@ addRangeTableEntryForCTE(ParseState *pstate,
  * This is much like addRangeTableEntry() except that it makes an RTE for an
  * ephemeral named relation.
  */
-RangeTblEntry *
+ParseNamespaceItem *
 addRangeTableEntryForENR(ParseState *pstate,
                          RangeVar *rv,
                          bool inFromCl)
@@ -2164,12 +2391,18 @@ addRangeTableEntryForENR(ParseState *pstate,
     rte->selectedCols = NULL;

     /*
-     * Add completed RTE to pstate's range table list, but not to join list
-     * nor namespace --- caller must do that if appropriate.
+     * Add completed RTE to pstate's range table list, so that we know its
+     * index.  But we don't add it to the join list --- caller must do that if
+     * appropriate.
      */
     pstate->p_rtable = lappend(pstate->p_rtable, rte);

-    return rte;
+    /*
+     * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+     * list --- caller must do that if appropriate.
+     */
+    return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+                                    tupdesc);
 }


@@ -2221,49 +2454,26 @@ isLockedRefname(ParseState *pstate, const char *refname)
 }

 /*
- * Add the given RTE as a top-level entry in the pstate's join list
+ * Add the given nsitem/RTE as a top-level entry in the pstate's join list
  * and/or namespace list.  (We assume caller has checked for any
- * namespace conflicts.)  The RTE is always marked as unconditionally
+ * namespace conflicts.)  The nsitem is always marked as unconditionally
  * visible, that is, not LATERAL-only.
- *
- * Note: some callers know that they can find the new ParseNamespaceItem
- * at the end of the pstate->p_namespace list.  This is a bit ugly but not
- * worth complicating this function's signature for.
  */
 void
-addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
-              bool addToJoinList,
-              bool addToRelNameSpace, bool addToVarNameSpace)
+addNSItemtoQuery(ParseState *pstate, ParseNamespaceItem *nsitem,
+                 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)
     {
         RangeTblRef *rtr = makeNode(RangeTblRef);

-        rtr->rtindex = rtindex;
+        rtr->rtindex = nsitem->p_rtindex;
         pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
     }
     if (addToRelNameSpace || addToVarNameSpace)
     {
-        ParseNamespaceItem *nsitem;
-
-        nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
-        nsitem->p_rte = rte;
-        nsitem->p_rtindex = rtindex;
+        /* Set the new nsitem's visibility flags correctly */
         nsitem->p_rel_visible = addToRelNameSpace;
         nsitem->p_cols_visible = addToVarNameSpace;
         nsitem->p_lateral_only = false;
@@ -2721,6 +2931,61 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
 }

 /*
+ * expandNSItemVars
+ *      Produce a list of Vars, and optionally a list of column names,
+ *      for the non-dropped columns of the nsitem.
+ *
+ * The emitted Vars are marked with the given sublevels_up and location.
+ *
+ * If colnames isn't NULL, a list of String items for the columns is stored
+ * there; note that it's just a subset of the RTE's eref list, and hence
+ * the list elements mustn't be modified.
+ */
+List *
+expandNSItemVars(ParseNamespaceItem *nsitem,
+                 int sublevels_up, int location,
+                 List **colnames)
+{
+    List       *result = NIL;
+    int            colindex;
+    ListCell   *lc;
+
+    if (colnames)
+        *colnames = NIL;
+    colindex = 0;
+    foreach(lc, nsitem->p_rte->eref->colnames)
+    {
+        Value       *colnameval = (Value *) lfirst(lc);
+        const char *colname = strVal(colnameval);
+        ParseNamespaceColumn *nscol = nsitem->p_nscolumns + colindex;
+
+        if (colname[0])
+        {
+            Var           *var;
+
+            Assert(nscol->p_varno > 0);
+            var = makeVar(nsitem->p_rtindex,
+                          colindex + 1,
+                          nscol->p_vartype,
+                          nscol->p_vartypmod,
+                          nscol->p_varcollid,
+                          sublevels_up);
+            var->location = location;
+            result = lappend(result, var);
+            if (colnames)
+                *colnames = lappend(*colnames, colnameval);
+        }
+        else
+        {
+            /* dropped column, ignore */
+            Assert(nscol->p_varno == 0);
+        }
+        colindex++;
+    }
+    return result;
+}
+
+/*
  * expandNSItemAttrs -
  *      Workhorse for "*" expansion: produce a list of targetentries
  *      for the attributes of the nsitem
@@ -2739,8 +3004,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
                *var;
     List       *te_list = NIL;

-    expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false,
-              &names, &vars);
+    vars = expandNSItemVars(nsitem, sublevels_up, location, &names);

     /*
      * Require read access to the table.  This is normally redundant with the
@@ -2816,204 +3080,6 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
 }

 /*
- * get_rte_attribute_type
- *        Get attribute type/typmod/collation information from a RangeTblEntry
- */
-void
-get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
-                       Oid *vartype, int32 *vartypmod, Oid *varcollid)
-{
-    switch (rte->rtekind)
-    {
-        case RTE_RELATION:
-            {
-                /* Plain relation RTE --- get the attribute's type info */
-                HeapTuple    tp;
-                Form_pg_attribute att_tup;
-
-                tp = SearchSysCache2(ATTNUM,
-                                     ObjectIdGetDatum(rte->relid),
-                                     Int16GetDatum(attnum));
-                if (!HeapTupleIsValid(tp))    /* shouldn't happen */
-                    elog(ERROR, "cache lookup failed for attribute %d of relation %u",
-                         attnum, rte->relid);
-                att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-
-                /*
-                 * If dropped column, pretend it ain't there.  See notes in
-                 * scanRTEForColumn.
-                 */
-                if (att_tup->attisdropped)
-                    ereport(ERROR,
-                            (errcode(ERRCODE_UNDEFINED_COLUMN),
-                             errmsg("column \"%s\" of relation \"%s\" does not exist",
-                                    NameStr(att_tup->attname),
-                                    get_rel_name(rte->relid))));
-                *vartype = att_tup->atttypid;
-                *vartypmod = att_tup->atttypmod;
-                *varcollid = att_tup->attcollation;
-                ReleaseSysCache(tp);
-            }
-            break;
-        case RTE_SUBQUERY:
-            {
-                /* Subselect RTE --- get type info from subselect's tlist */
-                TargetEntry *te = get_tle_by_resno(rte->subquery->targetList,
-                                                   attnum);
-
-                if (te == NULL || te->resjunk)
-                    elog(ERROR, "subquery %s does not have attribute %d",
-                         rte->eref->aliasname, attnum);
-                *vartype = exprType((Node *) te->expr);
-                *vartypmod = exprTypmod((Node *) te->expr);
-                *varcollid = exprCollation((Node *) te->expr);
-            }
-            break;
-        case RTE_FUNCTION:
-            {
-                /* Function RTE */
-                ListCell   *lc;
-                int            atts_done = 0;
-
-                /* Identify which function covers the requested column */
-                foreach(lc, rte->functions)
-                {
-                    RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
-
-                    if (attnum > atts_done &&
-                        attnum <= atts_done + rtfunc->funccolcount)
-                    {
-                        TypeFuncClass functypclass;
-                        Oid            funcrettype;
-                        TupleDesc    tupdesc;
-
-                        attnum -= atts_done;    /* now relative to this func */
-                        functypclass = get_expr_result_type(rtfunc->funcexpr,
-                                                            &funcrettype,
-                                                            &tupdesc);
-
-                        if (functypclass == TYPEFUNC_COMPOSITE ||
-                            functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
-                        {
-                            /* Composite data type, e.g. a table's row type */
-                            Form_pg_attribute att_tup;
-
-                            Assert(tupdesc);
-                            Assert(attnum <= tupdesc->natts);
-                            att_tup = TupleDescAttr(tupdesc, attnum - 1);
-
-                            /*
-                             * If dropped column, pretend it ain't there.  See
-                             * notes in scanRTEForColumn.
-                             */
-                            if (att_tup->attisdropped)
-                                ereport(ERROR,
-                                        (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                         errmsg("column \"%s\" of relation \"%s\" does not exist",
-                                                NameStr(att_tup->attname),
-                                                rte->eref->aliasname)));
-                            *vartype = att_tup->atttypid;
-                            *vartypmod = att_tup->atttypmod;
-                            *varcollid = att_tup->attcollation;
-                        }
-                        else if (functypclass == TYPEFUNC_SCALAR)
-                        {
-                            /* Base data type, i.e. scalar */
-                            *vartype = funcrettype;
-                            *vartypmod = -1;
-                            *varcollid = exprCollation(rtfunc->funcexpr);
-                        }
-                        else if (functypclass == TYPEFUNC_RECORD)
-                        {
-                            *vartype = list_nth_oid(rtfunc->funccoltypes,
-                                                    attnum - 1);
-                            *vartypmod = list_nth_int(rtfunc->funccoltypmods,
-                                                      attnum - 1);
-                            *varcollid = list_nth_oid(rtfunc->funccolcollations,
-                                                      attnum - 1);
-                        }
-                        else
-                        {
-                            /*
-                             * addRangeTableEntryForFunction should've caught
-                             * this
-                             */
-                            elog(ERROR, "function in FROM has unsupported return type");
-                        }
-                        return;
-                    }
-                    atts_done += rtfunc->funccolcount;
-                }
-
-                /* If we get here, must be looking for the ordinality column */
-                if (rte->funcordinality && attnum == atts_done + 1)
-                {
-                    *vartype = INT8OID;
-                    *vartypmod = -1;
-                    *varcollid = InvalidOid;
-                    return;
-                }
-
-                /* this probably can't happen ... */
-                ereport(ERROR,
-                        (errcode(ERRCODE_UNDEFINED_COLUMN),
-                         errmsg("column %d of relation \"%s\" does not exist",
-                                attnum,
-                                rte->eref->aliasname)));
-            }
-            break;
-        case RTE_JOIN:
-            {
-                /*
-                 * Join RTE --- get type info from join RTE's alias variable
-                 */
-                Node       *aliasvar;
-
-                Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
-                aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
-                Assert(aliasvar != NULL);
-                *vartype = exprType(aliasvar);
-                *vartypmod = exprTypmod(aliasvar);
-                *varcollid = exprCollation(aliasvar);
-            }
-            break;
-        case RTE_TABLEFUNC:
-        case RTE_VALUES:
-        case RTE_CTE:
-        case RTE_NAMEDTUPLESTORE:
-            {
-                /*
-                 * tablefunc, VALUES, CTE, or ENR RTE --- get type info from
-                 * lists in the RTE
-                 */
-                Assert(attnum > 0 && attnum <= list_length(rte->coltypes));
-                *vartype = list_nth_oid(rte->coltypes, attnum - 1);
-                *vartypmod = list_nth_int(rte->coltypmods, attnum - 1);
-                *varcollid = list_nth_oid(rte->colcollations, attnum - 1);
-
-                /* For ENR, better check for dropped column */
-                if (!OidIsValid(*vartype))
-                    ereport(ERROR,
-                            (errcode(ERRCODE_UNDEFINED_COLUMN),
-                             errmsg("column %d of relation \"%s\" does not exist",
-                                    attnum,
-                                    rte->eref->aliasname)));
-            }
-            break;
-        case RTE_RESULT:
-            /* this probably can't happen ... */
-            ereport(ERROR,
-                    (errcode(ERRCODE_UNDEFINED_COLUMN),
-                     errmsg("column %d of relation \"%s\" does not exist",
-                            attnum,
-                            rte->eref->aliasname)));
-            break;
-        default:
-            elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
-    }
-}
-
-/*
  * get_rte_attribute_is_dropped
  *        Check whether attempted attribute ref is to a dropped column
  */
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 46fe6c6..967808b 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -551,7 +551,7 @@ transformAssignedExpr(ParseState *pstate,
              */
             Var           *var;

-            var = makeVar(pstate->p_target_rtindex, attrno,
+            var = makeVar(pstate->p_target_nsitem->p_rtindex, attrno,
                           attrtype, attrtypmod, attrcollation, 0);
             var->location = location;

@@ -1359,8 +1359,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
         List       *vars;
         ListCell   *l;

-        expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false,
-                  NULL, &vars);
+        vars = expandNSItemVars(nsitem, sublevels_up, location, NULL);

         /*
          * Require read access to the table.  This is normally redundant with
@@ -1496,6 +1495,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
     Assert(IsA(var, Var));
     Assert(var->vartype == RECORDOID);

+    /*
+     * Note: it's tempting to use GetNSItemByRangeTablePosn here so that we
+     * can use expandNSItemVars instead of expandRTE; but that does not work
+     * for some of the recursion cases below, where we have consed up a
+     * ParseState that lacks p_namespace data.
+     */
     netlevelsup = var->varlevelsup + levelsup;
     rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
     attnum = var->varattno;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 45bb31e..b783cb8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -2598,7 +2598,7 @@ IndexStmt *
 transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
 {
     ParseState *pstate;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;
     ListCell   *l;
     Relation    rel;

@@ -2622,12 +2622,12 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
      * relation, but we still need to open it.
      */
     rel = relation_open(relid, NoLock);
-    rte = addRangeTableEntryForRelation(pstate, rel,
-                                        AccessShareLock,
-                                        NULL, false, true);
+    nsitem = addRangeTableEntryForRelation(pstate, rel,
+                                           AccessShareLock,
+                                           NULL, false, true);

     /* no to join list, yes to namespaces */
-    addRTEtoQuery(pstate, rte, false, true, true);
+    addNSItemtoQuery(pstate, nsitem, false, true, true);

     /* take care of the where clause */
     if (stmt->whereClause)
@@ -2707,8 +2707,8 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 {
     Relation    rel;
     ParseState *pstate;
-    RangeTblEntry *oldrte;
-    RangeTblEntry *newrte;
+    ParseNamespaceItem *oldnsitem;
+    ParseNamespaceItem *newnsitem;

     /*
      * To avoid deadlock, make sure the first thing we do is grab
@@ -2729,20 +2729,20 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,

     /*
      * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
-     * Set up their RTEs in the main pstate for use in parsing the rule
-     * qualification.
+     * Set up their ParseNamespaceItems in the main pstate for use in parsing
+     * the rule qualification.
      */
-    oldrte = addRangeTableEntryForRelation(pstate, rel,
-                                           AccessShareLock,
-                                           makeAlias("old", NIL),
-                                           false, false);
-    newrte = addRangeTableEntryForRelation(pstate, rel,
-                                           AccessShareLock,
-                                           makeAlias("new", NIL),
-                                           false, false);
+    oldnsitem = addRangeTableEntryForRelation(pstate, rel,
+                                              AccessShareLock,
+                                              makeAlias("old", NIL),
+                                              false, false);
+    newnsitem = addRangeTableEntryForRelation(pstate, rel,
+                                              AccessShareLock,
+                                              makeAlias("new", NIL),
+                                              false, false);
     /* Must override addRangeTableEntry's default access-check flags */
-    oldrte->requiredPerms = 0;
-    newrte->requiredPerms = 0;
+    oldnsitem->p_rte->requiredPerms = 0;
+    newnsitem->p_rte->requiredPerms = 0;

     /*
      * They must be in the namespace too for lookup purposes, but only add the
@@ -2754,17 +2754,17 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
     switch (stmt->event)
     {
         case CMD_SELECT:
-            addRTEtoQuery(pstate, oldrte, false, true, true);
+            addNSItemtoQuery(pstate, oldnsitem, false, true, true);
             break;
         case CMD_UPDATE:
-            addRTEtoQuery(pstate, oldrte, false, true, true);
-            addRTEtoQuery(pstate, newrte, false, true, true);
+            addNSItemtoQuery(pstate, oldnsitem, false, true, true);
+            addNSItemtoQuery(pstate, newnsitem, false, true, true);
             break;
         case CMD_INSERT:
-            addRTEtoQuery(pstate, newrte, false, true, true);
+            addNSItemtoQuery(pstate, newnsitem, false, true, true);
             break;
         case CMD_DELETE:
-            addRTEtoQuery(pstate, oldrte, false, true, true);
+            addNSItemtoQuery(pstate, oldnsitem, false, true, true);
             break;
         default:
             elog(ERROR, "unrecognized event type: %d",
@@ -2832,18 +2832,18 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
              * nor "*" in the rule actions.  We decide later whether to put
              * them in the joinlist.
              */
-            oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                   AccessShareLock,
-                                                   makeAlias("old", NIL),
-                                                   false, false);
-            newrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                   AccessShareLock,
-                                                   makeAlias("new", NIL),
-                                                   false, false);
-            oldrte->requiredPerms = 0;
-            newrte->requiredPerms = 0;
-            addRTEtoQuery(sub_pstate, oldrte, false, true, false);
-            addRTEtoQuery(sub_pstate, newrte, false, true, false);
+            oldnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
+                                                      AccessShareLock,
+                                                      makeAlias("old", NIL),
+                                                      false, false);
+            newnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
+                                                      AccessShareLock,
+                                                      makeAlias("new", NIL),
+                                                      false, false);
+            oldnsitem->p_rte->requiredPerms = 0;
+            newnsitem->p_rte->requiredPerms = 0;
+            addNSItemtoQuery(sub_pstate, oldnsitem, false, true, false);
+            addNSItemtoQuery(sub_pstate, newnsitem, false, true, false);

             /* Transform the rule action statement */
             top_subqry = transformStmt(sub_pstate,
@@ -2967,6 +2967,8 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
              */
             if (has_old || (has_new && stmt->event == CMD_UPDATE))
             {
+                RangeTblRef *rtr;
+
                 /*
                  * If sub_qry is a setop, manipulating its jointree will do no
                  * good at all, because the jointree is dummy. (This should be
@@ -2976,11 +2978,11 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
                     ereport(ERROR,
                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                              errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
-                /* hack so we can use addRTEtoQuery() */
-                sub_pstate->p_rtable = sub_qry->rtable;
-                sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
-                addRTEtoQuery(sub_pstate, oldrte, true, false, false);
-                sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
+                /* hackishly add OLD to the already-built FROM clause */
+                rtr = makeNode(RangeTblRef);
+                rtr->rtindex = oldnsitem->p_rtindex;
+                sub_qry->jointree->fromlist =
+                    lappend(sub_qry->jointree->fromlist, rtr);
             }

             newactions = lappend(newactions, top_subqry);
@@ -3025,7 +3027,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
     List       *newcmds = NIL;
     bool        skipValidation = true;
     AlterTableCmd *newcmd;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;

     /*
      * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
@@ -3040,13 +3042,13 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
     /* Set up pstate */
     pstate = make_parsestate(NULL);
     pstate->p_sourcetext = queryString;
-    rte = addRangeTableEntryForRelation(pstate,
-                                        rel,
-                                        AccessShareLock,
-                                        NULL,
-                                        false,
-                                        true);
-    addRTEtoQuery(pstate, rte, false, true, true);
+    nsitem = addRangeTableEntryForRelation(pstate,
+                                           rel,
+                                           AccessShareLock,
+                                           NULL,
+                                           false,
+                                           true);
+    addNSItemtoQuery(pstate, nsitem, false, true, true);

     /* Set up CreateStmtContext */
     cxt.pstate = pstate;
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index e01d18c..8f4eb8b 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -777,8 +777,8 @@ copy_table(Relation rel)
     copybuf = makeStringInfo();

     pstate = make_parsestate(NULL);
-    addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
-                                  NULL, false, false);
+    (void) addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
+                                         NULL, false, false);

     attnamelist = make_copy_attnamelist(relmapentry);
     cstate = BeginCopyFrom(pstate, rel, NULL, false, copy_read_data, attnamelist, NIL);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 334efba..54fa806 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3227,6 +3227,7 @@ rewriteTargetView(Query *parsetree, Relation view)
     {
         Index        old_exclRelIndex,
                     new_exclRelIndex;
+        ParseNamespaceItem *new_exclNSItem;
         RangeTblEntry *new_exclRte;
         List       *tmp_tlist;

@@ -3261,11 +3262,12 @@ rewriteTargetView(Query *parsetree, Relation view)
          */
         old_exclRelIndex = parsetree->onConflict->exclRelIndex;

-        new_exclRte = addRangeTableEntryForRelation(make_parsestate(NULL),
-                                                    base_rel,
-                                                    RowExclusiveLock,
-                                                    makeAlias("excluded", NIL),
-                                                    false, false);
+        new_exclNSItem = addRangeTableEntryForRelation(make_parsestate(NULL),
+                                                       base_rel,
+                                                       RowExclusiveLock,
+                                                       makeAlias("excluded", NIL),
+                                                       false, false);
+        new_exclRte = new_exclNSItem->p_rte;
         new_exclRte->relkind = RELKIND_COMPOSITE_TYPE;
         new_exclRte->requiredPerms = 0;
         /* other permissions fields in new_exclRte are already empty */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 674acc5..93e7c09 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -19,6 +19,11 @@
 #include "utils/relcache.h"


+/* Forward references for some structs declared below */
+typedef struct ParseState ParseState;
+typedef struct ParseNamespaceItem ParseNamespaceItem;
+typedef struct ParseNamespaceColumn ParseNamespaceColumn;
+
 /*
  * Expression kinds distinguished by transformExpr().  Many of these are not
  * semantically distinct so far as expression transformation goes; rather,
@@ -79,8 +84,6 @@ typedef enum ParseExprKind
 /*
  * Function signatures for parser hooks
  */
-typedef struct ParseState ParseState;
-
 typedef Node *(*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref);
 typedef Node *(*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var);
 typedef Node *(*ParseParamRefHook) (ParseState *pstate, ParamRef *pref);
@@ -132,9 +135,7 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
  *
  * p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE.
  *
- * p_target_rangetblentry: target relation's entry in the rtable list.
- *
- * p_target_rtindex: target relation's index in the rtable list.
+ * p_target_nsitem: target relation's ParseNamespaceItem.
  *
  * p_is_insert: true to process assignment expressions like INSERT, false
  * to process them like UPDATE.  (Note this can change intra-statement, for
@@ -174,7 +175,7 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
  */
 struct ParseState
 {
-    struct ParseState *parentParseState;    /* stack link */
+    ParseState *parentParseState;    /* stack link */
     const char *p_sourcetext;    /* source text, or NULL if not available */
     List       *p_rtable;        /* range table so far */
     List       *p_joinexprs;    /* JoinExprs for RTE_JOIN p_rtable entries */
@@ -187,8 +188,7 @@ 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, or NULL */
-    int            p_target_rtindex;    /* target rel's RT index, or 0 */
+    ParseNamespaceItem *p_target_nsitem;    /* target rel's NSItem, or NULL */
     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 */
@@ -225,6 +225,10 @@ struct ParseState
 /*
  * An element of a namespace list.
  *
+ * The p_nscolumns array contains info showing how to construct Vars
+ * referencing corresponding elements of the RTE's colnames list.  If the RTE
+ * contains dropped columns, the corresponding array elements are all-zeroes.
+ *
  * Namespace items with p_rel_visible set define which RTEs are accessible by
  * qualified names, while those with p_cols_visible set define which RTEs are
  * accessible by unqualified names.  These sets are different because a JOIN
@@ -249,15 +253,46 @@ struct ParseState
  * are more complicated than "must have different alias names", so in practice
  * code searching a namespace list has to check for ambiguous references.
  */
-typedef struct ParseNamespaceItem
+struct ParseNamespaceItem
 {
     RangeTblEntry *p_rte;        /* The relation's rangetable entry */
     int            p_rtindex;        /* The relation's index in the rangetable */
+    /* array of same length as p_rte->eref->colnames: */
+    ParseNamespaceColumn *p_nscolumns;    /* per-column data */
     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? */
     bool        p_lateral_ok;    /* If so, does join type allow use? */
-} ParseNamespaceItem;
+};
+
+/*
+ * Data about one column of a ParseNamespaceItem.
+ *
+ * We track the info needed to construct a Var referencing the column
+ * (but only for user-defined columns; system column references and
+ * whole-row references are handled separately).
+ *
+ * p_varno and p_varattno identify the semantic referent, which is a
+ * base-relation column unless the reference is to a join USING column that
+ * isn't semantically equivalent to either join input column (because it is a
+ * FULL join or the input column requires a type coercion).  In those cases
+ * p_varno and p_varattno refer to the JOIN RTE.
+ *
+ * p_varnosyn and p_varattnosyn are either identical to p_varno/p_varattno,
+ * or they specify the column's position in an aliased JOIN RTE that hides
+ * the semantic referent RTE's refname.  (That could be either the JOIN RTE
+ * in which this ParseNamespaceColumn entry exists, or some lower join level.)
+ */
+struct ParseNamespaceColumn
+{
+    Index        p_varno;        /* rangetable index */
+    AttrNumber    p_varattno;        /* attribute number of the column */
+    Oid            p_vartype;        /* pg_type OID */
+    int32        p_vartypmod;    /* type modifier value */
+    Oid            p_varcollid;    /* OID of collation, or InvalidOid */
+    Index        p_varnosyn;        /* rangetable index of syntactic referent */
+    AttrNumber    p_varattnosyn;    /* attribute number of syntactic referent */
+};

 /* Support for parser_errposition_callback function */
 typedef struct ParseCallbackState
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index b09a71e..cb44fd4 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -45,66 +45,70 @@ extern void markVarForSelectPriv(ParseState *pstate, Var *var,
                                  RangeTblEntry *rte);
 extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation,
                                 int lockmode);
-extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
-                                         RangeVar *relation,
-                                         Alias *alias,
-                                         bool inh,
-                                         bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForRelation(ParseState *pstate,
-                                                    Relation rel,
-                                                    int lockmode,
-                                                    Alias *alias,
-                                                    bool inh,
-                                                    bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
-                                                    Query *subquery,
-                                                    Alias *alias,
-                                                    bool lateral,
-                                                    bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
-                                                    List *funcnames,
-                                                    List *funcexprs,
-                                                    List *coldeflists,
-                                                    RangeFunction *rangefunc,
-                                                    bool lateral,
-                                                    bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
-                                                  List *exprs,
-                                                  List *coltypes,
-                                                  List *coltypmods,
-                                                  List *colcollations,
-                                                  Alias *alias,
-                                                  bool lateral,
-                                                  bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForTableFunc(ParseState *pstate,
-                                                     TableFunc *tf,
+extern ParseNamespaceItem *addRangeTableEntry(ParseState *pstate,
+                                              RangeVar *relation,
+                                              Alias *alias,
+                                              bool inh,
+                                              bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForRelation(ParseState *pstate,
+                                                         Relation rel,
+                                                         int lockmode,
+                                                         Alias *alias,
+                                                         bool inh,
+                                                         bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForSubquery(ParseState *pstate,
+                                                         Query *subquery,
+                                                         Alias *alias,
+                                                         bool lateral,
+                                                         bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForFunction(ParseState *pstate,
+                                                         List *funcnames,
+                                                         List *funcexprs,
+                                                         List *coldeflists,
+                                                         RangeFunction *rangefunc,
+                                                         bool lateral,
+                                                         bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForValues(ParseState *pstate,
+                                                       List *exprs,
+                                                       List *coltypes,
+                                                       List *coltypmods,
+                                                       List *colcollations,
+                                                       Alias *alias,
+                                                       bool lateral,
+                                                       bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForTableFunc(ParseState *pstate,
+                                                          TableFunc *tf,
+                                                          Alias *alias,
+                                                          bool lateral,
+                                                          bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForJoin(ParseState *pstate,
+                                                     List *colnames,
+                                                     ParseNamespaceColumn *nscolumns,
+                                                     JoinType jointype,
+                                                     List *aliasvars,
                                                      Alias *alias,
-                                                     bool lateral,
                                                      bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
-                                                List *colnames,
-                                                JoinType jointype,
-                                                List *aliasvars,
-                                                Alias *alias,
-                                                bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForCTE(ParseState *pstate,
-                                               CommonTableExpr *cte,
-                                               Index levelsup,
-                                               RangeVar *rv,
-                                               bool inFromCl);
-extern RangeTblEntry *addRangeTableEntryForENR(ParseState *pstate,
-                                               RangeVar *rv,
-                                               bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForCTE(ParseState *pstate,
+                                                    CommonTableExpr *cte,
+                                                    Index levelsup,
+                                                    RangeVar *rv,
+                                                    bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForENR(ParseState *pstate,
+                                                    RangeVar *rv,
+                                                    bool inFromCl);
 extern bool isLockedRefname(ParseState *pstate, const char *refname);
-extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
-                          bool addToJoinList,
-                          bool addToRelNameSpace, bool addToVarNameSpace);
+extern void addNSItemtoQuery(ParseState *pstate, ParseNamespaceItem *nsitem,
+                             bool addToJoinList,
+                             bool addToRelNameSpace, bool addToVarNameSpace);
 extern void errorMissingRTE(ParseState *pstate, RangeVar *relation) pg_attribute_noreturn();
 extern void errorMissingColumn(ParseState *pstate,
                                const char *relname, const char *colname, int location) pg_attribute_noreturn();
 extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                       int location, bool include_dropped,
                       List **colnames, List **colvars);
+extern List *expandNSItemVars(ParseNamespaceItem *nsitem,
+                              int sublevels_up, int location,
+                              List **colnames);
 extern List *expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
                                int sublevels_up, int location);
 extern int    attnameAttNum(Relation rd, const char *attname, bool sysColOK);
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index cf47e10..a2c3832 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.h
@@ -38,15 +38,7 @@
 extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);

 /*
- * Given an RTE and an attribute number, return the appropriate
- * type and typemod info for that attribute of that RTE.
- */
-extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
-                                   Oid *vartype, int32 *vartypmod, Oid *varcollid);
-
-/*
- * Check whether an attribute of an RTE has been dropped (note that
- * get_rte_attribute_type will fail on such an attr)
+ * Check whether an attribute of an RTE has been dropped
  */
 extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
                                          AttrNumber attnum);
diff --git a/src/test/modules/test_rls_hooks/test_rls_hooks.c b/src/test/modules/test_rls_hooks/test_rls_hooks.c
index f576ecb..66df39d 100644
--- a/src/test/modules/test_rls_hooks/test_rls_hooks.c
+++ b/src/test/modules/test_rls_hooks/test_rls_hooks.c
@@ -70,7 +70,7 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation)
     Node       *e;
     ColumnRef  *c;
     ParseState *qual_pstate;
-    RangeTblEntry *rte;
+    ParseNamespaceItem *nsitem;

     if (strcmp(RelationGetRelationName(relation), "rls_test_permissive") != 0 &&
         strcmp(RelationGetRelationName(relation), "rls_test_both") != 0)
@@ -78,9 +78,10 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation)

     qual_pstate = make_parsestate(NULL);

-    rte = addRangeTableEntryForRelation(qual_pstate, relation, AccessShareLock,
-                                        NULL, false, false);
-    addRTEtoQuery(qual_pstate, rte, false, true, true);
+    nsitem = addRangeTableEntryForRelation(qual_pstate,
+                                           relation, AccessShareLock,
+                                           NULL, false, false);
+    addNSItemtoQuery(qual_pstate, nsitem, false, true, true);

     role = ObjectIdGetDatum(ACL_ID_PUBLIC);

@@ -134,8 +135,7 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)
     Node       *e;
     ColumnRef  *c;
     ParseState *qual_pstate;
-    RangeTblEntry *rte;
-
+    ParseNamespaceItem *nsitem;

     if (strcmp(RelationGetRelationName(relation), "rls_test_restrictive") != 0 &&
         strcmp(RelationGetRelationName(relation), "rls_test_both") != 0)
@@ -143,9 +143,10 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)

     qual_pstate = make_parsestate(NULL);

-    rte = addRangeTableEntryForRelation(qual_pstate, relation, AccessShareLock,
-                                        NULL, false, false);
-    addRTEtoQuery(qual_pstate, rte, false, true, true);
+    nsitem = addRangeTableEntryForRelation(qual_pstate,
+                                           relation, AccessShareLock,
+                                           NULL, false, false);
+    addNSItemtoQuery(qual_pstate, nsitem, false, true, true);

     role = ObjectIdGetDatum(ACL_ID_PUBLIC);


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Amit Kapila
Дата:
Сообщение: Re: [HACKERS] Block level parallel vacuum
Следующее
От: Thomas Munro
Дата:
Сообщение: Re: Cache relation sizes?