39
39
40
40
#include "access/relscan.h"
41
41
#include "access/transam.h"
42
+ #include "access/visibilitymap.h"
42
43
#include "executor/execdebug.h"
43
44
#include "executor/nodeBitmapHeapscan.h"
44
45
#include "miscadmin.h"
@@ -225,9 +226,31 @@ BitmapHeapNext(BitmapHeapScanState *node)
225
226
}
226
227
227
228
/*
228
- * Fetch the current heap page and identify candidate tuples.
229
+ * We can skip fetching the heap page if we don't need any fields
230
+ * from the heap, and the bitmap entries don't need rechecking,
231
+ * and all tuples on the page are visible to our transaction.
229
232
*/
230
- bitgetpage (scan , tbmres );
233
+ node -> skip_fetch = (node -> can_skip_fetch &&
234
+ !tbmres -> recheck &&
235
+ VM_ALL_VISIBLE (node -> ss .ss_currentRelation ,
236
+ tbmres -> blockno ,
237
+ & node -> vmbuffer ));
238
+
239
+ if (node -> skip_fetch )
240
+ {
241
+ /*
242
+ * The number of tuples on this page is put into
243
+ * scan->rs_ntuples; note we don't fill scan->rs_vistuples.
244
+ */
245
+ scan -> rs_ntuples = tbmres -> ntuples ;
246
+ }
247
+ else
248
+ {
249
+ /*
250
+ * Fetch the current heap page and identify candidate tuples.
251
+ */
252
+ bitgetpage (scan , tbmres );
253
+ }
231
254
232
255
if (tbmres -> ntuples >= 0 )
233
256
node -> exact_pages ++ ;
@@ -289,45 +312,55 @@ BitmapHeapNext(BitmapHeapScanState *node)
289
312
*/
290
313
BitmapPrefetch (node , scan );
291
314
292
- /*
293
- * Okay to fetch the tuple
294
- */
295
- targoffset = scan -> rs_vistuples [scan -> rs_cindex ];
296
- dp = (Page ) BufferGetPage (scan -> rs_cbuf );
297
- lp = PageGetItemId (dp , targoffset );
298
- Assert (ItemIdIsNormal (lp ));
315
+ if (node -> skip_fetch )
316
+ {
317
+ /*
318
+ * If we don't have to fetch the tuple, just return nulls.
319
+ */
320
+ ExecStoreAllNullTuple (slot );
321
+ }
322
+ else
323
+ {
324
+ /*
325
+ * Okay to fetch the tuple.
326
+ */
327
+ targoffset = scan -> rs_vistuples [scan -> rs_cindex ];
328
+ dp = (Page ) BufferGetPage (scan -> rs_cbuf );
329
+ lp = PageGetItemId (dp , targoffset );
330
+ Assert (ItemIdIsNormal (lp ));
299
331
300
- scan -> rs_ctup .t_data = (HeapTupleHeader ) PageGetItem ((Page ) dp , lp );
301
- scan -> rs_ctup .t_len = ItemIdGetLength (lp );
302
- scan -> rs_ctup .t_tableOid = scan -> rs_rd -> rd_id ;
303
- ItemPointerSet (& scan -> rs_ctup .t_self , tbmres -> blockno , targoffset );
332
+ scan -> rs_ctup .t_data = (HeapTupleHeader ) PageGetItem ((Page ) dp , lp );
333
+ scan -> rs_ctup .t_len = ItemIdGetLength (lp );
334
+ scan -> rs_ctup .t_tableOid = scan -> rs_rd -> rd_id ;
335
+ ItemPointerSet (& scan -> rs_ctup .t_self , tbmres -> blockno , targoffset );
304
336
305
- pgstat_count_heap_fetch (scan -> rs_rd );
337
+ pgstat_count_heap_fetch (scan -> rs_rd );
306
338
307
- /*
308
- * Set up the result slot to point to this tuple. Note that the slot
309
- * acquires a pin on the buffer.
310
- */
311
- ExecStoreTuple (& scan -> rs_ctup ,
312
- slot ,
313
- scan -> rs_cbuf ,
314
- false);
315
-
316
- /*
317
- * If we are using lossy info, we have to recheck the qual conditions
318
- * at every tuple.
319
- */
320
- if (tbmres -> recheck )
321
- {
322
- econtext -> ecxt_scantuple = slot ;
323
- ResetExprContext (econtext );
339
+ /*
340
+ * Set up the result slot to point to this tuple. Note that the
341
+ * slot acquires a pin on the buffer.
342
+ */
343
+ ExecStoreTuple (& scan -> rs_ctup ,
344
+ slot ,
345
+ scan -> rs_cbuf ,
346
+ false);
324
347
325
- if (!ExecQual (node -> bitmapqualorig , econtext ))
348
+ /*
349
+ * If we are using lossy info, we have to recheck the qual
350
+ * conditions at every tuple.
351
+ */
352
+ if (tbmres -> recheck )
326
353
{
327
- /* Fails recheck, so drop it and loop back for another */
328
- InstrCountFiltered2 (node , 1 );
329
- ExecClearTuple (slot );
330
- continue ;
354
+ econtext -> ecxt_scantuple = slot ;
355
+ ResetExprContext (econtext );
356
+
357
+ if (!ExecQual (node -> bitmapqualorig , econtext ))
358
+ {
359
+ /* Fails recheck, so drop it and loop back for another */
360
+ InstrCountFiltered2 (node , 1 );
361
+ ExecClearTuple (slot );
362
+ continue ;
363
+ }
331
364
}
332
365
}
333
366
@@ -582,6 +615,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
582
615
while (node -> prefetch_pages < node -> prefetch_target )
583
616
{
584
617
TBMIterateResult * tbmpre = tbm_iterate (prefetch_iterator );
618
+ bool skip_fetch ;
585
619
586
620
if (tbmpre == NULL )
587
621
{
@@ -591,7 +625,26 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
591
625
break ;
592
626
}
593
627
node -> prefetch_pages ++ ;
594
- PrefetchBuffer (scan -> rs_rd , MAIN_FORKNUM , tbmpre -> blockno );
628
+
629
+ /*
630
+ * If we expect not to have to actually read this heap page,
631
+ * skip this prefetch call, but continue to run the prefetch
632
+ * logic normally. (Would it be better not to increment
633
+ * prefetch_pages?)
634
+ *
635
+ * This depends on the assumption that the index AM will
636
+ * report the same recheck flag for this future heap page as
637
+ * it did for the current heap page; which is not a certainty
638
+ * but is true in many cases.
639
+ */
640
+ skip_fetch = (node -> can_skip_fetch &&
641
+ (node -> tbmres ? !node -> tbmres -> recheck : false) &&
642
+ VM_ALL_VISIBLE (node -> ss .ss_currentRelation ,
643
+ tbmpre -> blockno ,
644
+ & node -> pvmbuffer ));
645
+
646
+ if (!skip_fetch )
647
+ PrefetchBuffer (scan -> rs_rd , MAIN_FORKNUM , tbmpre -> blockno );
595
648
}
596
649
}
597
650
@@ -608,6 +661,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
608
661
{
609
662
TBMIterateResult * tbmpre ;
610
663
bool do_prefetch = false;
664
+ bool skip_fetch ;
611
665
612
666
/*
613
667
* Recheck under the mutex. If some other process has already
@@ -633,7 +687,15 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
633
687
break ;
634
688
}
635
689
636
- PrefetchBuffer (scan -> rs_rd , MAIN_FORKNUM , tbmpre -> blockno );
690
+ /* As above, skip prefetch if we expect not to need page */
691
+ skip_fetch = (node -> can_skip_fetch &&
692
+ (node -> tbmres ? !node -> tbmres -> recheck : false) &&
693
+ VM_ALL_VISIBLE (node -> ss .ss_currentRelation ,
694
+ tbmpre -> blockno ,
695
+ & node -> pvmbuffer ));
696
+
697
+ if (!skip_fetch )
698
+ PrefetchBuffer (scan -> rs_rd , MAIN_FORKNUM , tbmpre -> blockno );
637
699
}
638
700
}
639
701
}
@@ -687,6 +749,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
687
749
/* rescan to release any page pin */
688
750
heap_rescan (node -> ss .ss_currentScanDesc , NULL );
689
751
752
+ /* release bitmaps and buffers if any */
690
753
if (node -> tbmiterator )
691
754
tbm_end_iterate (node -> tbmiterator );
692
755
if (node -> prefetch_iterator )
@@ -697,13 +760,19 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
697
760
tbm_end_shared_iterate (node -> shared_prefetch_iterator );
698
761
if (node -> tbm )
699
762
tbm_free (node -> tbm );
763
+ if (node -> vmbuffer != InvalidBuffer )
764
+ ReleaseBuffer (node -> vmbuffer );
765
+ if (node -> pvmbuffer != InvalidBuffer )
766
+ ReleaseBuffer (node -> pvmbuffer );
700
767
node -> tbm = NULL ;
701
768
node -> tbmiterator = NULL ;
702
769
node -> tbmres = NULL ;
703
770
node -> prefetch_iterator = NULL ;
704
771
node -> initialized = false;
705
772
node -> shared_tbmiterator = NULL ;
706
773
node -> shared_prefetch_iterator = NULL ;
774
+ node -> vmbuffer = InvalidBuffer ;
775
+ node -> pvmbuffer = InvalidBuffer ;
707
776
708
777
ExecScanReScan (& node -> ss );
709
778
@@ -748,7 +817,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
748
817
ExecEndNode (outerPlanState (node ));
749
818
750
819
/*
751
- * release bitmap if any
820
+ * release bitmaps and buffers if any
752
821
*/
753
822
if (node -> tbmiterator )
754
823
tbm_end_iterate (node -> tbmiterator );
@@ -760,6 +829,10 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
760
829
tbm_end_shared_iterate (node -> shared_tbmiterator );
761
830
if (node -> shared_prefetch_iterator )
762
831
tbm_end_shared_iterate (node -> shared_prefetch_iterator );
832
+ if (node -> vmbuffer != InvalidBuffer )
833
+ ReleaseBuffer (node -> vmbuffer );
834
+ if (node -> pvmbuffer != InvalidBuffer )
835
+ ReleaseBuffer (node -> pvmbuffer );
763
836
764
837
/*
765
838
* close heap scan
@@ -805,6 +878,9 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
805
878
scanstate -> tbm = NULL ;
806
879
scanstate -> tbmiterator = NULL ;
807
880
scanstate -> tbmres = NULL ;
881
+ scanstate -> skip_fetch = false;
882
+ scanstate -> vmbuffer = InvalidBuffer ;
883
+ scanstate -> pvmbuffer = InvalidBuffer ;
808
884
scanstate -> exact_pages = 0 ;
809
885
scanstate -> lossy_pages = 0 ;
810
886
scanstate -> prefetch_iterator = NULL ;
@@ -815,8 +891,19 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
815
891
scanstate -> pscan_len = 0 ;
816
892
scanstate -> initialized = false;
817
893
scanstate -> shared_tbmiterator = NULL ;
894
+ scanstate -> shared_prefetch_iterator = NULL ;
818
895
scanstate -> pstate = NULL ;
819
896
897
+ /*
898
+ * We can potentially skip fetching heap pages if we do not need any
899
+ * columns of the table, either for checking non-indexable quals or for
900
+ * returning data. This test is a bit simplistic, as it checks the
901
+ * stronger condition that there's no qual or return tlist at all. But in
902
+ * most cases it's probably not worth working harder than that.
903
+ */
904
+ scanstate -> can_skip_fetch = (node -> scan .plan .qual == NIL &&
905
+ node -> scan .plan .targetlist == NIL );
906
+
820
907
/*
821
908
* Miscellaneous initialization
822
909
*
0 commit comments