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
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 "access/relscan.h"
40 #include "access/transam.h"
41 #include "executor/execdebug.h"
42 #include "executor/nodeBitmapHeapscan.h"
44 #include "storage/bufmgr.h"
45 #include "utils/memutils.h"
46 #include "utils/snapmgr.h"
47 #include "utils/tqual.h"
50 static TupleTableSlot
*BitmapHeapNext(BitmapHeapScanState
*node
);
51 static void bitgetpage(HeapScanDesc scan
, TBMIterateResult
*tbmres
);
54 /* ----------------------------------------------------------------
57 * Retrieve next tuple from the BitmapHeapScan node's currentRelation
58 * ----------------------------------------------------------------
60 static TupleTableSlot
*
61 BitmapHeapNext(BitmapHeapScanState
*node
)
64 ExprContext
*econtext
;
68 TBMIterateResult
*tbmres
;
69 OffsetNumber targoffset
;
73 * extract necessary information from index scan node
75 estate
= node
->ss
.ps
.state
;
76 econtext
= node
->ss
.ps
.ps_ExprContext
;
77 slot
= node
->ss
.ss_ScanTupleSlot
;
78 scan
= node
->ss
.ss_currentScanDesc
;
79 scanrelid
= ((BitmapHeapScan
*) node
->ss
.ps
.plan
)->scan
.scanrelid
;
81 tbmres
= node
->tbmres
;
84 * Check if we are evaluating PlanQual for tuple of this relation.
85 * Additional checking is not good, but no other way for now. We could
86 * introduce new nodes for this case and handle IndexScan --> NewNode
87 * switching in Init/ReScan plan...
89 if (estate
->es_evTuple
!= NULL
&&
90 estate
->es_evTuple
[scanrelid
- 1] != NULL
)
92 if (estate
->es_evTupleNull
[scanrelid
- 1])
93 return ExecClearTuple(slot
);
95 ExecStoreTuple(estate
->es_evTuple
[scanrelid
- 1],
96 slot
, InvalidBuffer
, false);
98 /* Does the tuple meet the original qual conditions? */
99 econtext
->ecxt_scantuple
= slot
;
101 ResetExprContext(econtext
);
103 if (!ExecQual(node
->bitmapqualorig
, econtext
, false))
104 ExecClearTuple(slot
); /* would not be returned by scan */
106 /* Flag for the next call that no more tuples */
107 estate
->es_evTupleNull
[scanrelid
- 1] = true;
113 * If we haven't yet performed the underlying index scan, do it, and
114 * prepare the bitmap to be iterated over.
118 tbm
= (TIDBitmap
*) MultiExecProcNode(outerPlanState(node
));
120 if (!tbm
|| !IsA(tbm
, TIDBitmap
))
121 elog(ERROR
, "unrecognized result from subplan");
124 node
->tbmres
= tbmres
= NULL
;
126 tbm_begin_iterate(tbm
);
135 * Get next page of results if needed
139 node
->tbmres
= tbmres
= tbm_iterate(tbm
);
142 /* no more entries in the bitmap */
147 * Ignore any claimed entries past what we think is the end of the
148 * relation. (This is probably not necessary given that we got at
149 * least AccessShareLock on the table before performing any of the
150 * indexscans, but let's be safe.)
152 if (tbmres
->blockno
>= scan
->rs_nblocks
)
154 node
->tbmres
= tbmres
= NULL
;
159 * Fetch the current heap page and identify candidate tuples.
161 bitgetpage(scan
, tbmres
);
164 * Set rs_cindex to first slot to examine
171 * Continuing in previously obtained page; advance rs_cindex
177 * Out of range? If so, nothing more to look at on this page
179 if (scan
->rs_cindex
< 0 || scan
->rs_cindex
>= scan
->rs_ntuples
)
181 node
->tbmres
= tbmres
= NULL
;
186 * Okay to fetch the tuple
188 targoffset
= scan
->rs_vistuples
[scan
->rs_cindex
];
189 dp
= (Page
) BufferGetPage(scan
->rs_cbuf
);
190 lp
= PageGetItemId(dp
, targoffset
);
191 Assert(ItemIdIsNormal(lp
));
193 scan
->rs_ctup
.t_data
= (HeapTupleHeader
) PageGetItem((Page
) dp
, lp
);
194 scan
->rs_ctup
.t_len
= ItemIdGetLength(lp
);
195 ItemPointerSet(&scan
->rs_ctup
.t_self
, tbmres
->blockno
, targoffset
);
197 pgstat_count_heap_fetch(scan
->rs_rd
);
200 * Set up the result slot to point to this tuple. Note that the slot
201 * acquires a pin on the buffer.
203 ExecStoreTuple(&scan
->rs_ctup
,
209 * If we are using lossy info, we have to recheck the qual conditions
214 econtext
->ecxt_scantuple
= slot
;
215 ResetExprContext(econtext
);
217 if (!ExecQual(node
->bitmapqualorig
, econtext
, false))
219 /* Fails recheck, so drop it and loop back for another */
220 ExecClearTuple(slot
);
225 /* OK to return this tuple */
230 * if we get here it means we are at the end of the scan..
232 return ExecClearTuple(slot
);
236 * bitgetpage - subroutine for BitmapHeapNext()
238 * This routine reads and pins the specified page of the relation, then
239 * builds an array indicating which tuples on the page are both potentially
240 * interesting according to the bitmap, and visible according to the snapshot.
243 bitgetpage(HeapScanDesc scan
, TBMIterateResult
*tbmres
)
245 BlockNumber page
= tbmres
->blockno
;
251 * Acquire pin on the target heap page, trading in any pin we held before.
253 Assert(page
< scan
->rs_nblocks
);
255 scan
->rs_cbuf
= ReleaseAndReadBuffer(scan
->rs_cbuf
,
258 buffer
= scan
->rs_cbuf
;
259 snapshot
= scan
->rs_snapshot
;
264 * Prune and repair fragmentation for the whole page, if possible.
266 Assert(TransactionIdIsValid(RecentGlobalXmin
));
267 heap_page_prune_opt(scan
->rs_rd
, buffer
, RecentGlobalXmin
);
270 * We must hold share lock on the buffer content while examining tuple
271 * visibility. Afterwards, however, the tuples we have found to be
272 * visible are guaranteed good as long as we hold the buffer pin.
274 LockBuffer(buffer
, BUFFER_LOCK_SHARE
);
277 * We need two separate strategies for lossy and non-lossy cases.
279 if (tbmres
->ntuples
>= 0)
282 * Bitmap is non-lossy, so we just look through the offsets listed in
283 * tbmres; but we have to follow any HOT chain starting at each such
288 for (curslot
= 0; curslot
< tbmres
->ntuples
; curslot
++)
290 OffsetNumber offnum
= tbmres
->offsets
[curslot
];
293 ItemPointerSet(&tid
, page
, offnum
);
294 if (heap_hot_search_buffer(&tid
, buffer
, snapshot
, NULL
))
295 scan
->rs_vistuples
[ntup
++] = ItemPointerGetOffsetNumber(&tid
);
301 * Bitmap is lossy, so we must examine each item pointer on the page.
302 * But we can ignore HOT chains, since we'll check each tuple anyway.
304 Page dp
= (Page
) BufferGetPage(buffer
);
305 OffsetNumber maxoff
= PageGetMaxOffsetNumber(dp
);
308 for (offnum
= FirstOffsetNumber
; offnum
<= maxoff
; offnum
= OffsetNumberNext(offnum
))
311 HeapTupleData loctup
;
313 lp
= PageGetItemId(dp
, offnum
);
314 if (!ItemIdIsNormal(lp
))
316 loctup
.t_data
= (HeapTupleHeader
) PageGetItem((Page
) dp
, lp
);
317 loctup
.t_len
= ItemIdGetLength(lp
);
318 if (HeapTupleSatisfiesVisibility(&loctup
, snapshot
, buffer
))
319 scan
->rs_vistuples
[ntup
++] = offnum
;
323 LockBuffer(buffer
, BUFFER_LOCK_UNLOCK
);
325 Assert(ntup
<= MaxHeapTuplesPerPage
);
326 scan
->rs_ntuples
= ntup
;
329 /* ----------------------------------------------------------------
330 * ExecBitmapHeapScan(node)
331 * ----------------------------------------------------------------
334 ExecBitmapHeapScan(BitmapHeapScanState
*node
)
337 * use BitmapHeapNext as access method
339 return ExecScan(&node
->ss
, (ExecScanAccessMtd
) BitmapHeapNext
);
342 /* ----------------------------------------------------------------
343 * ExecBitmapHeapReScan(node)
344 * ----------------------------------------------------------------
347 ExecBitmapHeapReScan(BitmapHeapScanState
*node
, ExprContext
*exprCtxt
)
352 estate
= node
->ss
.ps
.state
;
353 scanrelid
= ((BitmapHeapScan
*) node
->ss
.ps
.plan
)->scan
.scanrelid
;
355 node
->ss
.ps
.ps_TupFromTlist
= false;
358 * If we are being passed an outer tuple, link it into the "regular"
359 * per-tuple econtext for possible qual eval.
361 if (exprCtxt
!= NULL
)
363 ExprContext
*stdecontext
;
365 stdecontext
= node
->ss
.ps
.ps_ExprContext
;
366 stdecontext
->ecxt_outertuple
= exprCtxt
->ecxt_outertuple
;
369 /* If this is re-scanning of PlanQual ... */
370 if (estate
->es_evTuple
!= NULL
&&
371 estate
->es_evTuple
[scanrelid
- 1] != NULL
)
373 estate
->es_evTupleNull
[scanrelid
- 1] = false;
376 /* rescan to release any page pin */
377 heap_rescan(node
->ss
.ss_currentScanDesc
, NULL
);
385 * Always rescan the input immediately, to ensure we can pass down any
386 * outer tuple that might be used in index quals.
388 ExecReScan(outerPlanState(node
), exprCtxt
);
391 /* ----------------------------------------------------------------
392 * ExecEndBitmapHeapScan
393 * ----------------------------------------------------------------
396 ExecEndBitmapHeapScan(BitmapHeapScanState
*node
)
399 HeapScanDesc scanDesc
;
402 * extract information from the node
404 relation
= node
->ss
.ss_currentRelation
;
405 scanDesc
= node
->ss
.ss_currentScanDesc
;
408 * Free the exprcontext
410 ExecFreeExprContext(&node
->ss
.ps
);
413 * clear out tuple table slots
415 ExecClearTuple(node
->ss
.ps
.ps_ResultTupleSlot
);
416 ExecClearTuple(node
->ss
.ss_ScanTupleSlot
);
419 * close down subplans
421 ExecEndNode(outerPlanState(node
));
424 * release bitmap if any
432 heap_endscan(scanDesc
);
435 * close the heap relation.
437 ExecCloseScanRelation(relation
);
440 /* ----------------------------------------------------------------
441 * ExecInitBitmapHeapScan
443 * Initializes the scan's state information.
444 * ----------------------------------------------------------------
446 BitmapHeapScanState
*
447 ExecInitBitmapHeapScan(BitmapHeapScan
*node
, EState
*estate
, int eflags
)
449 BitmapHeapScanState
*scanstate
;
450 Relation currentRelation
;
452 /* check for unsupported flags */
453 Assert(!(eflags
& (EXEC_FLAG_BACKWARD
| EXEC_FLAG_MARK
)));
456 * Assert caller didn't ask for an unsafe snapshot --- see comments at
459 Assert(IsMVCCSnapshot(estate
->es_snapshot
));
462 * create state structure
464 scanstate
= makeNode(BitmapHeapScanState
);
465 scanstate
->ss
.ps
.plan
= (Plan
*) node
;
466 scanstate
->ss
.ps
.state
= estate
;
468 scanstate
->tbm
= NULL
;
469 scanstate
->tbmres
= NULL
;
472 * Miscellaneous initialization
474 * create expression context for node
476 ExecAssignExprContext(estate
, &scanstate
->ss
.ps
);
478 scanstate
->ss
.ps
.ps_TupFromTlist
= false;
481 * initialize child expressions
483 scanstate
->ss
.ps
.targetlist
= (List
*)
484 ExecInitExpr((Expr
*) node
->scan
.plan
.targetlist
,
485 (PlanState
*) scanstate
);
486 scanstate
->ss
.ps
.qual
= (List
*)
487 ExecInitExpr((Expr
*) node
->scan
.plan
.qual
,
488 (PlanState
*) scanstate
);
489 scanstate
->bitmapqualorig
= (List
*)
490 ExecInitExpr((Expr
*) node
->bitmapqualorig
,
491 (PlanState
*) scanstate
);
493 #define BITMAPHEAPSCAN_NSLOTS 2
496 * tuple table initialization
498 ExecInitResultTupleSlot(estate
, &scanstate
->ss
.ps
);
499 ExecInitScanTupleSlot(estate
, &scanstate
->ss
);
502 * open the base relation and acquire appropriate lock on it.
504 currentRelation
= ExecOpenScanRelation(estate
, node
->scan
.scanrelid
);
506 scanstate
->ss
.ss_currentRelation
= currentRelation
;
509 * Even though we aren't going to do a conventional seqscan, it is useful
510 * to create a HeapScanDesc --- most of the fields in it are usable.
512 scanstate
->ss
.ss_currentScanDesc
= heap_beginscan_bm(currentRelation
,
518 * get the scan type from the relation descriptor.
520 ExecAssignScanType(&scanstate
->ss
, RelationGetDescr(currentRelation
));
523 * Initialize result tuple type and projection info.
525 ExecAssignResultTypeFromTL(&scanstate
->ss
.ps
);
526 ExecAssignScanProjectionInfo(&scanstate
->ss
);
529 * initialize child nodes
531 * We do this last because the child nodes will open indexscans on our
532 * relation's indexes, and we want to be sure we have acquired a lock on
533 * the relation first.
535 outerPlanState(scanstate
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
544 ExecCountSlotsBitmapHeapScan(BitmapHeapScan
*node
)
546 return ExecCountSlotsNode(outerPlan((Plan
*) node
)) +
547 ExecCountSlotsNode(innerPlan((Plan
*) node
)) + BITMAPHEAPSCAN_NSLOTS
;