mirror of
https://github.com/postgres/postgres.git
synced 2025-07-15 19:21:59 +03:00
Fix tuple-chain-moving tests to handle marked-for-update tuples correctly
(they are not part of a chain). When failing to find a parent tuple in an update chain, emit a warning and abandon repair_frag, but do not give an error as before. This should eliminate the infamous 'No one parent tuple was found' failure, which we now realize is not a can't-happen condition but a perfectly valid database state. Per recent pghackers discussion.
This commit is contained in:
@ -13,7 +13,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.233 2002/08/06 02:36:34 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.234 2002/08/13 20:14:24 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1298,7 +1298,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||||||
usable_free_size = 0;
|
usable_free_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usable_free_size > 0 && num_vtlinks > 0)
|
/* don't bother to save vtlinks if we will not call repair_frag */
|
||||||
|
if (fraged_pages->num_pages > 0 && num_vtlinks > 0)
|
||||||
{
|
{
|
||||||
qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData),
|
qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData),
|
||||||
vac_cmp_vtlinks);
|
vac_cmp_vtlinks);
|
||||||
@ -1322,7 +1323,6 @@ Re-using: Free/Avail. Space %.0f/%.0f; EndEmpty/Avail. Pages %u/%u.\n\t%s",
|
|||||||
free_size, usable_free_size,
|
free_size, usable_free_size,
|
||||||
empty_end_pages, fraged_pages->num_pages,
|
empty_end_pages, fraged_pages->num_pages,
|
||||||
vac_show_rusage(&ru0));
|
vac_show_rusage(&ru0));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1577,42 +1577,65 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
* If this tuple is in the chain of tuples created in updates
|
* If this tuple is in the chain of tuples created in updates
|
||||||
* by "recent" transactions then we have to move all chain of
|
* by "recent" transactions then we have to move all chain of
|
||||||
* tuples to another places.
|
* tuples to another places.
|
||||||
|
*
|
||||||
|
* NOTE: this test is not 100% accurate: it is possible for
|
||||||
|
* a tuple to be an updated one with recent xmin, and yet not
|
||||||
|
* have a corresponding tuple in the vtlinks list. Presumably
|
||||||
|
* there was once a parent tuple with xmax matching the xmin,
|
||||||
|
* but it's possible that that tuple has been removed --- for
|
||||||
|
* example, if it had xmin = xmax then HeapTupleSatisfiesVacuum
|
||||||
|
* would deem it removable as soon as the xmin xact completes.
|
||||||
|
*
|
||||||
|
* To be on the safe side, we abandon the repair_frag process if
|
||||||
|
* we cannot find the parent tuple in vtlinks. This may be overly
|
||||||
|
* conservative; AFAICS it would be safe to move the chain.
|
||||||
*/
|
*/
|
||||||
if ((tuple.t_data->t_infomask & HEAP_UPDATED &&
|
if (((tuple.t_data->t_infomask & HEAP_UPDATED) &&
|
||||||
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
|
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
|
||||||
OldestXmin)) ||
|
OldestXmin)) ||
|
||||||
(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) &&
|
(!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID |
|
||||||
|
HEAP_MARKED_FOR_UPDATE)) &&
|
||||||
!(ItemPointerEquals(&(tuple.t_self),
|
!(ItemPointerEquals(&(tuple.t_self),
|
||||||
&(tuple.t_data->t_ctid)))))
|
&(tuple.t_data->t_ctid)))))
|
||||||
{
|
{
|
||||||
Buffer Cbuf = buf;
|
Buffer Cbuf = buf;
|
||||||
|
bool freeCbuf = false;
|
||||||
|
bool chain_move_failed = false;
|
||||||
Page Cpage;
|
Page Cpage;
|
||||||
ItemId Citemid;
|
ItemId Citemid;
|
||||||
ItemPointerData Ctid;
|
ItemPointerData Ctid;
|
||||||
HeapTupleData tp = tuple;
|
HeapTupleData tp = tuple;
|
||||||
Size tlen = tuple_len;
|
Size tlen = tuple_len;
|
||||||
VTupleMove vtmove = (VTupleMove)
|
VTupleMove vtmove;
|
||||||
palloc(100 * sizeof(VTupleMoveData));
|
int num_vtmove;
|
||||||
int num_vtmove = 0;
|
int free_vtmove;
|
||||||
int free_vtmove = 100;
|
|
||||||
VacPage to_vacpage = NULL;
|
VacPage to_vacpage = NULL;
|
||||||
int to_item = 0;
|
int to_item = 0;
|
||||||
bool freeCbuf = false;
|
|
||||||
int ti;
|
int ti;
|
||||||
|
|
||||||
if (vacrelstats->vtlinks == NULL)
|
|
||||||
elog(ERROR, "No one parent tuple was found");
|
|
||||||
if (cur_buffer != InvalidBuffer)
|
if (cur_buffer != InvalidBuffer)
|
||||||
{
|
{
|
||||||
WriteBuffer(cur_buffer);
|
WriteBuffer(cur_buffer);
|
||||||
cur_buffer = InvalidBuffer;
|
cur_buffer = InvalidBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Quick exit if we have no vtlinks to search in */
|
||||||
|
if (vacrelstats->vtlinks == NULL)
|
||||||
|
{
|
||||||
|
elog(WARNING, "Parent item in update-chain not found - can't continue repair_frag");
|
||||||
|
break; /* out of walk-along-page loop */
|
||||||
|
}
|
||||||
|
|
||||||
|
vtmove = (VTupleMove) palloc(100 * sizeof(VTupleMoveData));
|
||||||
|
num_vtmove = 0;
|
||||||
|
free_vtmove = 100;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this tuple is in the begin/middle of the chain then
|
* If this tuple is in the begin/middle of the chain then
|
||||||
* we have to move to the end of chain.
|
* we have to move to the end of chain.
|
||||||
*/
|
*/
|
||||||
while (!(tp.t_data->t_infomask & HEAP_XMAX_INVALID) &&
|
while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID |
|
||||||
|
HEAP_MARKED_FOR_UPDATE)) &&
|
||||||
!(ItemPointerEquals(&(tp.t_self),
|
!(ItemPointerEquals(&(tp.t_self),
|
||||||
&(tp.t_data->t_ctid))))
|
&(tp.t_data->t_ctid))))
|
||||||
{
|
{
|
||||||
@ -1636,22 +1659,35 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
* in scan_heap(), but it's not implemented at the
|
* in scan_heap(), but it's not implemented at the
|
||||||
* moment and so we just stop shrinking here.
|
* moment and so we just stop shrinking here.
|
||||||
*/
|
*/
|
||||||
ReleaseBuffer(Cbuf);
|
|
||||||
pfree(vtmove);
|
|
||||||
vtmove = NULL;
|
|
||||||
elog(WARNING, "Child itemid in update-chain marked as unused - can't continue repair_frag");
|
elog(WARNING, "Child itemid in update-chain marked as unused - can't continue repair_frag");
|
||||||
break;
|
chain_move_failed = true;
|
||||||
|
break; /* out of loop to move to chain end */
|
||||||
}
|
}
|
||||||
tp.t_datamcxt = NULL;
|
tp.t_datamcxt = NULL;
|
||||||
tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);
|
tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);
|
||||||
tp.t_self = Ctid;
|
tp.t_self = Ctid;
|
||||||
tlen = tp.t_len = ItemIdGetLength(Citemid);
|
tlen = tp.t_len = ItemIdGetLength(Citemid);
|
||||||
}
|
}
|
||||||
if (vtmove == NULL)
|
if (chain_move_failed)
|
||||||
break;
|
{
|
||||||
/* first, can chain be moved ? */
|
if (freeCbuf)
|
||||||
|
ReleaseBuffer(Cbuf);
|
||||||
|
pfree(vtmove);
|
||||||
|
break; /* out of walk-along-page loop */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if all items in chain can be moved
|
||||||
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
Buffer Pbuf;
|
||||||
|
Page Ppage;
|
||||||
|
ItemId Pitemid;
|
||||||
|
HeapTupleData Ptp;
|
||||||
|
VTupleLinkData vtld,
|
||||||
|
*vtlp;
|
||||||
|
|
||||||
if (to_vacpage == NULL ||
|
if (to_vacpage == NULL ||
|
||||||
!enough_space(to_vacpage, tlen))
|
!enough_space(to_vacpage, tlen))
|
||||||
{
|
{
|
||||||
@ -1664,13 +1700,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
if (i == num_fraged_pages)
|
if (i == num_fraged_pages)
|
||||||
{
|
{
|
||||||
/* can't move item anywhere */
|
/* can't move item anywhere */
|
||||||
for (i = 0; i < num_vtmove; i++)
|
chain_move_failed = true;
|
||||||
{
|
break; /* out of check-all-items loop */
|
||||||
Assert(vtmove[i].vacpage->offsets_used > 0);
|
|
||||||
(vtmove[i].vacpage->offsets_used)--;
|
|
||||||
}
|
|
||||||
num_vtmove = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
to_item = i;
|
to_item = i;
|
||||||
to_vacpage = fraged_pages->pagedesc[to_item];
|
to_vacpage = fraged_pages->pagedesc[to_item];
|
||||||
@ -1682,9 +1713,10 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
if (free_vtmove == 0)
|
if (free_vtmove == 0)
|
||||||
{
|
{
|
||||||
free_vtmove = 1000;
|
free_vtmove = 1000;
|
||||||
vtmove = (VTupleMove) repalloc(vtmove,
|
vtmove = (VTupleMove)
|
||||||
(free_vtmove + num_vtmove) *
|
repalloc(vtmove,
|
||||||
sizeof(VTupleMoveData));
|
(free_vtmove + num_vtmove) *
|
||||||
|
sizeof(VTupleMoveData));
|
||||||
}
|
}
|
||||||
vtmove[num_vtmove].tid = tp.t_self;
|
vtmove[num_vtmove].tid = tp.t_self;
|
||||||
vtmove[num_vtmove].vacpage = to_vacpage;
|
vtmove[num_vtmove].vacpage = to_vacpage;
|
||||||
@ -1695,113 +1727,96 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
free_vtmove--;
|
free_vtmove--;
|
||||||
num_vtmove++;
|
num_vtmove++;
|
||||||
|
|
||||||
/* All done ? */
|
/* At beginning of chain? */
|
||||||
if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
|
if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
|
||||||
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data),
|
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data),
|
||||||
OldestXmin))
|
OldestXmin))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Well, try to find tuple with old row version */
|
/* No, move to tuple with prior row version */
|
||||||
for (;;)
|
vtld.new_tid = tp.t_self;
|
||||||
|
vtlp = (VTupleLink)
|
||||||
|
vac_bsearch((void *) &vtld,
|
||||||
|
(void *) (vacrelstats->vtlinks),
|
||||||
|
vacrelstats->num_vtlinks,
|
||||||
|
sizeof(VTupleLinkData),
|
||||||
|
vac_cmp_vtlinks);
|
||||||
|
if (vtlp == NULL)
|
||||||
{
|
{
|
||||||
Buffer Pbuf;
|
/* see discussion above */
|
||||||
Page Ppage;
|
elog(WARNING, "Parent item in update-chain not found - can't continue repair_frag");
|
||||||
ItemId Pitemid;
|
chain_move_failed = true;
|
||||||
HeapTupleData Ptp;
|
break; /* out of check-all-items loop */
|
||||||
VTupleLinkData vtld,
|
|
||||||
*vtlp;
|
|
||||||
|
|
||||||
vtld.new_tid = tp.t_self;
|
|
||||||
vtlp = (VTupleLink)
|
|
||||||
vac_bsearch((void *) &vtld,
|
|
||||||
(void *) (vacrelstats->vtlinks),
|
|
||||||
vacrelstats->num_vtlinks,
|
|
||||||
sizeof(VTupleLinkData),
|
|
||||||
vac_cmp_vtlinks);
|
|
||||||
if (vtlp == NULL)
|
|
||||||
elog(ERROR, "Parent tuple was not found");
|
|
||||||
tp.t_self = vtlp->this_tid;
|
|
||||||
Pbuf = ReadBuffer(onerel,
|
|
||||||
ItemPointerGetBlockNumber(&(tp.t_self)));
|
|
||||||
Ppage = BufferGetPage(Pbuf);
|
|
||||||
Pitemid = PageGetItemId(Ppage,
|
|
||||||
ItemPointerGetOffsetNumber(&(tp.t_self)));
|
|
||||||
if (!ItemIdIsUsed(Pitemid))
|
|
||||||
elog(ERROR, "Parent itemid marked as unused");
|
|
||||||
Ptp.t_datamcxt = NULL;
|
|
||||||
Ptp.t_data = (HeapTupleHeader) PageGetItem(Ppage, Pitemid);
|
|
||||||
Assert(ItemPointerEquals(&(vtld.new_tid),
|
|
||||||
&(Ptp.t_data->t_ctid)));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read above about cases when
|
|
||||||
* !ItemIdIsUsed(Citemid) (child item is
|
|
||||||
* removed)... Due to the fact that at the moment
|
|
||||||
* we don't remove unuseful part of update-chain,
|
|
||||||
* it's possible to get too old parent row here.
|
|
||||||
* Like as in the case which caused this problem,
|
|
||||||
* we stop shrinking here. I could try to find
|
|
||||||
* real parent row but want not to do it because
|
|
||||||
* of real solution will be implemented anyway,
|
|
||||||
* latter, and we are too close to 6.5 release. -
|
|
||||||
* vadim 06/11/99
|
|
||||||
*/
|
|
||||||
if (!(TransactionIdEquals(HeapTupleHeaderGetXmax(Ptp.t_data),
|
|
||||||
HeapTupleHeaderGetXmin(tp.t_data))))
|
|
||||||
{
|
|
||||||
if (freeCbuf)
|
|
||||||
ReleaseBuffer(Cbuf);
|
|
||||||
freeCbuf = false;
|
|
||||||
ReleaseBuffer(Pbuf);
|
|
||||||
for (i = 0; i < num_vtmove; i++)
|
|
||||||
{
|
|
||||||
Assert(vtmove[i].vacpage->offsets_used > 0);
|
|
||||||
(vtmove[i].vacpage->offsets_used)--;
|
|
||||||
}
|
|
||||||
num_vtmove = 0;
|
|
||||||
elog(WARNING, "Too old parent tuple found - can't continue repair_frag");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef NOT_USED /* I'm not sure that this will wotk
|
|
||||||
* properly... */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this tuple is updated version of row and it
|
|
||||||
* was created by the same transaction then no one
|
|
||||||
* is interested in this tuple - mark it as
|
|
||||||
* removed.
|
|
||||||
*/
|
|
||||||
if (Ptp.t_data->t_infomask & HEAP_UPDATED &&
|
|
||||||
TransactionIdEquals(HeapTupleHeaderGetXmin(Ptp.t_data),
|
|
||||||
HeapTupleHeaderGetXmax(Ptp.t_data)))
|
|
||||||
{
|
|
||||||
Ptp.t_data->t_infomask &=
|
|
||||||
~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
|
|
||||||
Ptp.t_data->t_infomask |= HEAP_MOVED_OFF;
|
|
||||||
HeapTupleHeaderSetXvac(Ptp.t_data, myXID);
|
|
||||||
WriteBuffer(Pbuf);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
tp.t_datamcxt = Ptp.t_datamcxt;
|
|
||||||
tp.t_data = Ptp.t_data;
|
|
||||||
tlen = tp.t_len = ItemIdGetLength(Pitemid);
|
|
||||||
if (freeCbuf)
|
|
||||||
ReleaseBuffer(Cbuf);
|
|
||||||
Cbuf = Pbuf;
|
|
||||||
freeCbuf = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (num_vtmove == 0)
|
tp.t_self = vtlp->this_tid;
|
||||||
break;
|
Pbuf = ReadBuffer(onerel,
|
||||||
}
|
ItemPointerGetBlockNumber(&(tp.t_self)));
|
||||||
|
Ppage = BufferGetPage(Pbuf);
|
||||||
|
Pitemid = PageGetItemId(Ppage,
|
||||||
|
ItemPointerGetOffsetNumber(&(tp.t_self)));
|
||||||
|
/* this can't happen since we saw tuple earlier: */
|
||||||
|
if (!ItemIdIsUsed(Pitemid))
|
||||||
|
elog(ERROR, "Parent itemid marked as unused");
|
||||||
|
Ptp.t_datamcxt = NULL;
|
||||||
|
Ptp.t_data = (HeapTupleHeader) PageGetItem(Ppage, Pitemid);
|
||||||
|
|
||||||
|
/* ctid should not have changed since we saved it */
|
||||||
|
Assert(ItemPointerEquals(&(vtld.new_tid),
|
||||||
|
&(Ptp.t_data->t_ctid)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read above about cases when
|
||||||
|
* !ItemIdIsUsed(Citemid) (child item is
|
||||||
|
* removed)... Due to the fact that at the moment
|
||||||
|
* we don't remove unuseful part of update-chain,
|
||||||
|
* it's possible to get too old parent row here.
|
||||||
|
* Like as in the case which caused this problem,
|
||||||
|
* we stop shrinking here. I could try to find
|
||||||
|
* real parent row but want not to do it because
|
||||||
|
* of real solution will be implemented anyway,
|
||||||
|
* later, and we are too close to 6.5 release. -
|
||||||
|
* vadim 06/11/99
|
||||||
|
*/
|
||||||
|
if (!(TransactionIdEquals(HeapTupleHeaderGetXmax(Ptp.t_data),
|
||||||
|
HeapTupleHeaderGetXmin(tp.t_data))))
|
||||||
|
{
|
||||||
|
ReleaseBuffer(Pbuf);
|
||||||
|
elog(WARNING, "Too old parent tuple found - can't continue repair_frag");
|
||||||
|
chain_move_failed = true;
|
||||||
|
break; /* out of check-all-items loop */
|
||||||
|
}
|
||||||
|
tp.t_datamcxt = Ptp.t_datamcxt;
|
||||||
|
tp.t_data = Ptp.t_data;
|
||||||
|
tlen = tp.t_len = ItemIdGetLength(Pitemid);
|
||||||
|
if (freeCbuf)
|
||||||
|
ReleaseBuffer(Cbuf);
|
||||||
|
Cbuf = Pbuf;
|
||||||
|
freeCbuf = true;
|
||||||
|
} /* end of check-all-items loop */
|
||||||
|
|
||||||
if (freeCbuf)
|
if (freeCbuf)
|
||||||
ReleaseBuffer(Cbuf);
|
ReleaseBuffer(Cbuf);
|
||||||
if (num_vtmove == 0) /* chain can't be moved */
|
freeCbuf = false;
|
||||||
|
|
||||||
|
if (chain_move_failed)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Undo changes to offsets_used state. We don't bother
|
||||||
|
* cleaning up the amount-free state, since we're not
|
||||||
|
* going to do any further tuple motion.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < num_vtmove; i++)
|
||||||
|
{
|
||||||
|
Assert(vtmove[i].vacpage->offsets_used > 0);
|
||||||
|
(vtmove[i].vacpage->offsets_used)--;
|
||||||
|
}
|
||||||
pfree(vtmove);
|
pfree(vtmove);
|
||||||
break;
|
break; /* out of walk-along-page loop */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Okay, move the whle tuple chain
|
||||||
|
*/
|
||||||
ItemPointerSetInvalid(&Ctid);
|
ItemPointerSetInvalid(&Ctid);
|
||||||
for (ti = 0; ti < num_vtmove; ti++)
|
for (ti = 0; ti < num_vtmove; ti++)
|
||||||
{
|
{
|
||||||
@ -1962,12 +1977,15 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
|
|
||||||
WriteBuffer(cur_buffer);
|
WriteBuffer(cur_buffer);
|
||||||
WriteBuffer(Cbuf);
|
WriteBuffer(Cbuf);
|
||||||
}
|
} /* end of move-the-tuple-chain loop */
|
||||||
|
|
||||||
cur_buffer = InvalidBuffer;
|
cur_buffer = InvalidBuffer;
|
||||||
pfree(vtmove);
|
pfree(vtmove);
|
||||||
chain_tuple_moved = true;
|
chain_tuple_moved = true;
|
||||||
|
|
||||||
|
/* advance to next tuple in walk-along-page loop */
|
||||||
continue;
|
continue;
|
||||||
}
|
} /* end of is-tuple-in-chain test */
|
||||||
|
|
||||||
/* try to find new page for this tuple */
|
/* try to find new page for this tuple */
|
||||||
if (cur_buffer == InvalidBuffer ||
|
if (cur_buffer == InvalidBuffer ||
|
||||||
@ -2089,10 +2107,19 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
}
|
}
|
||||||
} /* walk along page */
|
} /* walk along page */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we broke out of the walk-along-page loop early (ie, still have
|
||||||
|
* offnum <= maxoff), then we failed to move some tuple off
|
||||||
|
* this page. No point in shrinking any more, so clean up and
|
||||||
|
* exit the per-page loop.
|
||||||
|
*/
|
||||||
if (offnum < maxoff && keep_tuples > 0)
|
if (offnum < maxoff && keep_tuples > 0)
|
||||||
{
|
{
|
||||||
OffsetNumber off;
|
OffsetNumber off;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix vacpage state for any unvisited tuples remaining on page
|
||||||
|
*/
|
||||||
for (off = OffsetNumberNext(offnum);
|
for (off = OffsetNumberNext(offnum);
|
||||||
off <= maxoff;
|
off <= maxoff;
|
||||||
off = OffsetNumberNext(off))
|
off = OffsetNumberNext(off))
|
||||||
@ -2154,7 +2181,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
|
|||||||
ReleaseBuffer(buf);
|
ReleaseBuffer(buf);
|
||||||
|
|
||||||
if (offnum <= maxoff)
|
if (offnum <= maxoff)
|
||||||
break; /* some item(s) left */
|
break; /* had to quit early, see above note */
|
||||||
|
|
||||||
} /* walk along relation */
|
} /* walk along relation */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user