mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +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.
365 lines
9.3 KiB
C
365 lines
9.3 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeSeqscan.c
|
|
* Support routines for sequential scans of relations.
|
|
*
|
|
* 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/nodeSeqscan.c,v 1.52 2005/03/16 21:38:07 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* ExecSeqScan sequentially scans a relation.
|
|
* ExecSeqNext retrieve next tuple in sequential order.
|
|
* ExecInitSeqScan creates and initializes a seqscan node.
|
|
* ExecEndSeqScan releases any storage allocated.
|
|
* ExecSeqReScan rescans the relation
|
|
* ExecSeqMarkPos marks scan position
|
|
* ExecSeqRestrPos restores scan position
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/heapam.h"
|
|
#include "executor/execdebug.h"
|
|
#include "executor/nodeSeqscan.h"
|
|
#include "parser/parsetree.h"
|
|
|
|
static void InitScanRelation(SeqScanState *node, EState *estate);
|
|
static TupleTableSlot *SeqNext(SeqScanState *node);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Scan Support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* ----------------------------------------------------------------
|
|
* SeqNext
|
|
*
|
|
* This is a workhorse for ExecSeqScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot *
|
|
SeqNext(SeqScanState *node)
|
|
{
|
|
HeapTuple tuple;
|
|
HeapScanDesc scandesc;
|
|
Index scanrelid;
|
|
EState *estate;
|
|
ScanDirection direction;
|
|
TupleTableSlot *slot;
|
|
|
|
/*
|
|
* get information from the estate and scan state
|
|
*/
|
|
estate = node->ps.state;
|
|
scandesc = node->ss_currentScanDesc;
|
|
scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
|
|
direction = estate->es_direction;
|
|
slot = node->ss_ScanTupleSlot;
|
|
|
|
/*
|
|
* Clear any reference to the previously returned tuple. The idea
|
|
* here is to not have the tuple slot be the last holder of a pin on
|
|
* that tuple's buffer; if it is, we'll need a separate visit to the
|
|
* bufmgr to release the buffer. By clearing here, we get to have the
|
|
* release done by ReleaseAndReadBuffer inside heap_getnext.
|
|
*/
|
|
ExecClearTuple(slot);
|
|
|
|
/*
|
|
* Check if we are evaluating PlanQual for tuple of this relation.
|
|
* Additional checking is not good, but no other way for now. We could
|
|
* introduce new nodes for this case and handle SeqScan --> NewNode
|
|
* switching in Init/ReScan plan...
|
|
*/
|
|
if (estate->es_evTuple != NULL &&
|
|
estate->es_evTuple[scanrelid - 1] != NULL)
|
|
{
|
|
if (estate->es_evTupleNull[scanrelid - 1])
|
|
return slot; /* return empty slot */
|
|
|
|
ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
|
|
slot, InvalidBuffer, false);
|
|
|
|
/*
|
|
* Note that unlike IndexScan, SeqScan never use keys in
|
|
* heap_beginscan (and this is very bad) - so, here we do not
|
|
* check are keys ok or not.
|
|
*/
|
|
|
|
/* Flag for the next call that no more tuples */
|
|
estate->es_evTupleNull[scanrelid - 1] = true;
|
|
return (slot);
|
|
}
|
|
|
|
/*
|
|
* get the next tuple from the access methods
|
|
*/
|
|
tuple = heap_getnext(scandesc, direction);
|
|
|
|
/*
|
|
* save the tuple and the buffer returned to us by the access methods
|
|
* in our scan tuple slot and return the slot. Note: we pass 'false'
|
|
* because tuples returned by heap_getnext() are pointers onto disk
|
|
* pages and were not created with palloc() and so should not be
|
|
* pfree()'d. Note also that ExecStoreTuple will increment the
|
|
* refcount of the buffer; the refcount will not be dropped until the
|
|
* tuple table slot is cleared.
|
|
*/
|
|
if (tuple)
|
|
ExecStoreTuple(tuple, /* tuple to store */
|
|
slot, /* slot to store in */
|
|
scandesc->rs_cbuf, /* buffer associated with
|
|
* this tuple */
|
|
false); /* don't pfree this pointer */
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSeqScan(node)
|
|
*
|
|
* Scans the relation sequentially and returns the next qualifying
|
|
* tuple.
|
|
* It calls the ExecScan() routine and passes it the access method
|
|
* which retrieve tuples sequentially.
|
|
*
|
|
*/
|
|
|
|
TupleTableSlot *
|
|
ExecSeqScan(SeqScanState *node)
|
|
{
|
|
/*
|
|
* use SeqNext as access method
|
|
*/
|
|
return ExecScan((ScanState *) node, (ExecScanAccessMtd) SeqNext);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* InitScanRelation
|
|
*
|
|
* This does the initialization for scan relations and
|
|
* subplans of scans.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static void
|
|
InitScanRelation(SeqScanState *node, EState *estate)
|
|
{
|
|
Index relid;
|
|
List *rangeTable;
|
|
RangeTblEntry *rtentry;
|
|
Oid reloid;
|
|
Relation currentRelation;
|
|
HeapScanDesc currentScanDesc;
|
|
|
|
/*
|
|
* get the relation object id from the relid'th entry in the range
|
|
* table, open that relation and initialize the scan state.
|
|
*
|
|
* We acquire AccessShareLock for the duration of the scan.
|
|
*/
|
|
relid = ((SeqScan *) node->ps.plan)->scanrelid;
|
|
rangeTable = estate->es_range_table;
|
|
rtentry = rt_fetch(relid, rangeTable);
|
|
reloid = rtentry->relid;
|
|
|
|
currentRelation = heap_open(reloid, AccessShareLock);
|
|
|
|
currentScanDesc = heap_beginscan(currentRelation,
|
|
estate->es_snapshot,
|
|
0,
|
|
NULL);
|
|
|
|
node->ss_currentRelation = currentRelation;
|
|
node->ss_currentScanDesc = currentScanDesc;
|
|
|
|
ExecAssignScanType(node, RelationGetDescr(currentRelation), false);
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitSeqScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
SeqScanState *
|
|
ExecInitSeqScan(SeqScan *node, EState *estate)
|
|
{
|
|
SeqScanState *scanstate;
|
|
|
|
/*
|
|
* Once upon a time it was possible to have an outerPlan of a SeqScan,
|
|
* but not any more.
|
|
*/
|
|
Assert(outerPlan(node) == NULL);
|
|
Assert(innerPlan(node) == NULL);
|
|
|
|
/*
|
|
* create state structure
|
|
*/
|
|
scanstate = makeNode(SeqScanState);
|
|
scanstate->ps.plan = (Plan *) node;
|
|
scanstate->ps.state = estate;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
ExecAssignExprContext(estate, &scanstate->ps);
|
|
|
|
/*
|
|
* initialize child expressions
|
|
*/
|
|
scanstate->ps.targetlist = (List *)
|
|
ExecInitExpr((Expr *) node->plan.targetlist,
|
|
(PlanState *) scanstate);
|
|
scanstate->ps.qual = (List *)
|
|
ExecInitExpr((Expr *) node->plan.qual,
|
|
(PlanState *) scanstate);
|
|
|
|
#define SEQSCAN_NSLOTS 2
|
|
|
|
/*
|
|
* tuple table initialization
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &scanstate->ps);
|
|
ExecInitScanTupleSlot(estate, scanstate);
|
|
|
|
/*
|
|
* initialize scan relation
|
|
*/
|
|
InitScanRelation(scanstate, estate);
|
|
|
|
scanstate->ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* Initialize result tuple type and projection info.
|
|
*/
|
|
ExecAssignResultTypeFromTL(&scanstate->ps);
|
|
ExecAssignScanProjectionInfo(scanstate);
|
|
|
|
return scanstate;
|
|
}
|
|
|
|
int
|
|
ExecCountSlotsSeqScan(SeqScan *node)
|
|
{
|
|
return ExecCountSlotsNode(outerPlan(node)) +
|
|
ExecCountSlotsNode(innerPlan(node)) +
|
|
SEQSCAN_NSLOTS;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndSeqScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndSeqScan(SeqScanState *node)
|
|
{
|
|
Relation relation;
|
|
HeapScanDesc scanDesc;
|
|
|
|
/*
|
|
* get information from node
|
|
*/
|
|
relation = node->ss_currentRelation;
|
|
scanDesc = node->ss_currentScanDesc;
|
|
|
|
/*
|
|
* Free the exprcontext
|
|
*/
|
|
ExecFreeExprContext(&node->ps);
|
|
|
|
/*
|
|
* clean out the tuple table
|
|
*/
|
|
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
|
ExecClearTuple(node->ss_ScanTupleSlot);
|
|
|
|
/*
|
|
* close heap scan
|
|
*/
|
|
heap_endscan(scanDesc);
|
|
|
|
/*
|
|
* close the heap relation.
|
|
*
|
|
* Currently, we do not release the AccessShareLock acquired by
|
|
* InitScanRelation. This lock should be held till end of
|
|
* transaction. (There is a faction that considers this too much
|
|
* locking, however.)
|
|
*/
|
|
heap_close(relation, NoLock);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Join Support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSeqReScan
|
|
*
|
|
* Rescans the relation.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecSeqReScan(SeqScanState *node, ExprContext *exprCtxt)
|
|
{
|
|
EState *estate;
|
|
Index scanrelid;
|
|
HeapScanDesc scan;
|
|
|
|
estate = node->ps.state;
|
|
scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
|
|
|
|
/* If this is re-scanning of PlanQual ... */
|
|
if (estate->es_evTuple != NULL &&
|
|
estate->es_evTuple[scanrelid - 1] != NULL)
|
|
{
|
|
estate->es_evTupleNull[scanrelid - 1] = false;
|
|
return;
|
|
}
|
|
|
|
scan = node->ss_currentScanDesc;
|
|
|
|
heap_rescan(scan, /* scan desc */
|
|
NULL); /* new scan keys */
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSeqMarkPos(node)
|
|
*
|
|
* Marks scan position.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecSeqMarkPos(SeqScanState *node)
|
|
{
|
|
HeapScanDesc scan;
|
|
|
|
scan = node->ss_currentScanDesc;
|
|
heap_markpos(scan);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSeqRestrPos
|
|
*
|
|
* Restores scan position.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecSeqRestrPos(SeqScanState *node)
|
|
{
|
|
HeapScanDesc scan;
|
|
|
|
scan = node->ss_currentScanDesc;
|
|
heap_restrpos(scan);
|
|
}
|