From 85369f888ec9af8ed573c589b7d6fe28015a6cb6 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 2 Apr 2009 22:39:30 +0000 Subject: [PATCH] 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. --- src/backend/executor/execQual.c | 150 ++++++++++++++++------------- src/backend/executor/execUtils.c | 198 ++++++++++++++++++++++++--------------- src/include/nodes/execnodes.h | 27 ++++-- 3 files changed, 223 insertions(+), 152 deletions(-) diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d66bf4da82..21a9eb1753 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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); } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 995e1477e3..4b3d92d48b 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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 * diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 996efa8f87..cb293a0be5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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; -- 2.11.0