1 /*-------------------------------------------------------------------------
4 * Routines to support bitmapped scans of relations
6 * NOTE: it is critical that this plan type only be used with MVCC-compliant
7 * snapshots (ie, regular snapshots, not SnapshotNow or one of the other
8 * special snapshots). The reason is that since index and heap scans are
9 * decoupled, there can be no assurance that the index tuple prompting a
10 * visit to a particular heap TID still exists when the visit is made.
11 * Therefore the tuple might not exist anymore either (which is OK because
12 * heap_fetch will cope) --- but worse, the tuple slot could have been
13 * re-used for a newer tuple. With an MVCC snapshot the newer tuple is
14 * certain to fail the time qual and so it will not be mistakenly returned.
15 * With SnapshotNow we might return a tuple that doesn't meet the required
16 * index qual conditions.
19 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
20 * Portions Copyright (c) 1994, Regents of the University of California
24 * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapHeapscan.c,v 1.28 2008/05/13 15:44:08 momjian Exp $
26 *-------------------------------------------------------------------------
30 * ExecBitmapHeapScan scans a relation using bitmap info
31 * ExecBitmapHeapNext workhorse for above
32 * ExecInitBitmapHeapScan creates and initializes state info.
33 * ExecBitmapHeapReScan prepares to rescan the plan.
34 * ExecEndBitmapHeapScan releases all storage.
38 #include "access/heapam.h"
39 #include "executor/execdebug.h"
40 #include "executor/nodeBitmapHeapscan.h"
42 #include "storage/bufmgr.h"
43 #include "utils/memutils.h"
44 #include "utils/snapmgr.h"
45 #include "utils/tqual.h"
48 static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
49 static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
52 /* ----------------------------------------------------------------
55 * Retrieve next tuple from the BitmapHeapScan node's currentRelation
56 * ----------------------------------------------------------------
58 static TupleTableSlot *
59 BitmapHeapNext(BitmapHeapScanState *node)
62 ExprContext *econtext;
66 TBMIterateResult *tbmres;
67 OffsetNumber targoffset;
71 * extract necessary information from index scan node
73 estate = node->ss.ps.state;
74 econtext = node->ss.ps.ps_ExprContext;
75 slot = node->ss.ss_ScanTupleSlot;
76 scan = node->ss.ss_currentScanDesc;
77 scanrelid = ((BitmapHeapScan *) node->ss.ps.plan)->scan.scanrelid;
79 tbmres = node->tbmres;
82 * Check if we are evaluating PlanQual for tuple of this relation.
83 * Additional checking is not good, but no other way for now. We could
84 * introduce new nodes for this case and handle IndexScan --> NewNode
85 * switching in Init/ReScan plan...
87 if (estate->es_evTuple != NULL &&
88 estate->es_evTuple[scanrelid - 1] != NULL)
90 if (estate->es_evTupleNull[scanrelid - 1])
91 return ExecClearTuple(slot);
93 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
94 slot, InvalidBuffer, false);
96 /* Does the tuple meet the original qual conditions? */
97 econtext->ecxt_scantuple = slot;
99 ResetExprContext(econtext);
101 if (!ExecQual(node->bitmapqualorig, econtext, false))
102 ExecClearTuple(slot); /* would not be returned by scan */
104 /* Flag for the next call that no more tuples */
105 estate->es_evTupleNull[scanrelid - 1] = true;
111 * If we haven't yet performed the underlying index scan, do it, and
112 * prepare the bitmap to be iterated over.
116 tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
118 if (!tbm || !IsA(tbm, TIDBitmap))
119 elog(ERROR, "unrecognized result from subplan");
122 node->tbmres = tbmres = NULL;
124 tbm_begin_iterate(tbm);
133 * Get next page of results if needed
137 node->tbmres = tbmres = tbm_iterate(tbm);
140 /* no more entries in the bitmap */
145 * Ignore any claimed entries past what we think is the end of the
146 * relation. (This is probably not necessary given that we got at
147 * least AccessShareLock on the table before performing any of the
148 * indexscans, but let's be safe.)
150 if (tbmres->blockno >= scan->rs_nblocks)
152 node->tbmres = tbmres = NULL;
157 * Fetch the current heap page and identify candidate tuples.
159 bitgetpage(scan, tbmres);
162 * Set rs_cindex to first slot to examine
169 * Continuing in previously obtained page; advance rs_cindex
175 * Out of range? If so, nothing more to look at on this page
177 if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
179 node->tbmres = tbmres = NULL;
184 * Okay to fetch the tuple
186 targoffset = scan->rs_vistuples[scan->rs_cindex];
187 dp = (Page) BufferGetPage(scan->rs_cbuf);
188 lp = PageGetItemId(dp, targoffset);
189 Assert(ItemIdIsNormal(lp));
191 scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
192 scan->rs_ctup.t_len = ItemIdGetLength(lp);
193 ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
195 pgstat_count_heap_fetch(scan->rs_rd);
198 * Set up the result slot to point to this tuple. Note that the slot
199 * acquires a pin on the buffer.
201 ExecStoreTuple(&scan->rs_ctup,
207 * If we are using lossy info, we have to recheck the qual conditions
212 econtext->ecxt_scantuple = slot;
213 ResetExprContext(econtext);
215 if (!ExecQual(node->bitmapqualorig, econtext, false))
217 /* Fails recheck, so drop it and loop back for another */
218 ExecClearTuple(slot);
223 /* OK to return this tuple */
228 * if we get here it means we are at the end of the scan..
230 return ExecClearTuple(slot);
234 * bitgetpage - subroutine for BitmapHeapNext()
236 * This routine reads and pins the specified page of the relation, then
237 * builds an array indicating which tuples on the page are both potentially
238 * interesting according to the bitmap, and visible according to the snapshot.
241 bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
243 BlockNumber page = tbmres->blockno;
249 * Acquire pin on the target heap page, trading in any pin we held before.
251 Assert(page < scan->rs_nblocks);
253 scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
256 buffer = scan->rs_cbuf;
257 snapshot = scan->rs_snapshot;
262 * Prune and repair fragmentation for the whole page, if possible.
264 heap_page_prune_opt(scan->rs_rd, buffer, RecentGlobalXmin);
267 * We must hold share lock on the buffer content while examining tuple
268 * visibility. Afterwards, however, the tuples we have found to be
269 * visible are guaranteed good as long as we hold the buffer pin.
271 LockBuffer(buffer, BUFFER_LOCK_SHARE);
274 * We need two separate strategies for lossy and non-lossy cases.
276 if (tbmres->ntuples >= 0)
279 * Bitmap is non-lossy, so we just look through the offsets listed in
280 * tbmres; but we have to follow any HOT chain starting at each such
285 for (curslot = 0; curslot < tbmres->ntuples; curslot++)
287 OffsetNumber offnum = tbmres->offsets[curslot];
290 ItemPointerSet(&tid, page, offnum);
291 if (heap_hot_search_buffer(&tid, buffer, snapshot, NULL))
292 scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
298 * Bitmap is lossy, so we must examine each item pointer on the page.
299 * But we can ignore HOT chains, since we'll check each tuple anyway.
301 Page dp = (Page) BufferGetPage(buffer);
302 OffsetNumber maxoff = PageGetMaxOffsetNumber(dp);
305 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
308 HeapTupleData loctup;
310 lp = PageGetItemId(dp, offnum);
311 if (!ItemIdIsNormal(lp))
313 loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
314 loctup.t_len = ItemIdGetLength(lp);
315 if (HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer))
316 scan->rs_vistuples[ntup++] = offnum;
320 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
322 Assert(ntup <= MaxHeapTuplesPerPage);
323 scan->rs_ntuples = ntup;
326 /* ----------------------------------------------------------------
327 * ExecBitmapHeapScan(node)
328 * ----------------------------------------------------------------
331 ExecBitmapHeapScan(BitmapHeapScanState *node)
334 * use BitmapHeapNext as access method
336 return ExecScan(&node->ss, (ExecScanAccessMtd) BitmapHeapNext);
339 /* ----------------------------------------------------------------
340 * ExecBitmapHeapReScan(node)
341 * ----------------------------------------------------------------
344 ExecBitmapHeapReScan(BitmapHeapScanState *node, ExprContext *exprCtxt)
349 estate = node->ss.ps.state;
350 scanrelid = ((BitmapHeapScan *) node->ss.ps.plan)->scan.scanrelid;
352 node->ss.ps.ps_TupFromTlist = false;
355 * If we are being passed an outer tuple, link it into the "regular"
356 * per-tuple econtext for possible qual eval.
358 if (exprCtxt != NULL)
360 ExprContext *stdecontext;
362 stdecontext = node->ss.ps.ps_ExprContext;
363 stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
366 /* If this is re-scanning of PlanQual ... */
367 if (estate->es_evTuple != NULL &&
368 estate->es_evTuple[scanrelid - 1] != NULL)
370 estate->es_evTupleNull[scanrelid - 1] = false;
373 /* rescan to release any page pin */
374 heap_rescan(node->ss.ss_currentScanDesc, NULL);
382 * Always rescan the input immediately, to ensure we can pass down any
383 * outer tuple that might be used in index quals.
385 ExecReScan(outerPlanState(node), exprCtxt);
388 /* ----------------------------------------------------------------
389 * ExecEndBitmapHeapScan
390 * ----------------------------------------------------------------
393 ExecEndBitmapHeapScan(BitmapHeapScanState *node)
396 HeapScanDesc scanDesc;
399 * extract information from the node
401 relation = node->ss.ss_currentRelation;
402 scanDesc = node->ss.ss_currentScanDesc;
405 * Free the exprcontext
407 ExecFreeExprContext(&node->ss.ps);
410 * clear out tuple table slots
412 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
413 ExecClearTuple(node->ss.ss_ScanTupleSlot);
416 * close down subplans
418 ExecEndNode(outerPlanState(node));
421 * release bitmap if any
429 heap_endscan(scanDesc);
432 * close the heap relation.
434 ExecCloseScanRelation(relation);
437 /* ----------------------------------------------------------------
438 * ExecInitBitmapHeapScan
440 * Initializes the scan's state information.
441 * ----------------------------------------------------------------
443 BitmapHeapScanState *
444 ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
446 BitmapHeapScanState *scanstate;
447 Relation currentRelation;
449 /* check for unsupported flags */
450 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
453 * Assert caller didn't ask for an unsafe snapshot --- see comments at
456 Assert(IsMVCCSnapshot(estate->es_snapshot));
459 * create state structure
461 scanstate = makeNode(BitmapHeapScanState);
462 scanstate->ss.ps.plan = (Plan *) node;
463 scanstate->ss.ps.state = estate;
465 scanstate->tbm = NULL;
466 scanstate->tbmres = NULL;
469 * Miscellaneous initialization
471 * create expression context for node
473 ExecAssignExprContext(estate, &scanstate->ss.ps);
475 scanstate->ss.ps.ps_TupFromTlist = false;
478 * initialize child expressions
480 scanstate->ss.ps.targetlist = (List *)
481 ExecInitExpr((Expr *) node->scan.plan.targetlist,
482 (PlanState *) scanstate);
483 scanstate->ss.ps.qual = (List *)
484 ExecInitExpr((Expr *) node->scan.plan.qual,
485 (PlanState *) scanstate);
486 scanstate->bitmapqualorig = (List *)
487 ExecInitExpr((Expr *) node->bitmapqualorig,
488 (PlanState *) scanstate);
490 #define BITMAPHEAPSCAN_NSLOTS 2
493 * tuple table initialization
495 ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
496 ExecInitScanTupleSlot(estate, &scanstate->ss);
499 * open the base relation and acquire appropriate lock on it.
501 currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
503 scanstate->ss.ss_currentRelation = currentRelation;
506 * Even though we aren't going to do a conventional seqscan, it is useful
507 * to create a HeapScanDesc --- most of the fields in it are usable.
509 scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
515 * get the scan type from the relation descriptor.
517 ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
520 * Initialize result tuple type and projection info.
522 ExecAssignResultTypeFromTL(&scanstate->ss.ps);
523 ExecAssignScanProjectionInfo(&scanstate->ss);
526 * initialize child nodes
528 * We do this last because the child nodes will open indexscans on our
529 * relation's indexes, and we want to be sure we have acquired a lock on
530 * the relation first.
532 outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags);
541 ExecCountSlotsBitmapHeapScan(BitmapHeapScan *node)
543 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
544 ExecCountSlotsNode(innerPlan((Plan *) node)) + BITMAPHEAPSCAN_NSLOTS;