Further refactor of heapgettup and heapgettup_pagemode

Backward and forward scans share much of the same page acquisition code.
Here we consolidate that code to reduce some duplication.

Additionally, add a new rs_coffset field to HeapScanDescData to track the
offset of the current tuple.  The new field fits nicely into the padding
between a bool and BlockNumber field and saves having to look at the last
returned tuple to figure out which offset we should be looking at for the
current tuple.

Author: Melanie Plageman
Reviewed-by: David Rowley
Discussion: https://postgr.es/m/CAAKRu_bvkhka0CZQun28KTqhuUh5ZqY=_T8QEqZqOL02rpi2bw@mail.gmail.com
This commit is contained in:
David Rowley 2023-02-03 11:48:39 +13:00
parent cdf6518ef0
commit f9bc34fcb6
2 changed files with 64 additions and 139 deletions

View File

@ -576,101 +576,67 @@ heapgettup(HeapScanDesc scan,
BlockNumber block;
bool finished;
Page page;
int lines;
OffsetNumber lineoff;
int linesleft;
ItemId lpp;
/*
* calculate next starting lineoff, given scan direction
*/
if (ScanDirectionIsForward(dir))
if (unlikely(!scan->rs_inited))
{
if (!scan->rs_inited)
{
block = heapgettup_initial_block(scan, dir);
block = heapgettup_initial_block(scan, dir);
/*
* Check if we have reached the end of the scan already. This
* could happen if the table is empty or if the parallel workers
* have already finished the scan before we did anything ourselves
*/
if (block == InvalidBlockNumber)
{
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
lineoff = FirstOffsetNumber; /* first offnum */
scan->rs_inited = true;
}
else
/*
* Check if we have reached the end of the scan already. This could
* happen if the table is empty or if the parallel workers have
* already finished the scan before we did anything ourselves
*/
if (block == InvalidBlockNumber)
{
/* continue from previously returned page/tuple */
block = scan->rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
lines = PageGetMaxOffsetNumber(page);
/* block and lineoff now reference the physically next tid */
linesleft = lines - lineoff + 1;
linesleft = PageGetMaxOffsetNumber(page) - FirstOffsetNumber + 1;
if (ScanDirectionIsForward(dir))
lineoff = FirstOffsetNumber; /* first offnum */
else
lineoff = (OffsetNumber) linesleft;
scan->rs_inited = true;
}
else
{
if (!scan->rs_inited)
/* continue from previously returned page/tuple */
block = scan->rs_cblock;
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
if (ScanDirectionIsForward(dir))
{
block = heapgettup_initial_block(scan, dir);
/*
* Check if we have reached the end of the scan already. This
* could happen if the table is empty.
*/
if (block == InvalidBlockNumber)
{
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
lines = PageGetMaxOffsetNumber(page);
lineoff = lines; /* final offnum */
scan->rs_inited = true;
lineoff = OffsetNumberNext(scan->rs_coffset);
linesleft = PageGetMaxOffsetNumber(page) - lineoff + 1;
}
else
{
/* continue from previously returned page/tuple */
block = scan->rs_cblock; /* current page */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
lines = PageGetMaxOffsetNumber(page);
/*
* The previous returned tuple may have been vacuumed since the
* previous scan when we use a non-MVCC snapshot, so we must
* re-establish the lineoff <= PageGetMaxOffsetNumber(page)
* invariant
*/
lineoff = /* previous offnum */
Min(lines,
OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self))));
lineoff = Min(PageGetMaxOffsetNumber(page),
OffsetNumberPrev(scan->rs_coffset));
linesleft = lineoff;
}
/* block and lineoff now reference the physically previous tid */
linesleft = lineoff;
}
/*
@ -715,6 +681,7 @@ heapgettup(HeapScanDesc scan,
if (valid)
{
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
scan->rs_coffset = lineoff;
return;
}
}
@ -807,12 +774,11 @@ heapgettup(HeapScanDesc scan,
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
lines = PageGetMaxOffsetNumber(page);
linesleft = lines;
linesleft = PageGetMaxOffsetNumber(page);
if (backward)
{
lineoff = lines;
lpp = PageGetItemId(page, lines);
lineoff = linesleft;
lpp = PageGetItemId(page, linesleft);
}
else
{
@ -846,87 +812,46 @@ heapgettup_pagemode(HeapScanDesc scan,
BlockNumber block;
bool finished;
Page page;
int lines;
int lineindex;
OffsetNumber lineoff;
int linesleft;
ItemId lpp;
/*
* calculate next starting lineindex, given scan direction
*/
if (ScanDirectionIsForward(dir))
if (unlikely(!scan->rs_inited))
{
if (!scan->rs_inited)
{
block = heapgettup_initial_block(scan, dir);
block = heapgettup_initial_block(scan, dir);
/*
* Check if we have reached the end of the scan already. This
* could happen if the table is empty or if the parallel workers
* have already finished the scan before we did anything ourselves
*/
if (block == InvalidBlockNumber)
{
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
lineindex = 0;
scan->rs_inited = true;
}
else
/*
* Check if we have reached the end of the scan already. This could
* happen if the table is empty or if the other workers in a parallel
* scan have already finished the scan.
*/
if (block == InvalidBlockNumber)
{
/* continue from previously returned page/tuple */
block = scan->rs_cblock; /* current page */
lineindex = scan->rs_cindex + 1;
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
lines = scan->rs_ntuples;
/* block and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
linesleft = scan->rs_ntuples;
lineindex = ScanDirectionIsForward(dir) ? 0 : linesleft - 1;
scan->rs_inited = true;
}
else
{
if (!scan->rs_inited)
{
block = heapgettup_initial_block(scan, dir);
/* continue from previously returned page/tuple */
block = scan->rs_cblock; /* current page */
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
/*
* Check if we have reached the end of the scan already. This
* could happen if the table is empty.
*/
if (block == InvalidBlockNumber)
{
Assert(!BufferIsValid(scan->rs_cbuf));
tuple->t_data = NULL;
return;
}
heapgetpage((TableScanDesc) scan, block);
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
lines = scan->rs_ntuples;
lineindex = lines - 1;
scan->rs_inited = true;
}
lineindex = scan->rs_cindex + dir;
if (ScanDirectionIsForward(dir))
linesleft = scan->rs_ntuples - lineindex;
else
{
/* continue from previously returned page/tuple */
block = scan->rs_cblock; /* current page */
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
lines = scan->rs_ntuples;
lineindex = scan->rs_cindex - 1;
}
/* block and lineindex now reference the previous visible tid */
linesleft = lineindex + 1;
linesleft = scan->rs_cindex;
}
/*
@ -1041,10 +966,9 @@ heapgettup_pagemode(HeapScanDesc scan,
page = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
lines = scan->rs_ntuples;
linesleft = lines;
linesleft = scan->rs_ntuples;
if (backward)
lineindex = lines - 1;
lineindex = linesleft - 1;
else
lineindex = 0;
}

View File

@ -57,6 +57,7 @@ typedef struct HeapScanDescData
/* scan current state */
bool rs_inited; /* false = scan not init'd yet */
OffsetNumber rs_coffset; /* current offset # in non-page-at-a-time mode */
BlockNumber rs_cblock; /* current block # in scan, if any */
Buffer rs_cbuf; /* current buffer in scan, if any */
/* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */