diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-02-03 15:07:08 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-02-03 15:07:08 +0000 |
commit | 4cff59d8d540c441fb0c22dfaa517bc25aa5f794 (patch) | |
tree | 88b4c216d219582904d3d9d6a236bb9468fe148c /src/backend/executor/execScan.c | |
parent | 0d3e36b6687ae601551fb8047c10a68f2d6fb565 (diff) | |
download | postgresql-4cff59d8d540c441fb0c22dfaa517bc25aa5f794.tar.gz postgresql-4cff59d8d540c441fb0c22dfaa517bc25aa5f794.zip |
Tweak planner and executor to avoid doing ExecProject() in table scan
nodes where it's not really necessary. In many cases where the scan node
is not the topmost plan node (eg, joins, aggregation), it's possible to
just return the table tuple directly instead of generating an intermediate
projection tuple. In preliminary testing, this reduced the CPU time
needed for 'SELECT COUNT(*) FROM foo' by about 10%.
Diffstat (limited to 'src/backend/executor/execScan.c')
-rw-r--r-- | src/backend/executor/execScan.c | 108 |
1 files changed, 92 insertions, 16 deletions
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 6944e03e9bc..9352c79d81e 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -12,19 +12,20 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.22 2002/12/05 15:50:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.23 2003/02/03 15:07:07 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include <sys/file.h> - #include "executor/executor.h" #include "miscadmin.h" #include "utils/memutils.h" +static bool tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc); + + /* ---------------------------------------------------------------- * ExecScan * @@ -50,6 +51,7 @@ ExecScan(ScanState *node, EState *estate; ExprContext *econtext; List *qual; + ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -59,6 +61,7 @@ ExecScan(ScanState *node, estate = node->ps.state; econtext = node->ps.ps_ExprContext; qual = node->ps.qual; + projInfo = node->ps.ps_ProjInfo; /* * Check to see if we're still projecting out tuples from a previous @@ -67,7 +70,8 @@ ExecScan(ScanState *node, */ if (node->ps.ps_TupFromTlist) { - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); + Assert(projInfo); /* can't get here if not projecting */ + resultSlot = ExecProject(projInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ @@ -101,10 +105,13 @@ ExecScan(ScanState *node, */ if (TupIsNull(slot)) { - return ExecStoreTuple(NULL, - node->ps.ps_ProjInfo->pi_slot, - InvalidBuffer, - true); + if (projInfo) + return ExecStoreTuple(NULL, + projInfo->pi_slot, + InvalidBuffer, + true); + else + return slot; } /* @@ -123,16 +130,27 @@ ExecScan(ScanState *node, { /* * Found a satisfactory scan tuple. - * - * Form a projection tuple, store it in the result tuple slot and - * return it --- unless we find we can project no tuples from - * this scan tuple, in which case continue scan. */ - resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); - if (isDone != ExprEndResult) + if (projInfo) { - node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); - return resultSlot; + /* + * Form a projection tuple, store it in the result tuple slot + * and return it --- unless we find we can project no tuples + * from this scan tuple, in which case continue scan. + */ + resultSlot = ExecProject(projInfo, &isDone); + if (isDone != ExprEndResult) + { + node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + return resultSlot; + } + } + else + { + /* + * Here, we aren't projecting, so just return scan tuple. + */ + return slot; } } @@ -142,3 +160,61 @@ ExecScan(ScanState *node, ResetExprContext(econtext); } } + +/* + * ExecAssignScanProjectionInfo + * Set up projection info for a scan node, if necessary. + * + * We can avoid a projection step if the requested tlist exactly matches + * the underlying tuple type. If so, we just set ps_ProjInfo to NULL. + * Note that this case occurs not only for simple "SELECT * FROM ...", but + * also in most cases where there are joins or other processing nodes above + * the scan node, because the planner will preferentially generate a matching + * tlist. + * + * ExecAssignScanType must have been called already. + */ +void +ExecAssignScanProjectionInfo(ScanState *node) +{ + Scan *scan = (Scan *) node->ps.plan; + + if (tlist_matches_tupdesc(scan->plan.targetlist, + scan->scanrelid, + node->ss_ScanTupleSlot->ttc_tupleDescriptor)) + node->ps.ps_ProjInfo = NULL; + else + ExecAssignProjectionInfo(&node->ps); +} + +static bool +tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc) +{ + int numattrs = tupdesc->natts; + int attrno; + + for (attrno = 1; attrno <= numattrs; attrno++) + { + Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1]; + Var *var; + + if (tlist == NIL) + return false; /* tlist too short */ + var = (Var *) ((TargetEntry *) lfirst(tlist))->expr; + if (!var || !IsA(var, Var)) + return false; /* tlist item not a Var */ + Assert(var->varno == varno); + if (var->varattno != attrno) + return false; /* out of order */ + Assert(var->vartype == att_tup->atttypid); + Assert(var->vartypmod == att_tup->atttypmod); + Assert(var->varlevelsup == 0); + + tlist = lnext(tlist); + } + + if (tlist) + return false; /* tlist too long */ + + return true; +} |