OSDN Git Service

Refactor ExecProject and associated routines so that fast-path code is used
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 2 Apr 2009 22:39:30 +0000 (22:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 2 Apr 2009 22:39:30 +0000 (22:39 +0000)
for simple Var targetlist entries all the time, even when there are other
entries that are not simple Vars.  Also, ensure that we prefetch attributes
(with slot_getsomeattrs) for all Vars in the targetlist, even those buried
within expressions.  In combination these changes seem to significantly
reduce the runtime for cases where tlists are mostly but not exclusively
Vars.  Per my proposal of yesterday.

src/backend/executor/execQual.c
src/backend/executor/execUtils.c
src/include/nodes/execnodes.h

index d66bf4d..21a9eb1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.243 2009/03/27 18:30:21 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.244 2009/04/02 22:39:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4966,6 +4966,7 @@ ExecCleanTargetListLength(List *targetlist)
  * 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,
@@ -4987,9 +4988,6 @@ 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)
@@ -5105,50 +5103,6 @@ ExecTargetList(List *targetlist,
 }
 
 /*
- * 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
  *
  *             projects a tuple based on projection info and stores
@@ -5165,6 +5119,8 @@ TupleTableSlot *
 ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
+       ExprContext *econtext;
+       int                     numSimpleVars;
 
        /*
         * sanity checks
@@ -5175,6 +5131,11 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
         * 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
@@ -5184,29 +5145,84 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
        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);
 }
index 995e147..4b3d92d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.157 2009/01/01 17:23:41 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.158 2009/04/02 22:39:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,6 +46,7 @@
 #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"
@@ -66,6 +67,7 @@ int                   NIndexTupleInserted;
 int                    NIndexTupleProcessed;
 
 
+static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
 static void ShutdownExprContext(ExprContext *econtext);
 
 
@@ -559,121 +561,163 @@ ExecBuildProjectionInfo(List *targetList,
                                                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
  *
index 996efa8..cb293a0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.202 2009/03/21 00:04:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.203 2009/04/02 22:39:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -201,16 +201,25 @@ typedef struct ReturnSetInfo
  *
  *             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)
@@ -223,9 +232,11 @@ typedef struct ProjectionInfo
        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;