mirror of
https://github.com/postgres/postgres.git
synced 2025-10-28 11:55:03 +03:00
of tuples when passing data up through multiple plan nodes. A slot can now hold either a normal "physical" HeapTuple, or a "virtual" tuple consisting of Datum/isnull arrays. Upper plan levels can usually just copy the Datum arrays, avoiding heap_formtuple() and possible subsequent nocachegetattr() calls to extract the data again. This work extends Atsushi Ogawa's earlier patch, which provided the key idea of adding Datum arrays to TupleTableSlots. (I believe however that something like this was foreseen way back in Berkeley days --- see the old comment on ExecProject.) A test case involving many levels of join of fairly wide tables (about 80 columns altogether) showed about 3x overall speedup, though simple queries will probably not be helped very much. I have also duplicated some code in heaptuple.c in order to provide versions of heap_formtuple and friends that use "bool" arrays to indicate null attributes, instead of the old convention of "char" arrays containing either 'n' or ' '. This provides a better match to the convention used by ExecEvalExpr. While I have not made a concerted effort to get rid of uses of the old routines, I think they should be deprecated and eventually removed.
233 lines
6.4 KiB
C
233 lines
6.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* execScan.c
|
|
* This code provides support for generalized relation scans. ExecScan
|
|
* is passed a node and a pointer to a function to "do the right thing"
|
|
* and return a tuple from the relation. ExecScan then does the tedious
|
|
* stuff - checking the qualification and projecting the tuple
|
|
* appropriately.
|
|
*
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.35 2005/03/16 21:38:06 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "executor/executor.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/memutils.h"
|
|
|
|
|
|
static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecScan
|
|
*
|
|
* Scans the relation using the 'access method' indicated and
|
|
* returns the next qualifying tuple in the direction specified
|
|
* in the global variable ExecDirection.
|
|
* The access method returns the next tuple and execScan() is
|
|
* responsible for checking the tuple returned against the qual-clause.
|
|
*
|
|
* Conditions:
|
|
* -- the "cursor" maintained by the AMI is positioned at the tuple
|
|
* returned previously.
|
|
*
|
|
* Initial States:
|
|
* -- the relation indicated is opened for scanning so that the
|
|
* "cursor" is positioned before the first qualifying tuple.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleTableSlot *
|
|
ExecScan(ScanState *node,
|
|
ExecScanAccessMtd accessMtd) /* function returning a tuple */
|
|
{
|
|
EState *estate;
|
|
ExprContext *econtext;
|
|
List *qual;
|
|
ProjectionInfo *projInfo;
|
|
ExprDoneCond isDone;
|
|
TupleTableSlot *resultSlot;
|
|
|
|
/*
|
|
* Fetch data from 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
|
|
* scan tuple (because there is a function-returning-set in the
|
|
* projection expressions). If so, try to project another one.
|
|
*/
|
|
if (node->ps.ps_TupFromTlist)
|
|
{
|
|
Assert(projInfo); /* can't get here if not projecting */
|
|
resultSlot = ExecProject(projInfo, &isDone);
|
|
if (isDone == ExprMultipleResult)
|
|
return resultSlot;
|
|
/* Done with that source tuple... */
|
|
node->ps.ps_TupFromTlist = false;
|
|
}
|
|
|
|
/*
|
|
* Reset per-tuple memory context to free any expression evaluation
|
|
* storage allocated in the previous tuple cycle. Note this can't
|
|
* happen until we're done projecting out tuples from a scan tuple.
|
|
*/
|
|
ResetExprContext(econtext);
|
|
|
|
/*
|
|
* get a tuple from the access method loop until we obtain a tuple
|
|
* which passes the qualification.
|
|
*/
|
|
for (;;)
|
|
{
|
|
TupleTableSlot *slot;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
slot = (*accessMtd) (node);
|
|
|
|
/*
|
|
* if the slot returned by the accessMtd contains NULL, then it
|
|
* means there is nothing more to scan so we just return an empty
|
|
* slot, being careful to use the projection result slot so it has
|
|
* correct tupleDesc.
|
|
*/
|
|
if (TupIsNull(slot))
|
|
{
|
|
if (projInfo)
|
|
return ExecClearTuple(projInfo->pi_slot);
|
|
else
|
|
return slot;
|
|
}
|
|
|
|
/*
|
|
* place the current tuple into the expr context
|
|
*/
|
|
econtext->ecxt_scantuple = slot;
|
|
|
|
/*
|
|
* check that the current tuple satisfies the qual-clause
|
|
*
|
|
* check for non-nil qual here to avoid a function call to ExecQual()
|
|
* when the qual is nil ... saves only a few cycles, but they add
|
|
* up ...
|
|
*/
|
|
if (!qual || ExecQual(qual, econtext, false))
|
|
{
|
|
/*
|
|
* Found a satisfactory scan tuple.
|
|
*/
|
|
if (projInfo)
|
|
{
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Tuple fails qual, so free per-tuple memory and try again.
|
|
*/
|
|
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(&node->ps,
|
|
scan->plan.targetlist,
|
|
scan->scanrelid,
|
|
node->ss_ScanTupleSlot->tts_tupleDescriptor))
|
|
node->ps.ps_ProjInfo = NULL;
|
|
else
|
|
ExecAssignProjectionInfo(&node->ps);
|
|
}
|
|
|
|
static bool
|
|
tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
|
|
{
|
|
int numattrs = tupdesc->natts;
|
|
int attrno;
|
|
bool hasoid;
|
|
ListCell *tlist_item = list_head(tlist);
|
|
|
|
/* Check the tlist attributes */
|
|
for (attrno = 1; attrno <= numattrs; attrno++)
|
|
{
|
|
Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
|
|
Var *var;
|
|
|
|
if (tlist_item == NULL)
|
|
return false; /* tlist too short */
|
|
var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
|
|
if (!var || !IsA(var, Var))
|
|
return false; /* tlist item not a Var */
|
|
Assert(var->varno == varno);
|
|
Assert(var->varlevelsup == 0);
|
|
if (var->varattno != attrno)
|
|
return false; /* out of order */
|
|
if (att_tup->attisdropped)
|
|
return false; /* table contains dropped columns */
|
|
Assert(var->vartype == att_tup->atttypid);
|
|
Assert(var->vartypmod == att_tup->atttypmod);
|
|
|
|
tlist_item = lnext(tlist_item);
|
|
}
|
|
|
|
if (tlist_item)
|
|
return false; /* tlist too long */
|
|
|
|
/*
|
|
* If the plan context requires a particular hasoid setting, then that
|
|
* has to match, too.
|
|
*/
|
|
if (ExecContextForcesOids(ps, &hasoid) &&
|
|
hasoid != tupdesc->tdhasoid)
|
|
return false;
|
|
|
|
return true;
|
|
}
|