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) @@ -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); } 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) + 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 * 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;