diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/vacuum.c | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index b5ece5729f5..cd81adb172a 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.299.4.1 2005/08/25 19:44:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.299.4.2 2007/03/14 18:49:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1782,6 +1782,15 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, * we cannot find the parent tuple in vtlinks. This may be * overly conservative; AFAICS it would be safe to move the * chain. + * + * Also, because we distinguish DEAD and RECENTLY_DEAD tuples + * using OldestXmin, which is a rather coarse test, it is quite + * possible to have an update chain in which a tuple we think is + * RECENTLY_DEAD links forward to one that is definitely DEAD. + * In such a case the RECENTLY_DEAD tuple must actually be dead, + * but it seems too complicated to try to make VACUUM remove it. + * We treat each contiguous set of RECENTLY_DEAD tuples as a + * separately movable chain, ignoring any intervening DEAD ones. */ if (((tuple.t_data->t_infomask & HEAP_UPDATED) && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data), @@ -1794,6 +1803,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, Buffer Cbuf = buf; bool freeCbuf = false; bool chain_move_failed = false; + bool moved_target = false; ItemPointerData Ctid; HeapTupleData tp = tuple; Size tlen = tuple_len; @@ -1821,7 +1831,14 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, * If this tuple is in the begin/middle of the chain then * we have to move to the end of chain. As with any * t_ctid chase, we have to verify that each new tuple - * is really the descendant of the tuple we came from. + * is really the descendant of the tuple we came from; + * however, here we need even more than the normal amount of + * paranoia. If t_ctid links forward to a tuple determined + * to be DEAD, then depending on where that tuple is, it + * might already have been removed, and perhaps even replaced + * by a MOVED_IN tuple. We don't want to include any DEAD + * tuples in the chain, so we have to recheck + * HeapTupleSatisfiesVacuum. */ while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE)) && @@ -1835,6 +1852,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, OffsetNumber nextOffnum; ItemId nextItemid; HeapTupleHeader nextTdata; + HTSV_Result nextTstatus; nextTid = tp.t_data->t_ctid; priorXmax = HeapTupleHeaderGetXmax(tp.t_data); @@ -1865,6 +1883,19 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ReleaseBuffer(nextBuf); break; } + /* must check for DEAD or MOVED_IN tuple, too */ + nextTstatus = HeapTupleSatisfiesVacuum(nextTdata, + OldestXmin, + nextBuf); + if (nextTstatus == HEAPTUPLE_DEAD || + nextTstatus == HEAPTUPLE_INSERT_IN_PROGRESS) + { + ReleaseBuffer(nextBuf); + break; + } + /* if it's MOVED_OFF we shoulda moved this one with it */ + if (nextTstatus == HEAPTUPLE_DELETE_IN_PROGRESS) + elog(ERROR, "updated tuple is already HEAP_MOVED_OFF"); /* OK, switch our attention to the next tuple in chain */ tp.t_datamcxt = NULL; tp.t_data = nextTdata; @@ -1935,6 +1966,11 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, free_vtmove--; num_vtmove++; + /* Remember if we reached the original target tuple */ + if (ItemPointerGetBlockNumber(&tp.t_self) == blkno && + ItemPointerGetOffsetNumber(&tp.t_self) == offnum) + moved_target = true; + /* At beginning of chain? */ if (!(tp.t_data->t_infomask & HEAP_UPDATED) || TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data), @@ -2004,6 +2040,13 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ReleaseBuffer(Cbuf); freeCbuf = false; + /* Double-check that we will move the current target tuple */ + if (!moved_target && !chain_move_failed) + { + elog(DEBUG2, "failed to chain back to target --- cannot continue repair_frag"); + chain_move_failed = true; + } + if (chain_move_failed) { /* |