postgresql/src/backend/executor/nodeBitmapHeapscan.c

501 lines
13 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* nodeBitmapHeapscan.c
* Routines to support bitmapped scans of relations
*
* NOTE: it is critical that this plan type only be used with MVCC-compliant
* snapshots (ie, regular snapshots, not SnapshotNow or one of the other
* special snapshots). The reason is that since index and heap scans are
* decoupled, there can be no assurance that the index tuple prompting a
* visit to a particular heap TID still exists when the visit is made.
* Therefore the tuple might not exist anymore either (which is OK because
* heap_fetch will cope) --- but worse, the tuple slot could have been
* re-used for a newer tuple. With an MVCC snapshot the newer tuple is
* certain to fail the time qual and so it will not be mistakenly returned.
* With SnapshotNow we might return a tuple that doesn't meet the required
* index qual conditions.
*
*
* 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/nodeBitmapHeapscan.c,v 1.2 2005/05/06 17:24:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecBitmapHeapScan scans a relation using bitmap info
* ExecBitmapHeapNext workhorse for above
* ExecInitBitmapHeapScan creates and initializes state info.
* ExecBitmapHeapReScan prepares to rescan the plan.
* ExecEndBitmapHeapScan releases all storage.
*/
#include "postgres.h"
#include "access/heapam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
#include "parser/parsetree.h"
#include "utils/memutils.h"
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
/* ----------------------------------------------------------------
* BitmapHeapNext
*
* Retrieve next tuple from the BitmapHeapScan node's currentRelation
* ----------------------------------------------------------------
*/
static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
EState *estate;
ExprContext *econtext;
HeapScanDesc scandesc;
Index scanrelid;
TIDBitmap *tbm;
TBMIterateResult *tbmres;
OffsetNumber targoffset;
TupleTableSlot *slot;
/*
* extract necessary information from index scan node
*/
estate = node->ss.ps.state;
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scandesc = node->ss.ss_currentScanDesc;
scanrelid = ((BitmapHeapScan *) node->ss.ps.plan)->scan.scanrelid;
tbm = node->tbm;
tbmres = node->tbmres;
/*
* 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, below.
*/
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 IndexScan --> 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);
/* Does the tuple meet the original qual conditions? */
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->bitmapqualorig, econtext, false))
ExecClearTuple(slot); /* would not be returned by scan */
/* Flag for the next call that no more tuples */
estate->es_evTupleNull[scanrelid - 1] = true;
return slot;
}
/*
* If we haven't yet performed the underlying index scan, do it,
* and prepare the bitmap to be iterated over.
*/
if (tbm == NULL)
{
tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
if (!tbm || !IsA(tbm, TIDBitmap))
elog(ERROR, "unrecognized result from subplan");
node->tbm = tbm;
node->tbmres = tbmres = NULL;
tbm_begin_iterate(tbm);
}
for (;;)
{
/*
* Get next page of results if needed
*/
if (tbmres == NULL)
{
node->tbmres = tbmres = tbm_iterate(tbm);
if (tbmres == NULL)
{
/* no more entries in the bitmap */
break;
}
/*
* Ignore any claimed entries past what we think is the end of
* the relation. (This is probably not necessary given that we
* got AccessShareLock before performing any of the indexscans,
* but let's be safe.)
*/
if (tbmres->blockno >= scandesc->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
}
/*
* Acquire pin on the current heap page. We'll hold the pin
* until done looking at the page. We trade in any pin we
* held before.
*/
scandesc->rs_cbuf = ReleaseAndReadBuffer(scandesc->rs_cbuf,
scandesc->rs_rd,
tbmres->blockno);
/*
* Determine how many entries we need to look at on this page.
* If the bitmap is lossy then we need to look at each physical
* item pointer; otherwise we just look through the offsets
* listed in tbmres.
*/
if (tbmres->ntuples >= 0)
{
/* non-lossy case */
node->minslot = 0;
node->maxslot = tbmres->ntuples - 1;
}
else
{
/* lossy case */
Page dp;
LockBuffer(scandesc->rs_cbuf, BUFFER_LOCK_SHARE);
dp = (Page) BufferGetPage(scandesc->rs_cbuf);
node->minslot = FirstOffsetNumber;
node->maxslot = PageGetMaxOffsetNumber(dp);
LockBuffer(scandesc->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
/*
* Set curslot to first slot to examine
*/
node->curslot = node->minslot;
}
else
{
/*
* Continuing in previously obtained page; advance curslot
*/
node->curslot++;
}
/*
* Out of range? If so, nothing more to look at on this page
*/
if (node->curslot < node->minslot || node->curslot > node->maxslot)
{
node->tbmres = tbmres = NULL;
continue;
}
/*
* Okay to try to fetch the tuple
*/
if (tbmres->ntuples >= 0)
{
/* non-lossy case */
targoffset = tbmres->offsets[node->curslot];
}
else
{
/* lossy case */
targoffset = (OffsetNumber) node->curslot;
}
ItemPointerSet(&scandesc->rs_ctup.t_self, tbmres->blockno, targoffset);
/*
* Fetch the heap tuple and see if it matches the snapshot.
* We use heap_release_fetch to avoid useless bufmgr traffic.
*/
if (heap_release_fetch(scandesc->rs_rd,
scandesc->rs_snapshot,
&scandesc->rs_ctup,
&scandesc->rs_cbuf,
true,
&scandesc->rs_pgstat_info))
{
/*
* Set up the result slot to point to this tuple.
* Note that the slot acquires a pin on the buffer.
*/
ExecStoreTuple(&scandesc->rs_ctup,
slot,
scandesc->rs_cbuf,
false);
/*
* If we are using lossy info, we have to recheck the qual
* conditions at every tuple.
*/
if (tbmres->ntuples < 0)
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->bitmapqualorig, econtext, false))
{
/* Fails recheck, so drop it and loop back for another */
ExecClearTuple(slot);
continue;
}
}
/* OK to return this tuple */
return slot;
}
/*
* Failed the snap, so loop back and try again.
*/
}
/*
* if we get here it means we are at the end of the scan..
*/
return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
* ExecBitmapHeapScan(node)
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecBitmapHeapScan(BitmapHeapScanState *node)
{
/*
* use BitmapHeapNext as access method
*/
return ExecScan(&node->ss, (ExecScanAccessMtd) BitmapHeapNext);
}
/* ----------------------------------------------------------------
* ExecBitmapHeapReScan(node)
* ----------------------------------------------------------------
*/
void
ExecBitmapHeapReScan(BitmapHeapScanState *node, ExprContext *exprCtxt)
{
EState *estate;
Index scanrelid;
estate = node->ss.ps.state;
scanrelid = ((BitmapHeapScan *) node->ss.ps.plan)->scan.scanrelid;
/*
* If we are being passed an outer tuple, link it into the "regular"
* per-tuple econtext for possible qual eval.
*/
if (exprCtxt != NULL)
{
ExprContext *stdecontext;
stdecontext = node->ss.ps.ps_ExprContext;
stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
}
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
estate->es_evTuple[scanrelid - 1] != NULL)
{
estate->es_evTupleNull[scanrelid - 1] = false;
}
/* rescan to release any page pin */
heap_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbm)
tbm_free(node->tbm);
node->tbm = NULL;
node->tbmres = NULL;
/*
* Always rescan the input immediately, to ensure we can pass down
* any outer tuple that might be used in index quals.
*/
ExecReScan(outerPlanState(node), exprCtxt);
}
/* ----------------------------------------------------------------
* ExecEndBitmapHeapScan
* ----------------------------------------------------------------
*/
void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
HeapScanDesc scanDesc;
/*
* extract information from the node
*/
relation = node->ss.ss_currentRelation;
scanDesc = node->ss.ss_currentScanDesc;
/*
* Free the exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
/*
* clear out tuple table slots
*/
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
/*
* close down subplans
*/
ExecEndNode(outerPlanState(node));
/*
* release bitmap if any
*/
if (node->tbm)
tbm_free(node->tbm);
/*
* close heap scan
*/
heap_endscan(scanDesc);
/*
* close the heap relation.
*
* Currently, we do not release the AccessShareLock acquired by
* ExecInitBitmapHeapScan. This lock should be held till end of
* transaction. (There is a faction that considers this too much
* locking, however.)
*/
heap_close(relation, NoLock);
}
/* ----------------------------------------------------------------
* ExecInitBitmapHeapScan
*
* Initializes the scan's state information.
* ----------------------------------------------------------------
*/
BitmapHeapScanState *
ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate)
{
BitmapHeapScanState *scanstate;
RangeTblEntry *rtentry;
Index relid;
Oid reloid;
Relation currentRelation;
/*
* create state structure
*/
scanstate = makeNode(BitmapHeapScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
scanstate->tbm = NULL;
scanstate->tbmres = NULL;
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->bitmapqualorig = (List *)
ExecInitExpr((Expr *) node->bitmapqualorig,
(PlanState *) scanstate);
/*
* initialize child nodes
*/
outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate);
#define BITMAPHEAPSCAN_NSLOTS 2
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
CXT1_printf("ExecInitBitmapHeapScan: context is %d\n", CurrentMemoryContext);
/*
* open the base relation and acquire AccessShareLock on it.
*/
relid = node->scan.scanrelid;
rtentry = rt_fetch(relid, estate->es_range_table);
reloid = rtentry->relid;
currentRelation = heap_open(reloid, AccessShareLock);
scanstate->ss.ss_currentRelation = currentRelation;
/*
* Even though we aren't going to do a conventional seqscan, it is
* useful to create a HeapScanDesc --- this checks the relation size
* and sets up statistical infrastructure for us.
*/
scanstate->ss.ss_currentScanDesc = heap_beginscan(currentRelation,
estate->es_snapshot,
0,
NULL);
/*
* get the scan type from the relation descriptor.
*/
ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation), false);
/*
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
* all done.
*/
return scanstate;
}
int
ExecCountSlotsBitmapHeapScan(BitmapHeapScan *node)
{
return ExecCountSlotsNode(outerPlan((Plan *) node)) +
ExecCountSlotsNode(innerPlan((Plan *) node)) + BITMAPHEAPSCAN_NSLOTS;
}