From 386f1809a7db5c4d610ea8ba7cd702e699f1b4a9 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 24 Jan 2002 15:31:43 +0000
Subject: [PATCH] Fix logic error in insert_fsm_page_entry: because
 compact_fsm_page_list removes any empty chunks, the chunk previously added
 won't be there anymore, so it's possible there is zero free space in the
 rel's page list afterwards.  Must loop back and rerun the part that adds a
 chunk to the list.

---
 src/backend/storage/freespace/freespace.c | 97 +++++++++++------------
 1 file changed, 46 insertions(+), 51 deletions(-)

diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index 8d06cbbffcb..39634531350 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.10 2001/11/05 17:46:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.11 2002/01/24 15:31:43 tgl Exp $
  *
  *
  * NOTES:
@@ -841,64 +841,59 @@ static bool
 insert_fsm_page_entry(FSMRelation *fsmrel, BlockNumber page, Size spaceAvail,
 					  FSMChunk *chunk, int chunkRelIndex)
 {
-	FSMChunk   *newChunk;
-	int			newChunkRelIndex;
-
-	if (fsmrel->numPages >= fsmrel->numChunks * CHUNKPAGES)
+	/* Outer loop handles retry after compacting rel's page list */
+	for (;;)
 	{
-		/* No free space within chunk list, so need another chunk */
-		if ((newChunk = FreeSpaceMap->freeChunks) == NULL)
-			return false;		/* can't do it */
-		FreeSpaceMap->freeChunks = newChunk->next;
-		FreeSpaceMap->numFreeChunks--;
-		newChunk->next = NULL;
-		newChunk->numPages = 0;
-		if (fsmrel->relChunks == NULL)
-			fsmrel->relChunks = newChunk;
-		else
+		if (fsmrel->numPages >= fsmrel->numChunks * CHUNKPAGES)
 		{
-			FSMChunk   *priorChunk = fsmrel->relChunks;
+			/* No free space within chunk list, so need another chunk */
+			FSMChunk   *newChunk;
+
+			if ((newChunk = FreeSpaceMap->freeChunks) == NULL)
+				return false;		/* can't do it */
+			FreeSpaceMap->freeChunks = newChunk->next;
+			FreeSpaceMap->numFreeChunks--;
+			newChunk->next = NULL;
+			newChunk->numPages = 0;
+			if (fsmrel->relChunks == NULL)
+				fsmrel->relChunks = newChunk;
+			else
+			{
+				FSMChunk   *priorChunk = fsmrel->relChunks;
 
-			while (priorChunk->next != NULL)
-				priorChunk = priorChunk->next;
-			priorChunk->next = newChunk;
+				while (priorChunk->next != NULL)
+					priorChunk = priorChunk->next;
+				priorChunk->next = newChunk;
+			}
+			fsmrel->numChunks++;
+			if (chunk == NULL)
+			{
+				/* Original search found that new page belongs at end */
+				chunk = newChunk;
+				chunkRelIndex = 0;
+			}
 		}
-		fsmrel->numChunks++;
-		if (chunk == NULL)
+
+		/* Try to insert it the easy way, ie, just move down subsequent data */
+		if (chunk &&
+			push_fsm_page_entry(page, spaceAvail, chunk, chunkRelIndex))
 		{
-			/* Original search found that new page belongs at end */
-			chunk = newChunk;
-			chunkRelIndex = 0;
+			fsmrel->numPages++;
+			fsmrel->nextPage++;		/* don't return same page twice running */
+			return true;
 		}
-	}
 
-	/* Try to insert it the easy way, ie, just move down subsequent data */
-	if (chunk &&
-		push_fsm_page_entry(page, spaceAvail, chunk, chunkRelIndex))
-	{
-		fsmrel->numPages++;
-		fsmrel->nextPage++;		/* don't return same page twice running */
-		return true;
-	}
-
-	/*
-	 * There is space available, but evidently it's before the place where
-	 * the page entry needs to go.	Compact the list and try again. This
-	 * will require us to redo the search for the appropriate place.
-	 */
-	compact_fsm_page_list(fsmrel);
-	if (lookup_fsm_page_entry(fsmrel, page, &newChunk, &newChunkRelIndex))
-		elog(ERROR, "insert_fsm_page_entry: entry already exists!");
-	if (newChunk &&
-		push_fsm_page_entry(page, spaceAvail, newChunk, newChunkRelIndex))
-	{
-		fsmrel->numPages++;
-		fsmrel->nextPage++;		/* don't return same page twice running */
-		return true;
+		/*
+		 * There is space available, but evidently it's before the place where
+		 * the page entry needs to go.	Compact the list and try again. This
+		 * will require us to redo the search for the appropriate place.
+		 * Furthermore, compact_fsm_page_list deletes empty end chunks, so
+		 * we may need to repeat the action of grabbing a new end chunk.
+		 */
+		compact_fsm_page_list(fsmrel);
+		if (lookup_fsm_page_entry(fsmrel, page, &chunk, &chunkRelIndex))
+			elog(ERROR, "insert_fsm_page_entry: entry already exists!");
 	}
-	/* Shouldn't get here given the initial if-test for space available */
-	elog(ERROR, "insert_fsm_page_entry: failed to insert entry!");
-	return false;				/* keep compiler quiet */
 }
 
 /*
-- 
GitLab