1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-14 18:42:34 +03:00

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%.
This commit is contained in:
Tom Lane
2003-02-03 15:07:08 +00:00
parent 0d3e36b668
commit 4cff59d8d5
12 changed files with 476 additions and 230 deletions

View File

@ -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;
}