*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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),
vac_cmp_vtlinks);
free_size, usable_free_size,
empty_end_pages, fraged_pages->num_pages,
vac_show_rusage(&ru0));
-
}
* If this tuple is in the chain of tuples created in updates
* by "recent" transactions then we have to move all chain of
* 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),
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),
&(tuple.t_data->t_ctid)))))
{
Buffer Cbuf = buf;
+ bool freeCbuf = false;
+ bool chain_move_failed = false;
Page Cpage;
ItemId Citemid;
ItemPointerData Ctid;
HeapTupleData tp = tuple;
Size tlen = tuple_len;
- VTupleMove vtmove = (VTupleMove)
- palloc(100 * sizeof(VTupleMoveData));
- int num_vtmove = 0;
- int free_vtmove = 100;
+ VTupleMove vtmove;
+ int num_vtmove;
+ int free_vtmove;
VacPage to_vacpage = NULL;
int to_item = 0;
- bool freeCbuf = false;
int ti;
- if (vacrelstats->vtlinks == NULL)
- elog(ERROR, "No one parent tuple was found");
if (cur_buffer != InvalidBuffer)
{
WriteBuffer(cur_buffer);
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
* 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),
&(tp.t_data->t_ctid))))
{
* in scan_heap(), but it's not implemented at the
* 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");
- break;
+ chain_move_failed = true;
+ break; /* out of loop to move to chain end */
}
tp.t_datamcxt = NULL;
tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);
tp.t_self = Ctid;
tlen = tp.t_len = ItemIdGetLength(Citemid);
}
- if (vtmove == NULL)
- break;
- /* first, can chain be moved ? */
+ if (chain_move_failed)
+ {
+ if (freeCbuf)
+ ReleaseBuffer(Cbuf);
+ pfree(vtmove);
+ break; /* out of walk-along-page loop */
+ }
+
+ /*
+ * Check if all items in chain can be moved
+ */
for (;;)
{
+ Buffer Pbuf;
+ Page Ppage;
+ ItemId Pitemid;
+ HeapTupleData Ptp;
+ VTupleLinkData vtld,
+ *vtlp;
+
if (to_vacpage == NULL ||
!enough_space(to_vacpage, tlen))
{
if (i == num_fraged_pages)
{
/* can't move item anywhere */
- for (i = 0; i < num_vtmove; i++)
- {
- Assert(vtmove[i].vacpage->offsets_used > 0);
- (vtmove[i].vacpage->offsets_used)--;
- }
- num_vtmove = 0;
- break;
+ chain_move_failed = true;
+ break; /* out of check-all-items loop */
}
to_item = i;
to_vacpage = fraged_pages->pagedesc[to_item];
if (free_vtmove == 0)
{
free_vtmove = 1000;
- vtmove = (VTupleMove) repalloc(vtmove,
- (free_vtmove + num_vtmove) *
- sizeof(VTupleMoveData));
+ vtmove = (VTupleMove)
+ repalloc(vtmove,
+ (free_vtmove + num_vtmove) *
+ sizeof(VTupleMoveData));
}
vtmove[num_vtmove].tid = tp.t_self;
vtmove[num_vtmove].vacpage = to_vacpage;
free_vtmove--;
num_vtmove++;
- /* All done ? */
+ /* At beginning of chain? */
if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tp.t_data),
OldestXmin))
break;
- /* Well, try to find tuple with old row version */
- for (;;)
+ /* No, move to tuple with prior row version */
+ 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;
- Page Ppage;
- ItemId Pitemid;
- HeapTupleData Ptp;
- 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... */
+ /* see discussion above */
+ elog(WARNING, "Parent item in update-chain not found - can't continue repair_frag");
+ chain_move_failed = true;
+ break; /* out of check-all-items loop */
+ }
+ tp.t_self = vtlp->this_tid;
+ 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)));
- /*
- * 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;
+ /*
+ * 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 */
}
- if (num_vtmove == 0)
- break;
- }
+ 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)
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);
- break;
+ break; /* out of walk-along-page loop */
}
+
+ /*
+ * Okay, move the whle tuple chain
+ */
ItemPointerSetInvalid(&Ctid);
for (ti = 0; ti < num_vtmove; ti++)
{
WriteBuffer(cur_buffer);
WriteBuffer(Cbuf);
- }
+ } /* end of move-the-tuple-chain loop */
+
cur_buffer = InvalidBuffer;
pfree(vtmove);
chain_tuple_moved = true;
+
+ /* advance to next tuple in walk-along-page loop */
continue;
- }
+ } /* end of is-tuple-in-chain test */
/* try to find new page for this tuple */
if (cur_buffer == InvalidBuffer ||
}
} /* 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)
{
OffsetNumber off;
+ /*
+ * Fix vacpage state for any unvisited tuples remaining on page
+ */
for (off = OffsetNumberNext(offnum);
off <= maxoff;
off = OffsetNumberNext(off))
ReleaseBuffer(buf);
if (offnum <= maxoff)
- break; /* some item(s) left */
+ break; /* had to quit early, see above note */
} /* walk along relation */