* prepared to deal with sets of result tuples.  Otherwise, a return
  * of *isDone = ExprMultipleResult signifies a set element, and a return
  * of *isDone = ExprEndResult signifies end of the set of tuple.
+ * We assume that *isDone has been initialized to ExprSingleResult by caller.
  */
 static bool
 ExecTargetList(List *targetlist,
        /*
         * evaluate all the expressions in the target list
         */
-       if (isDone)
-               *isDone = ExprSingleResult;             /* until proven otherwise */
-
        haveDoneSets = false;           /* any exhausted set exprs in tlist? */
 
        foreach(tl, targetlist)
        return true;
 }
 
-/*
- * ExecVariableList
- *             Evaluates a simple-Variable-list projection.
- *
- * Results are stored into the passed values and isnull arrays.
- */
-static void
-ExecVariableList(ProjectionInfo *projInfo,
-                                Datum *values,
-                                bool *isnull)
-{
-       ExprContext *econtext = projInfo->pi_exprContext;
-       int                *varSlotOffsets = projInfo->pi_varSlotOffsets;
-       int                *varNumbers = projInfo->pi_varNumbers;
-       int                     i;
-
-       /*
-        * Force extraction of all input values that we need.
-        */
-       if (projInfo->pi_lastInnerVar > 0)
-               slot_getsomeattrs(econtext->ecxt_innertuple,
-                                                 projInfo->pi_lastInnerVar);
-       if (projInfo->pi_lastOuterVar > 0)
-               slot_getsomeattrs(econtext->ecxt_outertuple,
-                                                 projInfo->pi_lastOuterVar);
-       if (projInfo->pi_lastScanVar > 0)
-               slot_getsomeattrs(econtext->ecxt_scantuple,
-                                                 projInfo->pi_lastScanVar);
-
-       /*
-        * Assign to result by direct extraction of fields from source slots ... a
-        * mite ugly, but fast ...
-        */
-       for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
-       {
-               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
-               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-               int                     varNumber = varNumbers[i] - 1;
-
-               values[i] = varSlot->tts_values[varNumber];
-               isnull[i] = varSlot->tts_isnull[varNumber];
-       }
-}
-
 /*
  * ExecProject
  *
 ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
+       ExprContext *econtext;
+       int                     numSimpleVars;
 
        /*
         * sanity checks
         * get the projection info we want
         */
        slot = projInfo->pi_slot;
+       econtext = projInfo->pi_exprContext;
+
+       /* Assume single result row until proven otherwise */
+       if (isDone)
+               *isDone = ExprSingleResult;
 
        /*
         * Clear any former contents of the result slot.  This makes it safe for
        ExecClearTuple(slot);
 
        /*
-        * form a new result tuple (if possible); if successful, mark the result
-        * slot as containing a valid virtual tuple
+        * Force extraction of all input values that we'll need.  The
+        * Var-extraction loops below depend on this, and we are also prefetching
+        * all attributes that will be referenced in the generic expressions.
+        */
+       if (projInfo->pi_lastInnerVar > 0)
+               slot_getsomeattrs(econtext->ecxt_innertuple,
+                                                 projInfo->pi_lastInnerVar);
+       if (projInfo->pi_lastOuterVar > 0)
+               slot_getsomeattrs(econtext->ecxt_outertuple,
+                                                 projInfo->pi_lastOuterVar);
+       if (projInfo->pi_lastScanVar > 0)
+               slot_getsomeattrs(econtext->ecxt_scantuple,
+                                                 projInfo->pi_lastScanVar);
+
+       /*
+        * Assign simple Vars to result by direct extraction of fields from source
+        * slots ... a mite ugly, but fast ...
         */
-       if (projInfo->pi_isVarList)
+       numSimpleVars = projInfo->pi_numSimpleVars;
+       if (numSimpleVars > 0)
        {
-               /* simple Var list: this always succeeds with one result row */
-               if (isDone)
-                       *isDone = ExprSingleResult;
-               ExecVariableList(projInfo,
-                                                slot->tts_values,
-                                                slot->tts_isnull);
-               ExecStoreVirtualTuple(slot);
+               Datum  *values = slot->tts_values;
+               bool   *isnull = slot->tts_isnull;
+               int        *varSlotOffsets = projInfo->pi_varSlotOffsets;
+               int        *varNumbers = projInfo->pi_varNumbers;
+               int             i;
+
+               if (projInfo->pi_directMap)
+               {
+                       /* especially simple case where vars go to output in order */
+                       for (i = 0; i < numSimpleVars; i++)
+                       {
+                               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
+                               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+                               int                     varNumber = varNumbers[i] - 1;
+
+                               values[i] = varSlot->tts_values[varNumber];
+                               isnull[i] = varSlot->tts_isnull[varNumber];
+                       }
+               }
+               else
+               {
+                       /* we have to pay attention to varOutputCols[] */
+                       int        *varOutputCols = projInfo->pi_varOutputCols;
+
+                       for (i = 0; i < numSimpleVars; i++)
+                       {
+                               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
+                               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+                               int                     varNumber = varNumbers[i] - 1;
+                               int                     varOutputCol = varOutputCols[i] - 1;
+
+                               values[varOutputCol] = varSlot->tts_values[varNumber];
+                               isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
+                       }
+               }
        }
-       else
+
+       /*
+        * If there are any generic expressions, evaluate them.  It's possible
+        * that there are set-returning functions in such expressions; if so
+        * and we have reached the end of the set, we return the result slot,
+        * which we already marked empty.
+        */
+       if (projInfo->pi_targetlist)
        {
-               if (ExecTargetList(projInfo->pi_targetlist,
-                                                  projInfo->pi_exprContext,
-                                                  slot->tts_values,
-                                                  slot->tts_isnull,
-                                                  projInfo->pi_itemIsDone,
-                                                  isDone))
-                       ExecStoreVirtualTuple(slot);
+               if (!ExecTargetList(projInfo->pi_targetlist,
+                                                       econtext,
+                                                       slot->tts_values,
+                                                       slot->tts_isnull,
+                                                       projInfo->pi_itemIsDone,
+                                                       isDone))
+                       return slot;            /* no more result rows, return empty slot */
        }
 
-       return slot;
+       /*
+        * Successfully formed a result row.  Mark the result slot as containing a
+        * valid virtual tuple.
+        */
+       return ExecStoreVirtualTuple(slot);
 }
 
 #include "access/heapam.h"
 #include "catalog/index.h"
 #include "executor/execdebug.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
 int                    NIndexTupleProcessed;
 
 
+static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
 static void ShutdownExprContext(ExprContext *econtext);
 
 
                                                TupleDesc inputDesc)
 {
        ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-       int                     len;
-       bool            isVarList;
+       int                     len = ExecTargetListLength(targetList);
+       int                *workspace;
+       int                *varSlotOffsets;
+       int                *varNumbers;
+       int                *varOutputCols;
+       List       *exprlist;
+       int                     numSimpleVars;
+       bool            directMap;
        ListCell   *tl;
 
-       len = ExecTargetListLength(targetList);
-
-       projInfo->pi_targetlist = targetList;
        projInfo->pi_exprContext = econtext;
        projInfo->pi_slot = slot;
+       /* since these are all int arrays, we need do just one palloc */
+       workspace = (int *) palloc(len * 3 * sizeof(int));
+       projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
+       projInfo->pi_varNumbers = varNumbers = workspace + len;
+       projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
+       projInfo->pi_lastInnerVar = 0;
+       projInfo->pi_lastOuterVar = 0;
+       projInfo->pi_lastScanVar = 0;
 
        /*
-        * Determine whether the target list consists entirely of simple Var
-        * references (ie, references to non-system attributes) that match the
-        * input.  If so, we can use the simpler ExecVariableList instead of
-        * ExecTargetList.      (Note: if there is a type mismatch then ExecEvalVar
-        * will probably throw an error at runtime, but we leave that to it.)
+        * We separate the target list elements into simple Var references and
+        * expressions which require the full ExecTargetList machinery.  To be
+        * a simple Var, a Var has to be a user attribute and not mismatch the
+        * inputDesc.  (Note: if there is a type mismatch then ExecEvalVar will
+        * probably throw an error at runtime, but we leave that to it.)
         */
-       isVarList = true;
+       exprlist = NIL;
+       numSimpleVars = 0;
+       directMap = true;
        foreach(tl, targetList)
        {
                GenericExprState *gstate = (GenericExprState *) lfirst(tl);
                Var                *variable = (Var *) gstate->arg->expr;
-               Form_pg_attribute attr;
+               bool            isSimpleVar = false;
 
-               if (variable == NULL ||
-                       !IsA(variable, Var) ||
-                       variable->varattno <= 0)
-               {
-                       isVarList = false;
-                       break;
-               }
-               if (!inputDesc)
-                       continue;                       /* can't check type, assume OK */
-               if (variable->varattno > inputDesc->natts)
+               if (variable != NULL &&
+                       IsA(variable, Var) &&
+                       variable->varattno > 0)
                {
-                       isVarList = false;
-                       break;
-               }
-               attr = inputDesc->attrs[variable->varattno - 1];
-               if (attr->attisdropped || variable->vartype != attr->atttypid)
-               {
-                       isVarList = false;
-                       break;
-               }
-       }
-       projInfo->pi_isVarList = isVarList;
-
-       if (isVarList)
-       {
-               int                *varSlotOffsets;
-               int                *varNumbers;
-               AttrNumber      lastInnerVar = 0;
-               AttrNumber      lastOuterVar = 0;
-               AttrNumber      lastScanVar = 0;
+                       if (!inputDesc)
+                               isSimpleVar = true;             /* can't check type, assume OK */
+                       else if (variable->varattno <= inputDesc->natts)
+                       {
+                               Form_pg_attribute attr;
 
-               projInfo->pi_itemIsDone = NULL; /* not needed */
-               projInfo->pi_varSlotOffsets = varSlotOffsets = (int *)
-                       palloc0(len * sizeof(int));
-               projInfo->pi_varNumbers = varNumbers = (int *)
-                       palloc0(len * sizeof(int));
+                               attr = inputDesc->attrs[variable->varattno - 1];
+                               if (!attr->attisdropped && variable->vartype == attr->atttypid)
+                                       isSimpleVar = true;
+                       }
+               }
 
-               /*
-                * Set up the data needed by ExecVariableList.  The slots in which the
-                * variables can be found at runtime are denoted by the offsets of
-                * their slot pointers within the econtext.  This rather grotty
-                * representation is needed because the caller may not have given us
-                * the real econtext yet (see hacks in nodeSubplan.c).
-                */
-               foreach(tl, targetList)
+               if (isSimpleVar)
                {
-                       GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-                       Var                *variable = (Var *) gstate->arg->expr;
-                       AttrNumber      attnum = variable->varattno;
                        TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-                       AttrNumber      resind = tle->resno - 1;
+                       AttrNumber      attnum = variable->varattno;
 
-                       Assert(resind >= 0 && resind < len);
-                       varNumbers[resind] = attnum;
+                       varNumbers[numSimpleVars] = attnum;
+                       varOutputCols[numSimpleVars] = tle->resno;
+                       if (tle->resno != numSimpleVars+1)
+                               directMap = false;
 
                        switch (variable->varno)
                        {
                                case INNER:
-                                       varSlotOffsets[resind] = offsetof(ExprContext,
-                                                                                                         ecxt_innertuple);
-                                       lastInnerVar = Max(lastInnerVar, attnum);
+                                       varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
+                                                                                                                        ecxt_innertuple);
+                                       if (projInfo->pi_lastInnerVar < attnum)
+                                               projInfo->pi_lastInnerVar = attnum;
                                        break;
 
                                case OUTER:
-                                       varSlotOffsets[resind] = offsetof(ExprContext,
-                                                                                                         ecxt_outertuple);
-                                       lastOuterVar = Max(lastOuterVar, attnum);
+                                       varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
+                                                                                                                        ecxt_outertuple);
+                                       if (projInfo->pi_lastOuterVar < attnum)
+                                               projInfo->pi_lastOuterVar = attnum;
                                        break;
 
                                default:
-                                       varSlotOffsets[resind] = offsetof(ExprContext,
-                                                                                                         ecxt_scantuple);
-                                       lastScanVar = Max(lastScanVar, attnum);
+                                       varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
+                                                                                                                        ecxt_scantuple);
+                                       if (projInfo->pi_lastScanVar < attnum)
+                                               projInfo->pi_lastScanVar = attnum;
                                        break;
                        }
+                       numSimpleVars++;
+               }
+               else
+               {
+                       /* Not a simple variable, add it to generic targetlist */
+                       exprlist = lappend(exprlist, gstate);
+                       /* Examine expr to include contained Vars in lastXXXVar counts */
+                       get_last_attnums((Node *) variable, projInfo);
                }
-               projInfo->pi_lastInnerVar = lastInnerVar;
-               projInfo->pi_lastOuterVar = lastOuterVar;
-               projInfo->pi_lastScanVar = lastScanVar;
        }
+       projInfo->pi_targetlist = exprlist;
+       projInfo->pi_numSimpleVars = numSimpleVars;
+       projInfo->pi_directMap = directMap;
+
+       if (exprlist == NIL)
+               projInfo->pi_itemIsDone = NULL; /* not needed */
        else
-       {
                projInfo->pi_itemIsDone = (ExprDoneCond *)
                        palloc(len * sizeof(ExprDoneCond));
-               projInfo->pi_varSlotOffsets = NULL;
-               projInfo->pi_varNumbers = NULL;
-       }
 
        return projInfo;
 }
 
+/*
+ * get_last_attnums: expression walker for ExecBuildProjectionInfo
+ *
+ *     Update the lastXXXVar counts to be at least as large as the largest
+ *     attribute numbers found in the expression
+ */
+static bool
+get_last_attnums(Node *node, ProjectionInfo *projInfo)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var        *variable = (Var *) node;
+               AttrNumber      attnum = variable->varattno;
+
+               switch (variable->varno)
+               {
+                       case INNER:
+                               if (projInfo->pi_lastInnerVar < attnum)
+                                       projInfo->pi_lastInnerVar = attnum;
+                               break;
+
+                       case OUTER:
+                               if (projInfo->pi_lastOuterVar < attnum)
+                                       projInfo->pi_lastOuterVar = attnum;
+                               break;
+
+                       default:
+                               if (projInfo->pi_lastScanVar < attnum)
+                                       projInfo->pi_lastScanVar = attnum;
+                               break;
+               }
+               return false;
+       }
+       /*
+        * Don't examine the arguments of Aggrefs or WindowFuncs, because those
+        * do not represent expressions to be evaluated within the overall
+        * targetlist's econtext.
+        */
+       if (IsA(node, Aggref))
+               return false;
+       if (IsA(node, WindowFunc))
+               return false;
+       return expression_tree_walker(node, get_last_attnums,
+                                                                 (void *) projInfo);
+}
+
 /* ----------------
  *             ExecAssignProjectionInfo
  *
 
  *
  *             The planner very often produces tlists that consist entirely of
  *             simple Var references (lower levels of a plan tree almost always
- *             look like that).  So we have an optimization to handle that case
- *             with minimum overhead.
+ *             look like that).  And top-level tlists are often mostly Vars too.
+ *             We therefore optimize execution of simple-Var tlist entries.
+ *             The pi_targetlist list actually contains only the tlist entries that
+ *             aren't simple Vars, while those that are Vars are processed using the
+ *             varSlotOffsets/varNumbers/varOutputCols arrays.
  *
- *             targetlist              target list for projection
+ *             The lastXXXVar fields are used to optimize fetching of fields from
+ *             input tuples: they let us do a slot_getsomeattrs() call to ensure
+ *             that all needed attributes are extracted in one pass.
+ *
+ *             targetlist              target list for projection (non-Var expressions only)
  *             exprContext             expression context in which to evaluate targetlist
  *             slot                    slot to place projection result in
- *             itemIsDone              workspace for ExecProject
- *             isVarList               TRUE if simple-Var-list optimization applies
+ *             itemIsDone              workspace array for ExecProject
+ *             directMap               true if varOutputCols[] is an identity map
+ *             numSimpleVars   number of simple Vars found in original tlist
  *             varSlotOffsets  array indicating which slot each simple Var is from
- *             varNumbers              array indicating attr numbers of simple Vars
+ *             varNumbers              array containing input attr numbers of simple Vars
+ *             varOutputCols   array containing output attr numbers of simple Vars
  *             lastInnerVar    highest attnum from inner tuple slot (0 if none)
  *             lastOuterVar    highest attnum from outer tuple slot (0 if none)
  *             lastScanVar             highest attnum from scan tuple slot (0 if none)
        ExprContext *pi_exprContext;
        TupleTableSlot *pi_slot;
        ExprDoneCond *pi_itemIsDone;
-       bool            pi_isVarList;
+       bool            pi_directMap;
+       int                     pi_numSimpleVars;
        int                *pi_varSlotOffsets;
        int                *pi_varNumbers;
+       int                *pi_varOutputCols;
        int                     pi_lastInnerVar;
        int                     pi_lastOuterVar;
        int                     pi_lastScanVar;