diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/vacuum.c | 46 |
1 files changed, 44 insertions, 2 deletions
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 885b6a367b3..abb0eda4f7f 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.317.2.3 2006/01/18 20:35:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.317.2.4 2007/03/14 18:49:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1862,6 +1862,15 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, * 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. + * + * 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), @@ -1874,6 +1883,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; @@ -1901,7 +1911,13 @@ 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. + * 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_IS_LOCKED)) && @@ -1915,6 +1931,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); @@ -1945,6 +1962,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; @@ -2017,6 +2047,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; + /* Done if at beginning of chain */ if (!(tp.t_data->t_infomask & HEAP_UPDATED) || TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data), @@ -2086,6 +2121,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) { /* |