From 9ffc8ed58b55cb3925bb95cc184583fcb9772013 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 15 Oct 2004 22:40:29 +0000
Subject: [PATCH] Repair possible failure to update hint bits back to disk, per
 http://archives.postgresql.org/pgsql-hackers/2004-10/msg00464.php. This fix
 is intended to be permanent: it moves the responsibility for calling
 SetBufferCommitInfoNeedsSave() into the tqual.c routines, eliminating the
 requirement for callers to test whether t_infomask changed. Also, tighten
 validity checking on buffer IDs in bufmgr.c --- several routines were
 paranoid about out-of-range shared buffer numbers but not about out-of-range
 local ones, which seems a tad pointless.

---
 contrib/pgstattuple/pgstattuple.c     |  11 +-
 src/backend/access/heap/heapam.c      |  18 ++--
 src/backend/access/index/indexam.c    |  14 +--
 src/backend/access/nbtree/nbtinsert.c |  12 +--
 src/backend/catalog/index.c           |  18 ++--
 src/backend/commands/vacuum.c         |  17 ++-
 src/backend/commands/vacuumlazy.c     |  33 ++----
 src/backend/storage/buffer/bufmgr.c   |  33 +++---
 src/backend/utils/adt/ri_triggers.c   |   9 +-
 src/backend/utils/time/tqual.c        | 149 ++++++++++++++++++++++----
 src/include/access/valid.h            |  45 +++-----
 src/include/storage/bufmgr.h          |  24 ++---
 src/include/utils/tqual.h             |  34 +++---
 13 files changed, 241 insertions(+), 176 deletions(-)

diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index bfe37eb9ed6..e2fab7863eb 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.16 2004/08/29 05:06:37 momjian Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.17 2004/10/15 22:39:38 tgl Exp $
  *
  * Copyright (c) 2001,2002	Tatsuo Ishii
  *
@@ -134,7 +134,10 @@ pgstattuple_real(Relation rel)
 	/* scan the relation */
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
-		if (HeapTupleSatisfiesNow(tuple->t_data))
+		/* must hold a buffer lock to call HeapTupleSatisfiesNow */
+		LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+		if (HeapTupleSatisfiesNow(tuple->t_data, scan->rs_cbuf))
 		{
 			tuple_len += tuple->t_len;
 			tuple_count++;
@@ -145,6 +148,8 @@ pgstattuple_real(Relation rel)
 			dead_tuple_count++;
 		}
 
+		LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
 		/*
 		 * To avoid physically reading the table twice, try to do the
 		 * free-space scan in parallel with the heap scan.	However,
@@ -156,7 +161,9 @@ pgstattuple_real(Relation rel)
 		while (block <= tupblock)
 		{
 			buffer = ReadBuffer(rel, block);
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
 			free_space += PageGetFreeSpace((Page) BufferGetPage(buffer));
+			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buffer);
 			block++;
 		}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b9a6072c799..af5325ea632 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.178 2004/10/12 21:54:34 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.179 2004/10/15 22:39:42 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1314,7 +1314,7 @@ heap_delete(Relation relation, ItemPointer tid,
 	tp.t_tableOid = relation->rd_id;
 
 l1:
-	result = HeapTupleSatisfiesUpdate(tp.t_data, cid);
+	result = HeapTupleSatisfiesUpdate(tp.t_data, cid, buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -1331,7 +1331,7 @@ l1:
 		XactLockTableWait(xwait);
 
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-		if (TransactionIdDidAbort(xwait))
+		if (!TransactionIdDidCommit(xwait))
 			goto l1;
 
 		/*
@@ -1356,7 +1356,7 @@ l1:
 	if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
 	{
 		/* Perform additional check for serializable RI updates */
-		if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck))
+		if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck, buffer))
 			result = HeapTupleUpdated;
 	}
 
@@ -1543,7 +1543,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	 */
 
 l2:
-	result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid);
+	result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid, buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -1560,7 +1560,7 @@ l2:
 		XactLockTableWait(xwait);
 
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-		if (TransactionIdDidAbort(xwait))
+		if (!TransactionIdDidCommit(xwait))
 			goto l2;
 
 		/*
@@ -1585,7 +1585,7 @@ l2:
 	if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
 	{
 		/* Perform additional check for serializable RI updates */
-		if (!HeapTupleSatisfiesSnapshot(oldtup.t_data, crosscheck))
+		if (!HeapTupleSatisfiesSnapshot(oldtup.t_data, crosscheck, buffer))
 			result = HeapTupleUpdated;
 	}
 
@@ -1871,7 +1871,7 @@ heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer,
 	tuple->t_len = ItemIdGetLength(lp);
 
 l3:
-	result = HeapTupleSatisfiesUpdate(tuple->t_data, cid);
+	result = HeapTupleSatisfiesUpdate(tuple->t_data, cid, *buffer);
 
 	if (result == HeapTupleInvisible)
 	{
@@ -1888,7 +1888,7 @@ l3:
 		XactLockTableWait(xwait);
 
 		LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-		if (TransactionIdDidAbort(xwait))
+		if (!TransactionIdDidCommit(xwait))
 			goto l3;
 
 		/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 7c698fb48a2..214f646276b 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.75 2004/09/30 23:21:14 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.76 2004/10/15 22:39:46 tgl Exp $
  *
  * INTERFACE ROUTINES
  *		index_open		- open an index relation by relation OID
@@ -497,7 +497,6 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
 	for (;;)
 	{
 		bool		found;
-		uint16		sv_infomask;
 
 		pgstat_count_index_scan(&scan->xs_pgstat_info);
 
@@ -541,19 +540,14 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
 		 * index AM to not return it on future indexscans.
 		 *
 		 * We told heap_release_fetch to keep a pin on the buffer, so we can
-		 * re-access the tuple here.  But we must re-lock the buffer
-		 * first. Also, it's just barely possible for an update of hint
-		 * bits to occur here.
+		 * re-access the tuple here.  But we must re-lock the buffer first.
 		 */
 		LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
-		sv_infomask = heapTuple->t_data->t_infomask;
 
-		if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin) ==
-			HEAPTUPLE_DEAD)
+		if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
+									 scan->xs_cbuf) == HEAPTUPLE_DEAD)
 			scan->kill_prior_tuple = true;
 
-		if (sv_infomask != heapTuple->t_data->t_infomask)
-			SetBufferCommitInfoNeedsSave(scan->xs_cbuf);
 		LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
 	}
 
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 815e207fb2f..32615a62c27 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.116 2004/08/29 05:06:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.117 2004/10/15 22:39:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -261,19 +261,13 @@ _bt_check_unique(Relation rel, BTItem btitem, Relation heapRel,
 					 * marked killed.  This logic should match
 					 * index_getnext and btgettuple.
 					 */
-					uint16		sv_infomask;
-
 					LockBuffer(hbuffer, BUFFER_LOCK_SHARE);
-					sv_infomask = htup.t_data->t_infomask;
-					if (HeapTupleSatisfiesVacuum(htup.t_data,
-												 RecentGlobalXmin) ==
-						HEAPTUPLE_DEAD)
+					if (HeapTupleSatisfiesVacuum(htup.t_data, RecentGlobalXmin,
+												 hbuffer) == HEAPTUPLE_DEAD)
 					{
 						curitemid->lp_flags |= LP_DELETE;
 						SetBufferCommitInfoNeedsSave(buf);
 					}
-					if (sv_infomask != htup.t_data->t_infomask)
-						SetBufferCommitInfoNeedsSave(hbuffer);
 					LockBuffer(hbuffer, BUFFER_LOCK_UNLOCK);
 					ReleaseBuffer(hbuffer);
 				}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8fb2f8f4f22..19fc60b6c88 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.240 2004/10/01 17:11:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.241 2004/10/15 22:39:53 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1472,18 +1472,16 @@ IndexBuildHeapScan(Relation heapRelation,
 		{
 			/* do our own time qual check */
 			bool		indexIt;
-			uint16		sv_infomask;
 
 			/*
-			 * HeapTupleSatisfiesVacuum may update tuple's hint status
-			 * bits. We could possibly get away with not locking the
-			 * buffer here, since caller should hold ShareLock on the
-			 * relation, but let's be conservative about it.
+			 * We could possibly get away with not locking the buffer here,
+			 * since caller should hold ShareLock on the relation, but let's
+			 * be conservative about it.
 			 */
 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-			sv_infomask = heapTuple->t_data->t_infomask;
 
-			switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin))
+			switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin,
+											 scan->rs_cbuf))
 			{
 				case HEAPTUPLE_DEAD:
 					indexIt = false;
@@ -1544,10 +1542,6 @@ IndexBuildHeapScan(Relation heapRelation,
 					break;
 			}
 
-			/* check for hint-bit update by HeapTupleSatisfiesVacuum */
-			if (sv_infomask != heapTuple->t_data->t_infomask)
-				SetBufferCommitInfoNeedsSave(scan->rs_cbuf);
-
 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
 
 			if (!indexIt)
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 232ec9a89e6..3d0c6e0e996 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.294 2004/10/07 14:19:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.295 2004/10/15 22:39:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1190,6 +1190,12 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 		buf = ReadBuffer(onerel, blkno);
 		page = BufferGetPage(buf);
 
+		/*
+		 * We don't bother to do LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE)
+		 * because we assume that holding exclusive lock on the relation
+		 * will keep other backends from looking at the page.
+		 */
+
 		vacpage->blkno = blkno;
 		vacpage->offsets_used = 0;
 		vacpage->offsets_free = 0;
@@ -1235,7 +1241,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 			 offnum <= maxoff;
 			 offnum = OffsetNumberNext(offnum))
 		{
-			uint16		sv_infomask;
 			ItemId		itemid = PageGetItemId(page, offnum);
 			bool		tupgone = false;
 
@@ -1255,9 +1260,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 			tuple.t_len = ItemIdGetLength(itemid);
 			ItemPointerSet(&(tuple.t_self), blkno, offnum);
 
-			sv_infomask = tuple.t_data->t_infomask;
-
-			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
 			{
 				case HEAPTUPLE_DEAD:
 					tupgone = true;		/* we can delete the tuple */
@@ -1348,10 +1351,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 					break;
 			}
 
-			/* check for hint-bit update by HeapTupleSatisfiesVacuum */
-			if (sv_infomask != tuple.t_data->t_infomask)
-				pgchanged = true;
-
 			if (tupgone)
 			{
 				ItemId		lpp;
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index ebdb27f336f..62ba05f9698 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.46 2004/09/30 23:21:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.47 2004/10/15 22:39:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -291,7 +291,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			 offnum = OffsetNumberNext(offnum))
 		{
 			ItemId		itemid;
-			uint16		sv_infomask;
 
 			itemid = PageGetItemId(page, offnum);
 
@@ -307,9 +306,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			ItemPointerSet(&(tuple.t_self), blkno, offnum);
 
 			tupgone = false;
-			sv_infomask = tuple.t_data->t_infomask;
 
-			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
 			{
 				case HEAPTUPLE_DEAD:
 					tupgone = true;		/* we can delete the tuple */
@@ -364,10 +362,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 					break;
 			}
 
-			/* check for hint-bit update by HeapTupleSatisfiesVacuum */
-			if (sv_infomask != tuple.t_data->t_infomask)
-				pgchanged = true;
-
 			if (tupgone)
 			{
 				lazy_record_dead_tuple(vacrelstats, &(tuple.t_self));
@@ -399,9 +393,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 
 		if (pgchanged)
-			SetBufferCommitInfoNeedsSave(buf);
-
-		ReleaseBuffer(buf);
+			WriteBuffer(buf);
+		else
+			ReleaseBuffer(buf);
 	}
 
 	/* save stats for use later */
@@ -790,8 +784,7 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 		Page		page;
 		OffsetNumber offnum,
 					maxoff;
-		bool		pgchanged,
-					tupgone,
+		bool		tupgone,
 					hastup;
 
 		vacuum_delay_point();
@@ -813,7 +806,6 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 			continue;
 		}
 
-		pgchanged = false;
 		hastup = false;
 		maxoff = PageGetMaxOffsetNumber(page);
 		for (offnum = FirstOffsetNumber;
@@ -821,7 +813,6 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 			 offnum = OffsetNumberNext(offnum))
 		{
 			ItemId		itemid;
-			uint16		sv_infomask;
 
 			itemid = PageGetItemId(page, offnum);
 
@@ -834,9 +825,8 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 			ItemPointerSet(&(tuple.t_self), blkno, offnum);
 
 			tupgone = false;
-			sv_infomask = tuple.t_data->t_infomask;
 
-			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+			switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
 			{
 				case HEAPTUPLE_DEAD:
 					tupgone = true;		/* we can delete the tuple */
@@ -862,10 +852,6 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 					break;
 			}
 
-			/* check for hint-bit update by HeapTupleSatisfiesVacuum */
-			if (sv_infomask != tuple.t_data->t_infomask)
-				pgchanged = true;
-
 			if (!tupgone)
 			{
 				hastup = true;
@@ -875,10 +861,7 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 
-		if (pgchanged)
-			WriteBuffer(buf);
-		else
-			ReleaseBuffer(buf);
+		ReleaseBuffer(buf);
 
 		/* Done scanning if we found a tuple here */
 		if (hastup)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index b98193ad38c..a0f2bf9c117 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.177 2004/09/06 17:31:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.178 2004/10/15 22:39:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -477,15 +477,15 @@ write_buffer(Buffer buffer, bool release)
 {
 	BufferDesc *bufHdr;
 
+	if (!BufferIsValid(buffer))
+		elog(ERROR, "bad buffer id: %d", buffer);
+
 	if (BufferIsLocal(buffer))
 	{
 		WriteLocalBuffer(buffer, release);
 		return;
 	}
 
-	if (BAD_BUFFER_ID(buffer))
-		elog(ERROR, "bad buffer id: %d", buffer);
-
 	bufHdr = &BufferDescriptors[buffer - 1];
 
 	Assert(PrivateRefCount[buffer - 1] > 0);
@@ -1465,6 +1465,9 @@ ReleaseBuffer(Buffer buffer)
 {
 	BufferDesc *bufHdr;
 
+	if (!BufferIsValid(buffer))
+		elog(ERROR, "bad buffer id: %d", buffer);
+
 	ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
 
 	if (BufferIsLocal(buffer))
@@ -1474,9 +1477,6 @@ ReleaseBuffer(Buffer buffer)
 		return;
 	}
 
-	if (BAD_BUFFER_ID(buffer))
-		elog(ERROR, "bad buffer id: %d", buffer);
-
 	bufHdr = &BufferDescriptors[buffer - 1];
 
 	Assert(PrivateRefCount[buffer - 1] > 0);
@@ -1503,17 +1503,16 @@ ReleaseBuffer(Buffer buffer)
 void
 IncrBufferRefCount(Buffer buffer)
 {
+	Assert(BufferIsValid(buffer));
 	ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
 	ResourceOwnerRememberBuffer(CurrentResourceOwner, buffer);
 	if (BufferIsLocal(buffer))
 	{
-		Assert(buffer >= -NLocBuffer);
 		Assert(LocalRefCount[-buffer - 1] > 0);
 		LocalRefCount[-buffer - 1]++;
 	}
 	else
 	{
-		Assert(!BAD_BUFFER_ID(buffer));
 		Assert(PrivateRefCount[buffer - 1] > 0);
 		PrivateRefCount[buffer - 1]++;
 	}
@@ -1606,9 +1605,12 @@ ReleaseAndReadBuffer_Debug(char *file,
  *
  *	Mark a buffer dirty when we have updated tuple commit-status bits in it.
  *
- * This is similar to WriteNoReleaseBuffer, except that we have not made a
- * critical change that has to be flushed to disk before xact commit --- the
- * status-bit update could be redone by someone else just as easily.
+ * This is essentially the same as WriteNoReleaseBuffer.  We preserve the
+ * distinction as a way of documenting that the caller has not made a critical
+ * data change --- the status-bit update could be redone by someone else just
+ * as easily.  Therefore, no WAL log record need be generated, whereas calls
+ * to WriteNoReleaseBuffer really ought to be associated with a WAL-entry-
+ * creating action.
  *
  * This routine might get called many times on the same page, if we are making
  * the first scan after commit of an xact that added/deleted many tuples.
@@ -1623,15 +1625,15 @@ SetBufferCommitInfoNeedsSave(Buffer buffer)
 {
 	BufferDesc *bufHdr;
 
+	if (!BufferIsValid(buffer))
+		elog(ERROR, "bad buffer id: %d", buffer);
+
 	if (BufferIsLocal(buffer))
 	{
 		WriteLocalBuffer(buffer, false);
 		return;
 	}
 
-	if (BAD_BUFFER_ID(buffer))
-		elog(ERROR, "bad buffer id: %d", buffer);
-
 	bufHdr = &BufferDescriptors[buffer - 1];
 
 	if ((bufHdr->flags & (BM_DIRTY | BM_JUST_DIRTIED)) !=
@@ -1662,7 +1664,6 @@ UnlockBuffers(void)
 		if (buflocks == 0)
 			continue;
 
-		Assert(BufferIsValid(i + 1));
 		buf = &(BufferDescriptors[i]);
 
 		HOLD_INTERRUPTS();		/* don't want to die() partway through... */
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 20ad56c31f1..b195e243393 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -17,7 +17,7 @@
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.73 2004/09/13 20:07:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.74 2004/10/15 22:40:11 tgl Exp $
  *
  * ----------
  */
@@ -224,10 +224,15 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 * We should not even consider checking the row if it is no longer
 	 * valid since it was either deleted (doesn't matter) or updated (in
 	 * which case it'll be checked with its final values).
+	 *
+	 * We do not know what buffer the new_row is in, but it doesn't matter
+	 * since it's not possible for a hint-bit update to occur here (the
+	 * new_row could only contain our own XID, and we haven't yet committed
+	 * or aborted...)
 	 */
 	if (new_row)
 	{
-		if (!HeapTupleSatisfiesItself(new_row->t_data))
+		if (!HeapTupleSatisfiesItself(new_row->t_data, InvalidBuffer))
 		{
 			heap_close(pk_rel, RowShareLock);
 			return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 3adab87ace4..f36e2c13b6c 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -3,20 +3,20 @@
  * tqual.c
  *	  POSTGRES "time" qualification code, ie, tuple visibility rules.
  *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted.  The caller is responsible for noticing any
- * change in t_infomask and scheduling a disk write if so.	Note that the
- * caller must hold at least a shared buffer context lock on the buffer
+ * The caller must hold at least a shared buffer context lock on the buffer
  * containing the tuple.  (VACUUM FULL assumes it's sufficient to have
  * exclusive lock on the containing relation, instead.)
  *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted.
+ *
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.79 2004/09/16 18:35:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.80 2004/10/15 22:40:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,7 +78,7 @@ TransactionId RecentGlobalXmin = InvalidTransactionId;
  *			 Xmax is not committed)))			that has not been committed
  */
 bool
-HeapTupleSatisfiesItself(HeapTupleHeader tuple)
+HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
@@ -96,9 +96,11 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -110,10 +112,14 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(xvac))
 					return false;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 			}
@@ -127,6 +133,7 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return true;
 			}
 
@@ -140,11 +147,17 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 		else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
 		{
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
-				tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+			{
+				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			return false;
 		}
 		else
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 	}
 
 	/* by here, the inserting transaction has committed */
@@ -169,7 +182,10 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 	if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
 	{
 		if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
-			tuple->t_infomask |= HEAP_XMAX_INVALID;		/* aborted */
+		{
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 		return true;
 	}
 
@@ -178,10 +194,12 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 	{
 		tuple->t_infomask |= HEAP_XMAX_INVALID;
+		SetBufferCommitInfoNeedsSave(buffer);
 		return true;
 	}
 
 	tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+	SetBufferCommitInfoNeedsSave(buffer);
 	return false;
 }
 
@@ -228,7 +246,7 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
  *		that do catalog accesses.  this is unfortunate, but not critical.
  */
 bool
-HeapTupleSatisfiesNow(HeapTupleHeader tuple)
+HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
@@ -246,9 +264,11 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -260,10 +280,14 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(xvac))
 					return false;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 			}
@@ -280,6 +304,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return true;
 			}
 
@@ -296,11 +321,17 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 		else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
 		{
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
-				tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+			{
+				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			return false;
 		}
 		else
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 	}
 
 	/* by here, the inserting transaction has committed */
@@ -328,7 +359,10 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 	if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
 	{
 		if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
-			tuple->t_infomask |= HEAP_XMAX_INVALID;		/* aborted */
+		{
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 		return true;
 	}
 
@@ -337,10 +371,12 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 	{
 		tuple->t_infomask |= HEAP_XMAX_INVALID;
+		SetBufferCommitInfoNeedsSave(buffer);
 		return true;
 	}
 
 	tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+	SetBufferCommitInfoNeedsSave(buffer);
 	return false;
 }
 
@@ -359,7 +395,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
  * table.
  */
 bool
-HeapTupleSatisfiesToast(HeapTupleHeader tuple)
+HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
@@ -377,9 +413,11 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -391,10 +429,14 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(xvac))
 					return false;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 			}
@@ -414,7 +456,8 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple)
  *	CurrentCommandId.
  */
 int
-HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
+HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
+						 Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
@@ -432,9 +475,11 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return HeapTupleInvisible;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -446,10 +491,14 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 				if (TransactionIdIsInProgress(xvac))
 					return HeapTupleInvisible;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return HeapTupleInvisible;
 				}
 			}
@@ -467,6 +516,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return HeapTupleMayBeUpdated;
 			}
 
@@ -485,11 +535,17 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 		else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
 		{
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
-				tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+			{
+				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			return HeapTupleInvisible;
 		}
 		else
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 	}
 
 	/* by here, the inserting transaction has committed */
@@ -519,7 +575,8 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 	{
 		if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 		{
-			tuple->t_infomask |= HEAP_XMAX_INVALID;		/* aborted */
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
 			return HeapTupleMayBeUpdated;
 		}
 		/* running xact */
@@ -531,10 +588,12 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
 	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 	{
 		tuple->t_infomask |= HEAP_XMAX_INVALID;
+		SetBufferCommitInfoNeedsSave(buffer);
 		return HeapTupleMayBeUpdated;
 	}
 
 	tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+	SetBufferCommitInfoNeedsSave(buffer);
 	return HeapTupleUpdated;	/* updated by other */
 }
 
@@ -556,7 +615,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
  * t_ctid (forward link) is returned if it's being updated.
  */
 bool
-HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
+HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
 {
 	SnapshotDirty->xmin = SnapshotDirty->xmax = InvalidTransactionId;
 	ItemPointerSetInvalid(&(SnapshotDirty->tid));
@@ -577,9 +636,11 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -591,10 +652,14 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(xvac))
 					return false;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 			}
@@ -608,6 +673,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return true;
 			}
 
@@ -623,6 +689,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return false;
 			}
 			SnapshotDirty->xmin = HeapTupleHeaderGetXmin(tuple);
@@ -630,7 +697,10 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 			return true;		/* in insertion by other */
 		}
 		else
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 	}
 
 	/* by here, the inserting transaction has committed */
@@ -657,7 +727,8 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 	{
 		if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 		{
-			tuple->t_infomask |= HEAP_XMAX_INVALID;		/* aborted */
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
 			return true;
 		}
 		/* running xact */
@@ -670,10 +741,12 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 	{
 		tuple->t_infomask |= HEAP_XMAX_INVALID;
+		SetBufferCommitInfoNeedsSave(buffer);
 		return true;
 	}
 
 	tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+	SetBufferCommitInfoNeedsSave(buffer);
 	SnapshotDirty->tid = tuple->t_ctid;
 	return false;				/* updated by other */
 }
@@ -700,7 +773,8 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
  * can't see it.)
  */
 bool
-HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
+HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
+						   Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
@@ -718,9 +792,11 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 				if (TransactionIdDidCommit(xvac))
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -732,10 +808,14 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 				if (TransactionIdIsInProgress(xvac))
 					return false;
 				if (TransactionIdDidCommit(xvac))
+				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+					SetBufferCommitInfoNeedsSave(buffer);
+				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
+					SetBufferCommitInfoNeedsSave(buffer);
 					return false;
 				}
 			}
@@ -753,6 +833,7 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
 			{
 				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return true;
 			}
 
@@ -769,11 +850,17 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 		else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
 		{
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
+			{
 				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			return false;
 		}
 		else
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 	}
 
 	/*
@@ -831,12 +918,16 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 		if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
 		{
 			if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
-				tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+			{
+				tuple->t_infomask |= HEAP_XMAX_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			return true;
 		}
 
 		/* xmax transaction committed */
 		tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+		SetBufferCommitInfoNeedsSave(buffer);
 	}
 
 	/*
@@ -886,7 +977,8 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
  * even if we see that the deleting transaction has committed.
  */
 HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
+HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
+						 Buffer buffer)
 {
 	/*
 	 * Has inserting transaction committed?
@@ -916,9 +1008,11 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			if (TransactionIdDidCommit(xvac))
 			{
 				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return HEAPTUPLE_DEAD;
 			}
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
 		{
@@ -929,10 +1023,14 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			if (TransactionIdIsInProgress(xvac))
 				return HEAPTUPLE_INSERT_IN_PROGRESS;
 			if (TransactionIdDidCommit(xvac))
+			{
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+				SetBufferCommitInfoNeedsSave(buffer);
+			}
 			else
 			{
 				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				SetBufferCommitInfoNeedsSave(buffer);
 				return HEAPTUPLE_DEAD;
 			}
 		}
@@ -946,7 +1044,10 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			return HEAPTUPLE_DELETE_IN_PROGRESS;
 		}
 		else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+		{
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 		else
 		{
 			/*
@@ -954,6 +1055,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			 * crashed
 			 */
 			tuple->t_infomask |= HEAP_XMIN_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
 			return HEAPTUPLE_DEAD;
 		}
 		/* Should only get here if we set XMIN_COMMITTED */
@@ -986,6 +1088,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			 * it did not and will never actually update it.
 			 */
 			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
 		}
 		return HEAPTUPLE_LIVE;
 	}
@@ -995,7 +1098,10 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 		if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
 			return HEAPTUPLE_DELETE_IN_PROGRESS;
 		else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
+		{
 			tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+			SetBufferCommitInfoNeedsSave(buffer);
+		}
 		else
 		{
 			/*
@@ -1003,6 +1109,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			 * crashed
 			 */
 			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			SetBufferCommitInfoNeedsSave(buffer);
 			return HEAPTUPLE_LIVE;
 		}
 		/* Should only get here if we set XMAX_COMMITTED */
diff --git a/src/include/access/valid.h b/src/include/access/valid.h
index 785ae80b243..62a547a181a 100644
--- a/src/include/access/valid.h
+++ b/src/include/access/valid.h
@@ -7,20 +7,18 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/valid.h,v 1.34 2004/08/29 04:13:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/valid.h,v 1.35 2004/10/15 22:40:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef VALID_H
 #define VALID_H
 
-/* ----------------
+/*
  *		HeapKeyTest
  *
- *		Test a heap tuple with respect to a scan key.
- * ----------------
+ *		Test a heap tuple to see if it satisfies a scan key.
  */
-
 #define HeapKeyTest(tuple, \
 					tupdesc, \
 					nkeys, \
@@ -28,9 +26,7 @@
 					result) \
 do \
 { \
-/* We use underscores to protect the variable passed in as parameters */ \
-/* We use two underscore here because this macro is included in the \
-   macro below */ \
+	/* Use underscores to protect the variables passed in as parameters */ \
 	int			__cur_nkeys = (nkeys); \
 	ScanKey		__cur_keys = (keys); \
  \
@@ -40,6 +36,12 @@ do \
 		Datum	__atp; \
 		bool	__isnull; \
 		Datum	__test; \
+ \
+		if (__cur_keys->sk_flags & SK_ISNULL) \
+		{ \
+			(result) = false; \
+			break; \
+		} \
  \
 		__atp = heap_getattr((tuple), \
 							 __cur_keys->sk_attno, \
@@ -47,13 +49,6 @@ do \
 							 &__isnull); \
  \
 		if (__isnull) \
-		{ \
-			/* XXX eventually should check if SK_ISNULL */ \
-			(result) = false; \
-			break; \
-		} \
- \
-		if (__cur_keys->sk_flags & SK_ISNULL) \
 		{ \
 			(result) = false; \
 			break; \
@@ -70,7 +65,7 @@ do \
 	} \
 } while (0)
 
-/* ----------------
+/*
  *		HeapTupleSatisfies
  *
  *	res is set TRUE if the HeapTuple satisfies the timequal and keytest,
@@ -83,7 +78,6 @@ do \
  *	least likely to fail, too.	we should really add the time qual test to
  *	the restriction and optimize it in the normal way.	this has interactions
  *	with joey's expensive function work.
- * ----------------
  */
 #define HeapTupleSatisfies(tuple, \
 						   relation, \
@@ -95,24 +89,13 @@ do \
 						   res) \
 do \
 { \
-/* We use underscores to protect the variable passed in as parameters */ \
 	if ((key) != NULL) \
-		HeapKeyTest(tuple, RelationGetDescr(relation), \
-						   (nKeys), (key), (res)); \
+		HeapKeyTest(tuple, RelationGetDescr(relation), nKeys, key, res); \
 	else \
 		(res) = true; \
  \
-	if (res) \
-	{ \
-		if ((relation)->rd_rel->relkind != RELKIND_UNCATALOGED) \
-		{ \
-			uint16	_infomask = (tuple)->t_data->t_infomask; \
-			\
-			(res) = HeapTupleSatisfiesVisibility((tuple), (snapshot)); \
-			if ((tuple)->t_data->t_infomask != _infomask) \
-				SetBufferCommitInfoNeedsSave(buffer); \
-		} \
-	} \
+	if ((res) && (relation)->rd_rel->relkind != RELKIND_UNCATALOGED) \
+		(res) = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer); \
 } while (0)
 
 #endif   /* VALID_H */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index cb8feda8bdf..04bc09e22c6 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.86 2004/08/29 05:06:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.87 2004/10/15 22:40:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,13 +51,14 @@ extern int32 *LocalRefCount;
  * These routines are beaten on quite heavily, hence the macroization.
  */
 
-#define BAD_BUFFER_ID(bid) ((bid) < 1 || (bid) > NBuffers)
-
 /*
  * BufferIsValid
  *		True iff the given buffer number is valid (either as a shared
  *		or local buffer).
  *
+ * This is not quite the inverse of the BufferIsInvalid() macro, since this
+ * adds sanity rangechecks on the buffer number.
+ *
  * Note: For a long time this was defined the same as BufferIsPinned,
  * that is it would say False if you didn't hold a pin on the buffer.
  * I believe this was bogus and served only to mask logic errors.
@@ -66,10 +67,9 @@ extern int32 *LocalRefCount;
  */
 #define BufferIsValid(bufnum) \
 ( \
-	BufferIsLocal(bufnum) ? \
-		((bufnum) >= -NLocBuffer) \
-	: \
-		(! BAD_BUFFER_ID(bufnum)) \
+	(bufnum) != InvalidBuffer && \
+	(bufnum) >= -NLocBuffer && \
+	(bufnum) <= NBuffers \
 )
 
 /*
@@ -81,15 +81,13 @@ extern int32 *LocalRefCount;
  */
 #define BufferIsPinned(bufnum) \
 ( \
-	BufferIsLocal(bufnum) ? \
-		((bufnum) >= -NLocBuffer && LocalRefCount[-(bufnum) - 1] > 0) \
+	!BufferIsValid(bufnum) ? \
+		false \
 	: \
-	( \
-		BAD_BUFFER_ID(bufnum) ? \
-			false \
+		BufferIsLocal(bufnum) ? \
+			(LocalRefCount[-(bufnum) - 1] > 0) \
 		: \
 			(PrivateRefCount[(bufnum) - 1] > 0) \
-	) \
 )
 
 /*
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 0854fa793a1..8aa6ed2c678 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * tqual.h
- *	  POSTGRES "time" qualification definitions, ie, tuple visibility rules.
+ *	  POSTGRES "time qualification" definitions, ie, tuple visibility rules.
  *
  *	  Should be moved/renamed...	- vadim 07/28/98
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.53 2004/09/16 18:35:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.54 2004/10/15 22:40:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 #include "access/htup.h"
 #include "access/xact.h"
+#include "storage/buf.h"
 
 
 /*
@@ -72,23 +73,23 @@ extern TransactionId RecentGlobalXmin;
  *		Assumes heap tuple is valid.
  *		Beware of multiple evaluations of snapshot argument.
  */
-#define HeapTupleSatisfiesVisibility(tuple, snapshot) \
+#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
 ((snapshot) == SnapshotNow ? \
-	HeapTupleSatisfiesNow((tuple)->t_data) \
+	HeapTupleSatisfiesNow((tuple)->t_data, buffer) \
 : \
 	((snapshot) == SnapshotSelf ? \
-		HeapTupleSatisfiesItself((tuple)->t_data) \
+		HeapTupleSatisfiesItself((tuple)->t_data, buffer) \
 	: \
 		((snapshot) == SnapshotAny ? \
 			true \
 		: \
 			((snapshot) == SnapshotToast ? \
-				HeapTupleSatisfiesToast((tuple)->t_data) \
+				HeapTupleSatisfiesToast((tuple)->t_data, buffer) \
 			: \
 				((snapshot) == SnapshotDirty ? \
-					HeapTupleSatisfiesDirty((tuple)->t_data) \
+					HeapTupleSatisfiesDirty((tuple)->t_data, buffer) \
 				: \
-					HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
+					HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot, buffer) \
 				) \
 			) \
 		) \
@@ -108,21 +109,20 @@ typedef enum
 	HEAPTUPLE_DEAD,				/* tuple is dead and deletable */
 	HEAPTUPLE_LIVE,				/* tuple is live (committed, no deleter) */
 	HEAPTUPLE_RECENTLY_DEAD,	/* tuple is dead, but not deletable yet */
-	HEAPTUPLE_INSERT_IN_PROGRESS,		/* inserting xact is still in
-										 * progress */
+	HEAPTUPLE_INSERT_IN_PROGRESS,	/* inserting xact is still in progress */
 	HEAPTUPLE_DELETE_IN_PROGRESS	/* deleting xact is still in progress */
 } HTSV_Result;
 
-extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple);
+extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer);
 extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple,
-						   Snapshot snapshot);
+						   Snapshot snapshot, Buffer buffer);
 extern int HeapTupleSatisfiesUpdate(HeapTupleHeader tuple,
-						 CommandId curcid);
+						 CommandId curcid, Buffer buffer);
 extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,
-						 TransactionId OldestXmin);
+						 TransactionId OldestXmin, Buffer buffer);
 
 extern Snapshot GetTransactionSnapshot(void);
 extern Snapshot GetLatestSnapshot(void);
-- 
GitLab