@@ -483,6 +483,67 @@ heapgetpage(TableScanDesc sscan, BlockNumber block)
483483scan -> rs_ntuples = ntup ;
484484}
485485
486+ /*
487+ * heapgettup_initial_block - return the first BlockNumber to scan
488+ *
489+ * Returns InvalidBlockNumber when there are no blocks to scan. This can
490+ * occur with empty tables and in parallel scans when parallel workers get all
491+ * of the pages before we can get a chance to get our first page.
492+ */
493+ static BlockNumber
494+ heapgettup_initial_block (HeapScanDesc scan ,ScanDirection dir )
495+ {
496+ Assert (!scan -> rs_inited );
497+
498+ /* When there are no pages to scan, return InvalidBlockNumber */
499+ if (scan -> rs_nblocks == 0 || scan -> rs_numblocks == 0 )
500+ return InvalidBlockNumber ;
501+
502+ if (ScanDirectionIsForward (dir ))
503+ {
504+ /* serial scan */
505+ if (scan -> rs_base .rs_parallel == NULL )
506+ return scan -> rs_startblock ;
507+ else
508+ {
509+ /* parallel scan */
510+ table_block_parallelscan_startblock_init (scan -> rs_base .rs_rd ,
511+ scan -> rs_parallelworkerdata ,
512+ (ParallelBlockTableScanDesc )scan -> rs_base .rs_parallel );
513+
514+ /* may return InvalidBlockNumber if there are no more blocks */
515+ return table_block_parallelscan_nextpage (scan -> rs_base .rs_rd ,
516+ scan -> rs_parallelworkerdata ,
517+ (ParallelBlockTableScanDesc )scan -> rs_base .rs_parallel );
518+ }
519+ }
520+ else
521+ {
522+ /* backward parallel scan not supported */
523+ Assert (scan -> rs_base .rs_parallel == NULL );
524+
525+ /*
526+ * Disable reporting to syncscan logic in a backwards scan; it's not
527+ * very likely anyone else is doing the same thing at the same time,
528+ * and much more likely that we'll just bollix things for forward
529+ * scanners.
530+ */
531+ scan -> rs_base .rs_flags &= ~SO_ALLOW_SYNC ;
532+
533+ /*
534+ * Start from last page of the scan. Ensure we take into account
535+ * rs_numblocks if it's been adjusted by heap_setscanlimits().
536+ */
537+ if (scan -> rs_numblocks != InvalidBlockNumber )
538+ return (scan -> rs_startblock + scan -> rs_numblocks - 1 ) %scan -> rs_nblocks ;
539+
540+ if (scan -> rs_startblock > 0 )
541+ return scan -> rs_startblock - 1 ;
542+
543+ return scan -> rs_nblocks - 1 ;
544+ }
545+ }
546+
486547/* ----------------
487548 *heapgettup - fetch next heap tuple
488549 *
@@ -527,38 +588,19 @@ heapgettup(HeapScanDesc scan,
527588{
528589if (!scan -> rs_inited )
529590{
591+ block = heapgettup_initial_block (scan ,dir );
592+
530593/*
531- * return null immediately if relation is empty
594+ * Check if we have reached the end of the scan already. This
595+ * could happen if the table is empty or if the parallel workers
596+ * have already finished the scan before we did anything ourselves
532597 */
533- if (scan -> rs_nblocks == 0 || scan -> rs_numblocks == 0 )
598+ if (block == InvalidBlockNumber )
534599{
535600Assert (!BufferIsValid (scan -> rs_cbuf ));
536601tuple -> t_data = NULL ;
537602return ;
538603}
539- if (scan -> rs_base .rs_parallel != NULL )
540- {
541- ParallelBlockTableScanDesc pbscan =
542- (ParallelBlockTableScanDesc )scan -> rs_base .rs_parallel ;
543- ParallelBlockTableScanWorker pbscanwork =
544- scan -> rs_parallelworkerdata ;
545-
546- table_block_parallelscan_startblock_init (scan -> rs_base .rs_rd ,
547- pbscanwork ,pbscan );
548-
549- block = table_block_parallelscan_nextpage (scan -> rs_base .rs_rd ,
550- pbscanwork ,pbscan );
551-
552- /* Other processes might have already finished the scan. */
553- if (block == InvalidBlockNumber )
554- {
555- Assert (!BufferIsValid (scan -> rs_cbuf ));
556- tuple -> t_data = NULL ;
557- return ;
558- }
559- }
560- else
561- block = scan -> rs_startblock ;/* first page */
562604heapgetpage ((TableScanDesc )scan ,block );
563605lineoff = FirstOffsetNumber ;/* first offnum */
564606scan -> rs_inited = true;
@@ -582,60 +624,40 @@ heapgettup(HeapScanDesc scan,
582624}
583625else
584626{
585- /* backward parallel scan not supported */
586- Assert (scan -> rs_base .rs_parallel == NULL );
587-
588627if (!scan -> rs_inited )
589628{
629+ block = heapgettup_initial_block (scan ,dir );
630+
590631/*
591- * return null immediately if relation is empty
632+ * Check if we have reached the end of the scan already. This
633+ * could happen if the table is empty.
592634 */
593- if (scan -> rs_nblocks == 0 || scan -> rs_numblocks == 0 )
635+ if (block == InvalidBlockNumber )
594636{
595637Assert (!BufferIsValid (scan -> rs_cbuf ));
596638tuple -> t_data = NULL ;
597639return ;
598640}
599641
600- /*
601- * Disable reporting to syncscan logic in a backwards scan; it's
602- * not very likely anyone else is doing the same thing at the same
603- * time, and much more likely that we'll just bollix things for
604- * forward scanners.
605- */
606- scan -> rs_base .rs_flags &= ~SO_ALLOW_SYNC ;
607-
608- /*
609- * Start from last page of the scan. Ensure we take into account
610- * rs_numblocks if it's been adjusted by heap_setscanlimits().
611- */
612- if (scan -> rs_numblocks != InvalidBlockNumber )
613- block = (scan -> rs_startblock + scan -> rs_numblocks - 1 ) %scan -> rs_nblocks ;
614- else if (scan -> rs_startblock > 0 )
615- block = scan -> rs_startblock - 1 ;
616- else
617- block = scan -> rs_nblocks - 1 ;
618642heapgetpage ((TableScanDesc )scan ,block );
643+ LockBuffer (scan -> rs_cbuf ,BUFFER_LOCK_SHARE );
644+
645+ page = BufferGetPage (scan -> rs_cbuf );
646+ TestForOldSnapshot (snapshot ,scan -> rs_base .rs_rd ,page );
647+ lines = PageGetMaxOffsetNumber (page );
648+ lineoff = lines ;/* final offnum */
649+ scan -> rs_inited = true;
619650}
620651else
621652{
622653/* continue from previously returned page/tuple */
623654block = scan -> rs_cblock ;/* current page */
624- }
655+ LockBuffer ( scan -> rs_cbuf , BUFFER_LOCK_SHARE );
625656
626- LockBuffer (scan -> rs_cbuf ,BUFFER_LOCK_SHARE );
657+ page = BufferGetPage (scan -> rs_cbuf );
658+ TestForOldSnapshot (snapshot ,scan -> rs_base .rs_rd ,page );
659+ lines = PageGetMaxOffsetNumber (page );
627660
628- page = BufferGetPage (scan -> rs_cbuf );
629- TestForOldSnapshot (snapshot ,scan -> rs_base .rs_rd ,page );
630- lines = PageGetMaxOffsetNumber (page );
631-
632- if (!scan -> rs_inited )
633- {
634- lineoff = lines ;/* final offnum */
635- scan -> rs_inited = true;
636- }
637- else
638- {
639661/*
640662 * The previous returned tuple may have been vacuumed since the
641663 * previous scan when we use a non-MVCC snapshot, so we must
@@ -837,38 +859,19 @@ heapgettup_pagemode(HeapScanDesc scan,
837859{
838860if (!scan -> rs_inited )
839861{
862+ block = heapgettup_initial_block (scan ,dir );
863+
840864/*
841- * return null immediately if relation is empty
865+ * Check if we have reached the end of the scan already. This
866+ * could happen if the table is empty or if the parallel workers
867+ * have already finished the scan before we did anything ourselves
842868 */
843- if (scan -> rs_nblocks == 0 || scan -> rs_numblocks == 0 )
869+ if (block == InvalidBlockNumber )
844870{
845871Assert (!BufferIsValid (scan -> rs_cbuf ));
846872tuple -> t_data = NULL ;
847873return ;
848874}
849- if (scan -> rs_base .rs_parallel != NULL )
850- {
851- ParallelBlockTableScanDesc pbscan =
852- (ParallelBlockTableScanDesc )scan -> rs_base .rs_parallel ;
853- ParallelBlockTableScanWorker pbscanwork =
854- scan -> rs_parallelworkerdata ;
855-
856- table_block_parallelscan_startblock_init (scan -> rs_base .rs_rd ,
857- pbscanwork ,pbscan );
858-
859- block = table_block_parallelscan_nextpage (scan -> rs_base .rs_rd ,
860- pbscanwork ,pbscan );
861-
862- /* Other processes might have already finished the scan. */
863- if (block == InvalidBlockNumber )
864- {
865- Assert (!BufferIsValid (scan -> rs_cbuf ));
866- tuple -> t_data = NULL ;
867- return ;
868- }
869- }
870- else
871- block = scan -> rs_startblock ;/* first page */
872875heapgetpage ((TableScanDesc )scan ,block );
873876lineindex = 0 ;
874877scan -> rs_inited = true;
@@ -889,58 +892,36 @@ heapgettup_pagemode(HeapScanDesc scan,
889892}
890893else
891894{
892- /* backward parallel scan not supported */
893- Assert (scan -> rs_base .rs_parallel == NULL );
894-
895895if (!scan -> rs_inited )
896896{
897+ block = heapgettup_initial_block (scan ,dir );
898+
897899/*
898- * return null immediately if relation is empty
900+ * Check if we have reached the end of the scan already. This
901+ * could happen if the table is empty.
899902 */
900- if (scan -> rs_nblocks == 0 || scan -> rs_numblocks == 0 )
903+ if (block == InvalidBlockNumber )
901904{
902905Assert (!BufferIsValid (scan -> rs_cbuf ));
903906tuple -> t_data = NULL ;
904907return ;
905908}
906909
907- /*
908- * Disable reporting to syncscan logic in a backwards scan; it's
909- * not very likely anyone else is doing the same thing at the same
910- * time, and much more likely that we'll just bollix things for
911- * forward scanners.
912- */
913- scan -> rs_base .rs_flags &= ~SO_ALLOW_SYNC ;
914-
915- /*
916- * Start from last page of the scan. Ensure we take into account
917- * rs_numblocks if it's been adjusted by heap_setscanlimits().
918- */
919- if (scan -> rs_numblocks != InvalidBlockNumber )
920- block = (scan -> rs_startblock + scan -> rs_numblocks - 1 ) %scan -> rs_nblocks ;
921- else if (scan -> rs_startblock > 0 )
922- block = scan -> rs_startblock - 1 ;
923- else
924- block = scan -> rs_nblocks - 1 ;
925910heapgetpage ((TableScanDesc )scan ,block );
911+ page = BufferGetPage (scan -> rs_cbuf );
912+ TestForOldSnapshot (scan -> rs_base .rs_snapshot ,scan -> rs_base .rs_rd ,page );
913+ lines = scan -> rs_ntuples ;
914+ lineindex = lines - 1 ;
915+ scan -> rs_inited = true;
926916}
927917else
928918{
929919/* continue from previously returned page/tuple */
930920block = scan -> rs_cblock ;/* current page */
931- }
932-
933- page = BufferGetPage (scan -> rs_cbuf );
934- TestForOldSnapshot (scan -> rs_base .rs_snapshot ,scan -> rs_base .rs_rd ,page );
935- lines = scan -> rs_ntuples ;
936921
937- if (!scan -> rs_inited )
938- {
939- lineindex = lines - 1 ;
940- scan -> rs_inited = true;
941- }
942- else
943- {
922+ page = BufferGetPage (scan -> rs_cbuf );
923+ TestForOldSnapshot (scan -> rs_base .rs_snapshot ,scan -> rs_base .rs_rd ,page );
924+ lines = scan -> rs_ntuples ;
944925lineindex = scan -> rs_cindex - 1 ;
945926}
946927/* block and lineindex now reference the previous visible tid */