From de09da547a6328fd594160508b9597f02e75d06e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 24 May 2002 19:52:43 +0000
Subject: [PATCH] Wups, managed to break ANALYZE with one aspect of that
 heap_fetch change.

---
 src/backend/access/heap/heapam.c      | 38 +++++++++++++++++----------
 src/backend/access/index/indexam.c    |  6 ++++-
 src/backend/access/nbtree/nbtinsert.c |  4 +--
 3 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e763823a16..a6f7e59fae 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.136 2002/05/24 18:57:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.137 2002/05/24 19:52:43 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -873,15 +873,24 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
  * the tuple, fill in the remaining fields of *tuple, and check the tuple
  * against the specified snapshot.
  *
- * If successful (tuple passes snapshot time qual), then *userbuf is set to
- * the buffer holding the tuple and TRUE is returned.  The caller must
- * unpin the buffer when done with the tuple.
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned.  The caller
+ * must unpin the buffer when done with the tuple.
  *
- * If the tuple fails the time qual check, then FALSE will be returned.
- * When the caller specifies keep_buf = true, we retain the pin on the
- * buffer and return it in *userbuf (so the caller can still access the
- * tuple); when keep_buf = false, the pin is released and *userbuf is set
+ * If the tuple is not found, then tuple->t_data is set to NULL, *userbuf
+ * is set to InvalidBuffer, and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE will be
+ * returned. When the caller specifies keep_buf = true, we retain the pin
+ * on the buffer and return it in *userbuf (so the caller can still access
+ * the tuple); when keep_buf = false, the pin is released and *userbuf is set
  * to InvalidBuffer.
+ *
+ * It is somewhat inconsistent that we elog() on invalid block number but
+ * return false on invalid item number.  This is historical.  The only
+ * justification I can see is that the caller can relatively easily check the
+ * block number for validity, but cannot check the item number without reading
+ * the page himself.
  */
 bool
 heap_fetch(Relation relation,
@@ -928,17 +937,18 @@ heap_fetch(Relation relation,
 	lp = PageGetItemId(dp, offnum);
 
 	/*
-	 * more sanity checks
+	 * must check for deleted tuple (see for example analyze.c, which is
+	 * careful to pass an offnum in range, but doesn't know if the offnum
+	 * actually corresponds to an undeleted tuple).
 	 */
 	if (!ItemIdIsUsed(lp))
 	{
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 		ReleaseBuffer(buffer);
-
-		elog(ERROR, "heap_fetch: invalid tuple id (%s, %lu, %u)",
-			 RelationGetRelationName(relation),
-			 (unsigned long) ItemPointerGetBlockNumber(tid),
-			 offnum);
+		*userbuf = InvalidBuffer;
+		tuple->t_datamcxt = NULL;
+		tuple->t_data = NULL;
+		return false;
 	}
 
 	/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 8c0fbaa6a1..475a613204 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.59 2002/05/24 18:57:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.60 2002/05/24 19:52:43 tgl Exp $
  *
  * INTERFACE ROUTINES
  *		index_open		- open an index relation by relation OID
@@ -444,6 +444,10 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
 					   &scan->xs_pgstat_info))
 			break;
 
+		/* Skip if no tuple at this location */
+		if (heapTuple->t_data == NULL)
+			continue;			/* should we raise an error instead? */
+
 		/*
 		 * If we can't see it, maybe no one else can either.  Check to see
 		 * if the tuple is dead to all transactions.  If so, signal the
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index fe3e98b982..27d0fc85c4 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.91 2002/05/24 18:57:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.92 2002/05/24 19:52:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -248,7 +248,7 @@ _bt_check_unique(Relation rel, BTItem btitem, Relation heapRel,
 					elog(ERROR, "Cannot insert a duplicate key into unique index %s",
 						 RelationGetRelationName(rel));
 				}
-				else
+				else if (htup.t_data != NULL)
 				{
 					/*
 					 * Hmm, if we can't see the tuple, maybe it can be
-- 
2.24.1