Refactor ExecProject and associated routines so that fast-path code is used

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.
This commit is contained in:
Tom Lane 2009-04-02 22:39:30 +00:00
parent 0e550ff617
commit 85369f888e
3 changed files with 225 additions and 154 deletions

View 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)
@ -5104,50 +5102,6 @@ ExecTargetList(List *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
*
@ -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_isVarList)
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 ...
*/
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);
}
else
{
if (ExecTargetList(projInfo->pi_targetlist,
projInfo->pi_exprContext,
slot->tts_values,
slot->tts_isnull,
projInfo->pi_itemIsDone,
isDone))
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];
}
}
}
return slot;
/*
* 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,
econtext,
slot->tts_values,
slot->tts_isnull,
projInfo->pi_itemIsDone,
isDone))
return slot; /* no more result rows, return empty slot */
}
/*
* Successfully formed a result row. Mark the result slot as containing a
* valid virtual tuple.
*/
return ExecStoreVirtualTuple(slot);
}

View 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)
if (variable != NULL &&
IsA(variable, Var) &&
variable->varattno > 0)
{
isVarList = false;
break;
if (!inputDesc)
isSimpleVar = true; /* can't check type, assume OK */
else if (variable->varattno <= inputDesc->natts)
{
Form_pg_attribute attr;
attr = inputDesc->attrs[variable->varattno - 1];
if (!attr->attisdropped && variable->vartype == attr->atttypid)
isSimpleVar = true;
}
}
if (!inputDesc)
continue; /* can't check type, assume OK */
if (variable->varattno > inputDesc->natts)
{
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;
projInfo->pi_itemIsDone = NULL; /* not needed */
projInfo->pi_varSlotOffsets = varSlotOffsets = (int *)
palloc0(len * sizeof(int));
projInfo->pi_varNumbers = varNumbers = (int *)
palloc0(len * sizeof(int));
/*
* 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
*

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