I attached second version of space reservation patch. You can see first
version here:
http://archives.postgresql.org/pgsql-hackers/2008-12/msg00886.php

I thought about Heikki'es comments and I removed all catalog changes,
because there are not necessary to be in pg_class. Instead of
pg_preugrade script should create own schema (pg_upgrade) and tables on
its needs.

This patch implement settings like relation options. Tom had objection
about this approach. I can rewrite it and extend pg_class instead.
However before I will do it I would like to know opinion about rest of
the patch.

And last thing is most important. Space reservation MUST TO be implemented if we
want to have 8.4->8.5 upgrade. Else we will be at the begging...


        Zdenek 
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/common/reloptions.c pgsql_spacereserve/src/backend/access/common/reloptions.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/common/reloptions.c	2009-01-23 14:53:17.553968280 +0100
--- pgsql_spacereserve/src/backend/access/common/reloptions.c	2009-01-23 14:53:17.785452704 +0100
***************
*** 86,91 ****
--- 86,108 ----
  		},
  		GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
  	},
+ 	{
+ 		{
+ 			"rs_perpage",
+ 			"Page reserved space per page for in-place upgrade in bytes",
+ 			RELOPT_KIND_ALL
+ 		},
+ 		0, 0, BLCKSZ/4
+ 	},
+ 	{
+ 		{
+ 			"rs_pertuple",
+ 			"Page reserved space per tuple for in-place upgrade in bytes",
+ 			RELOPT_KIND_ALL
+ 		},
+ 		0, 0, BLCKSZ/16
+ 	},
+ 
  	/* list terminator */
  	{ { NULL } }
  };
***************
*** 592,598 ****
  	/* Build a list of expected options, based on kind */
  
  	for (i = 0; relOpts[i]; i++)
! 		if (relOpts[i]->kind == kind)
  			numoptions++;
  
  	if (numoptions == 0)
--- 609,615 ----
  	/* Build a list of expected options, based on kind */
  
  	for (i = 0; relOpts[i]; i++)
! 		if (relOpts[i]->kind == kind || relOpts[i]->kind == RELOPT_KIND_ALL)
  			numoptions++;
  
  	if (numoptions == 0)
***************
*** 605,611 ****
  
  	for (i = 0, j = 0; relOpts[i]; i++)
  	{
! 		if (relOpts[i]->kind == kind)
  		{
  			reloptions[j].gen = relOpts[i];
  			reloptions[j].isset = false;
--- 622,628 ----
  
  	for (i = 0, j = 0; relOpts[i]; i++)
  	{
! 		if (relOpts[i]->kind == kind || relOpts[i]->kind == RELOPT_KIND_ALL)
  		{
  			reloptions[j].gen = relOpts[i];
  			reloptions[j].isset = false;
***************
*** 868,874 ****
  
  
  /*
!  * Option parser for anything that uses StdRdOptions (i.e. fillfactor only)
   */
  bytea *
  default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
--- 885,891 ----
  
  
  /*
!  * Option parser for anything that uses StdRdOptions (i.e. fillfactor)
   */
  bytea *
  default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
***************
*** 877,883 ****
  	StdRdOptions   *rdopts;
  	int				numoptions;
  	relopt_parse_elt tab[] = {
! 		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}
  	};
  
  	options = parseRelOptions(reloptions, validate, kind, &numoptions);
--- 894,902 ----
  	StdRdOptions   *rdopts;
  	int				numoptions;
  	relopt_parse_elt tab[] = {
! 		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
! 		{"rs_perpage", RELOPT_TYPE_INT, offsetof(StdRdOptions, rs_perpage)},
! 		{"rs_pertuple", RELOPT_TYPE_INT, offsetof(StdRdOptions, rs_pertuple)}
  	};
  
  	options = parseRelOptions(reloptions, validate, kind, &numoptions);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/gin/ginentrypage.c pgsql_spacereserve/src/backend/access/gin/ginentrypage.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/gin/ginentrypage.c	2009-01-23 14:53:17.557611451 +0100
--- pgsql_spacereserve/src/backend/access/gin/ginentrypage.c	2009-01-23 14:53:17.785727884 +0100
***************
*** 314,320 ****
  		itupsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
  	}
  
! 	if (PageGetFreeSpace(page) + itupsz >= MAXALIGN(IndexTupleSize(btree->entry)) + sizeof(ItemIdData))
  		return true;
  
  	return false;
--- 314,323 ----
  		itupsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
  	}
  
! 	if (PageGetFreeSpace(page,
! 						 RelationGetReservedSpacePerPage(btree->index),
! 						 RelationGetReservedSpacePerTuple(btree->index)) + itupsz
! 			 >= MAXALIGN(IndexTupleSize(btree->entry)) + sizeof(ItemIdData))
  		return true;
  
  	return false;
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gist.c pgsql_spacereserve/src/backend/access/gist/gist.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gist.c	2009-01-23 14:53:17.566159107 +0100
--- pgsql_spacereserve/src/backend/access/gist/gist.c	2009-01-23 14:53:17.786021437 +0100
***************
*** 299,305 ****
  	 * XXX: If we want to change fillfactors between node and leaf, fillfactor
  	 * = (is_leaf ? state->leaf_fillfactor : state->node_fillfactor)
  	 */
! 	if (gistnospace(state->stack->page, state->itup, state->ituplen,
  					is_leaf ? InvalidOffsetNumber : state->stack->childoffnum,
  					state->freespace))
  	{
--- 299,305 ----
  	 * XXX: If we want to change fillfactors between node and leaf, fillfactor
  	 * = (is_leaf ? state->leaf_fillfactor : state->node_fillfactor)
  	 */
! 	if (gistnospace(state->r, state->stack->page, state->itup, state->ituplen,
  					is_leaf ? InvalidOffsetNumber : state->stack->childoffnum,
  					state->freespace))
  	{
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistutil.c pgsql_spacereserve/src/backend/access/gist/gistutil.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistutil.c	2009-01-23 14:53:17.571324407 +0100
--- pgsql_spacereserve/src/backend/access/gist/gistutil.c	2009-01-23 14:53:17.786183788 +0100
***************
*** 56,62 ****
   * Check space for itup vector on page
   */
  bool
! gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
  {
  	unsigned int size = freespace,
  				deleted = 0;
--- 56,62 ----
   * Check space for itup vector on page
   */
  bool
! gistnospace(Relation rel, Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
  {
  	unsigned int size = freespace,
  				deleted = 0;
***************
*** 72,78 ****
  		deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
  	}
  
! 	return (PageGetFreeSpace(page) + deleted < size);
  }
  
  bool
--- 72,79 ----
  		deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
  	}
  
! 	return (PageGetFreeSpace(page, RelationGetReservedSpacePerPage(rel), 
! 							 RelationGetReservedSpacePerTuple(rel)) + deleted < size);
  }
  
  bool
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistvacuum.c pgsql_spacereserve/src/backend/access/gist/gistvacuum.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistvacuum.c	2009-01-23 14:53:17.577307424 +0100
--- pgsql_spacereserve/src/backend/access/gist/gistvacuum.c	2009-01-23 14:53:17.786351410 +0100
***************
*** 385,391 ****
  		if (curlenaddon)
  		{
  			/* insert updated tuples */
! 			if (gistnospace(tempPage, addon, curlenaddon, InvalidOffsetNumber, 0))
  			{
  				/* there is no space on page to insert tuples */
  				res = vacuumSplitPage(gv, tempPage, buffer, addon, curlenaddon);
--- 385,391 ----
  		if (curlenaddon)
  		{
  			/* insert updated tuples */
! 			if (gistnospace(gv->index, tempPage, addon, curlenaddon, InvalidOffsetNumber, 0))
  			{
  				/* there is no space on page to insert tuples */
  				res = vacuumSplitPage(gv, tempPage, buffer, addon, curlenaddon);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistxlog.c pgsql_spacereserve/src/backend/access/gist/gistxlog.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/gist/gistxlog.c	2009-01-23 14:53:17.583642716 +0100
--- pgsql_spacereserve/src/backend/access/gist/gistxlog.c	2009-01-23 14:53:17.786531067 +0100
***************
*** 689,695 ****
  			 * hope, that wiil be enough space....
  			 */
  
! 			if (gistnospace(pages[0], itup, lenitup, *todelete, 0))
  			{
  
  				/* no space left on page, so we must split */
--- 689,695 ----
  			 * hope, that wiil be enough space....
  			 */
  
! 			if (gistnospace(index, pages[0], itup, lenitup, *todelete, 0))
  			{
  
  				/* no space left on page, so we must split */
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashinsert.c pgsql_spacereserve/src/backend/access/hash/hashinsert.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashinsert.c	2009-01-23 14:53:17.587065560 +0100
--- pgsql_spacereserve/src/backend/access/hash/hashinsert.c	2009-01-23 14:53:17.786787904 +0100
***************
*** 106,112 ****
  	Assert(pageopaque->hasho_bucket == bucket);
  
  	/* Do the insertion */
! 	while (PageGetFreeSpace(page) < itemsz)
  	{
  		/*
  		 * no space on this page; check for an overflow page
--- 106,113 ----
  	Assert(pageopaque->hasho_bucket == bucket);
  
  	/* Do the insertion */
! 	while (PageGetFreeSpace(page,RelationGetReservedSpacePerPage(rel),
! 			RelationGetReservedSpacePerTuple(rel)) < itemsz)
  	{
  		/*
  		 * no space on this page; check for an overflow page
***************
*** 138,144 ****
  			page = BufferGetPage(buf);
  
  			/* should fit now, given test above */
! 			Assert(PageGetFreeSpace(page) >= itemsz);
  		}
  		pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
  		Assert(pageopaque->hasho_flag == LH_OVERFLOW_PAGE);
--- 139,146 ----
  			page = BufferGetPage(buf);
  
  			/* should fit now, given test above */
! 			Assert(PageGetFreeSpace(page, RelationGetReservedSpacePerPage(rel),
! 				RelationGetReservedSpacePerTuple(rel)) >= itemsz);
  		}
  		pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
  		Assert(pageopaque->hasho_flag == LH_OVERFLOW_PAGE);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashovfl.c pgsql_spacereserve/src/backend/access/hash/hashovfl.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashovfl.c	2009-01-23 14:53:17.595447169 +0100
--- pgsql_spacereserve/src/backend/access/hash/hashovfl.c	2009-01-23 14:53:17.786962823 +0100
***************
*** 656,662 ****
  			 * Walk up the bucket chain, looking for a page big enough for
  			 * this item.  Exit if we reach the read page.
  			 */
! 			while (PageGetFreeSpace(wpage) < itemsz)
  			{
  				Assert(!PageIsEmpty(wpage));
  
--- 656,663 ----
  			 * Walk up the bucket chain, looking for a page big enough for
  			 * this item.  Exit if we reach the read page.
  			 */
! 			while (PageGetFreeSpace(wpage, RelationGetReservedSpacePerPage(rel),
! 					RelationGetReservedSpacePerTuple(rel)) < itemsz)
  			{
  				Assert(!PageIsEmpty(wpage));
  
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashpage.c pgsql_spacereserve/src/backend/access/hash/hashpage.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/hash/hashpage.c	2009-01-23 14:53:17.605903238 +0100
--- pgsql_spacereserve/src/backend/access/hash/hashpage.c	2009-01-23 14:53:17.787146081 +0100
***************
*** 856,862 ****
  			itemsz = IndexTupleDSize(*itup);
  			itemsz = MAXALIGN(itemsz);
  
! 			if (PageGetFreeSpace(npage) < itemsz)
  			{
  				/* write out nbuf and drop lock, but keep pin */
  				_hash_chgbufaccess(rel, nbuf, HASH_WRITE, HASH_NOLOCK);
--- 856,863 ----
  			itemsz = IndexTupleDSize(*itup);
  			itemsz = MAXALIGN(itemsz);
  
! 			if (PageGetFreeSpace(npage, RelationGetReservedSpacePerPage(rel),
! 				RelationGetReservedSpacePerTuple(rel)) < itemsz)
  			{
  				/* write out nbuf and drop lock, but keep pin */
  				_hash_chgbufaccess(rel, nbuf, HASH_WRITE, HASH_NOLOCK);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/heapam.c pgsql_spacereserve/src/backend/access/heap/heapam.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/heapam.c	2009-01-23 14:53:17.644676111 +0100
--- pgsql_spacereserve/src/backend/access/heap/heapam.c	2009-01-23 14:53:17.787631177 +0100
***************
*** 2635,2641 ****
  					  HeapTupleHasExternal(newtup) ||
  					  newtup->t_len > TOAST_TUPLE_THRESHOLD);
  
! 	pagefree = PageGetHeapFreeSpace(page);
  
  	newtupsize = MAXALIGN(newtup->t_len);
  
--- 2635,2643 ----
  					  HeapTupleHasExternal(newtup) ||
  					  newtup->t_len > TOAST_TUPLE_THRESHOLD);
  
! 	pagefree = PageGetHeapFreeSpace(page,
! 									RelationGetReservedSpacePerPage(relation),
! 									RelationGetReservedSpacePerTuple(relation));
  
  	newtupsize = MAXALIGN(newtup->t_len);
  
***************
*** 2700,2706 ****
  			/* Re-acquire the lock on the old tuple's page. */
  			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  			/* Re-check using the up-to-date free space */
! 			pagefree = PageGetHeapFreeSpace(page);
  			if (newtupsize > pagefree)
  			{
  				/*
--- 2702,2710 ----
  			/* Re-acquire the lock on the old tuple's page. */
  			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
  			/* Re-check using the up-to-date free space */
! 			pagefree = PageGetHeapFreeSpace(page,
! 											RelationGetReservedSpacePerPage(relation),
! 											RelationGetReservedSpacePerTuple(relation));
  			if (newtupsize > pagefree)
  			{
  				/*
***************
*** 4156,4162 ****
  							nowunused, nunused,
  							clean_move);
  
! 	freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
  
  	/*
  	 * Note: we don't worry about updating the page's prunability hints.
--- 4160,4168 ----
  							nowunused, nunused,
  							clean_move);
  
! 	/* needed to update FSM below, we ignore reservation here, because there is
! 		no relation information and FSM can be inaccurate */
! 	freespace = PageGetHeapFreeSpace(page, 0, 0); 
  
  	/*
  	 * Note: we don't worry about updating the page's prunability hints.
***************
*** 4398,4408 ****
  	HeapTupleHeaderSetCmin(htup, FirstCommandId);
  	htup->t_ctid = xlrec->target.tid;
  
  	offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
  	if (offnum == InvalidOffsetNumber)
  		elog(PANIC, "heap_insert_redo: failed to add tuple");
  
! 	freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
  
  	PageSetLSN(page, lsn);
  	PageSetTLI(page, ThisTimeLineID);
--- 4404,4418 ----
  	HeapTupleHeaderSetCmin(htup, FirstCommandId);
  	htup->t_ctid = xlrec->target.tid;
  
+ 	/* XXX: Should we check reserved space here? It is probably not problem,
+ 	  because item is added and WAL logged only if reserved space is OK */
  	offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
  	if (offnum == InvalidOffsetNumber)
  		elog(PANIC, "heap_insert_redo: failed to add tuple");
  
! 	/* needed to update FSM below, we ignore reservation here, because there is
! 		no relation information and FSM can be inaccurate */
! 	freespace = PageGetHeapFreeSpace(page, 0, 0); 
  
  	PageSetLSN(page, lsn);
  	PageSetTLI(page, ThisTimeLineID);
***************
*** 4636,4642 ****
  	if (xlrec->new_all_visible_cleared)
  		PageClearAllVisible(page);
  
! 	freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
  
  	PageSetLSN(page, lsn);
  	PageSetTLI(page, ThisTimeLineID);
--- 4646,4654 ----
  	if (xlrec->new_all_visible_cleared)
  		PageClearAllVisible(page);
  
! 	/* needed to update FSM below, we ignore reservation here, because there is
! 		no relation information and FSM can be inaccurate */
! 	freespace = PageGetHeapFreeSpace(page,0,0); 
  
  	PageSetLSN(page, lsn);
  	PageSetTLI(page, ThisTimeLineID);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/hio.c pgsql_spacereserve/src/backend/access/heap/hio.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/hio.c	2009-01-23 14:53:17.650378515 +0100
--- pgsql_spacereserve/src/backend/access/heap/hio.c	2009-01-23 14:53:17.787790126 +0100
***************
*** 269,275 ****
  		 * we're done.
  		 */
  		page = BufferGetPage(buffer);
! 		pageFreeSpace = PageGetHeapFreeSpace(page);
  		if (len + saveFreeSpace <= pageFreeSpace)
  		{
  			/* use this page as future insert target, too */
--- 269,277 ----
  		 * we're done.
  		 */
  		page = BufferGetPage(buffer);
! 		pageFreeSpace = PageGetHeapFreeSpace(page,
! 											 RelationGetReservedSpacePerPage(relation),
! 											 RelationGetReservedSpacePerTuple(relation));
  		if (len + saveFreeSpace <= pageFreeSpace)
  		{
  			/* use this page as future insert target, too */
***************
*** 362,368 ****
  
  	PageInit(page, BufferGetPageSize(buffer), 0);
  
! 	if (len > PageGetHeapFreeSpace(page))
  	{
  		/* We should not get here given the test at the top */
  		elog(PANIC, "tuple is too big: size %lu", (unsigned long) len);
--- 364,372 ----
  
  	PageInit(page, BufferGetPageSize(buffer), 0);
  
! 	if (len > PageGetHeapFreeSpace(page,
! 								   RelationGetReservedSpacePerPage(relation),
! 								   RelationGetReservedSpacePerTuple(relation)))
  	{
  		/* We should not get here given the test at the top */
  		elog(PANIC, "tuple is too big: size %lu", (unsigned long) len);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/pruneheap.c pgsql_spacereserve/src/backend/access/heap/pruneheap.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/pruneheap.c	2009-01-23 14:53:17.654232335 +0100
--- pgsql_spacereserve/src/backend/access/heap/pruneheap.c	2009-01-23 14:53:17.787968497 +0100
***************
*** 100,106 ****
  											 HEAP_DEFAULT_FILLFACTOR);
  	minfree = Max(minfree, BLCKSZ / 10);
  
! 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
  	{
  		/* OK, try to get exclusive buffer lock */
  		if (!ConditionalLockBufferForCleanup(buffer))
--- 100,109 ----
  											 HEAP_DEFAULT_FILLFACTOR);
  	minfree = Max(minfree, BLCKSZ / 10);
  
! 	if (PageIsFull(page) || 
! 		PageGetHeapFreeSpace(page,
! 							 RelationGetReservedSpacePerPage(relation),
! 							 RelationGetReservedSpacePerTuple(relation)) < minfree)
  	{
  		/* OK, try to get exclusive buffer lock */
  		if (!ConditionalLockBufferForCleanup(buffer))
***************
*** 112,118 ****
  		 * prune. (We needn't recheck PageIsPrunable, since no one else could
  		 * have pruned while we hold pin.)
  		 */
! 		if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
  		{
  			/* OK to prune (though not to remove redirects) */
  			(void) heap_page_prune(relation, buffer, OldestXmin, false, true);
--- 115,124 ----
  		 * prune. (We needn't recheck PageIsPrunable, since no one else could
  		 * have pruned while we hold pin.)
  		 */
! 		if (PageIsFull(page) || 
! 			PageGetHeapFreeSpace(page,
! 								 RelationGetReservedSpacePerPage(relation),
! 								 RelationGetReservedSpacePerTuple(relation)))
  		{
  			/* OK to prune (though not to remove redirects) */
  			(void) heap_page_prune(relation, buffer, OldestXmin, false, true);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/rewriteheap.c pgsql_spacereserve/src/backend/access/heap/rewriteheap.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/heap/rewriteheap.c	2009-01-23 14:53:17.657610976 +0100
--- pgsql_spacereserve/src/backend/access/heap/rewriteheap.c	2009-01-23 14:53:17.788139931 +0100
***************
*** 600,606 ****
  	/* Now we can check to see if there's enough free space already. */
  	if (state->rs_buffer_valid)
  	{
! 		pageFreeSpace = PageGetHeapFreeSpace(page);
  
  		if (len + saveFreeSpace > pageFreeSpace)
  		{
--- 600,608 ----
  	/* Now we can check to see if there's enough free space already. */
  	if (state->rs_buffer_valid)
  	{
! 		pageFreeSpace = PageGetHeapFreeSpace(page,
! 											 RelationGetReservedSpacePerPage(state->rs_new_rel),
! 											 RelationGetReservedSpacePerTuple(state->rs_new_rel));
  
  		if (len + saveFreeSpace > pageFreeSpace)
  		{
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/nbtree/nbtinsert.c pgsql_spacereserve/src/backend/access/nbtree/nbtinsert.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/nbtree/nbtinsert.c	2009-01-23 14:53:17.670183347 +0100
--- pgsql_spacereserve/src/backend/access/nbtree/nbtinsert.c	2009-01-23 14:53:17.788477683 +0100
***************
*** 79,85 ****
  			int keysz, ScanKey scankey);
  static void _bt_vacuum_one_page(Relation rel, Buffer buffer);
  
- 
  /*
   *	_bt_doinsert() -- Handle insertion of a single index tuple in the tree.
   *
--- 79,84 ----
***************
*** 445,451 ****
  	 */
  	movedright = false;
  	vacuumed = false;
! 	while (PageGetFreeSpace(page) < itemsz)
  	{
  		Buffer		rbuf;
  
--- 444,452 ----
  	 */
  	movedright = false;
  	vacuumed = false;
! 	while (PageGetFreeSpace(page,
! 						 	RelationGetReservedSpacePerPage(rel),
! 						 	RelationGetReservedSpacePerTuple(rel)) < itemsz)
  	{
  		Buffer		rbuf;
  
***************
*** 463,469 ****
  			 */
  			vacuumed = true;
  
! 			if (PageGetFreeSpace(page) >= itemsz)
  				break;			/* OK, now we have enough space */
  		}
  
--- 464,472 ----
  			 */
  			vacuumed = true;
  
! 			if (PageGetFreeSpace(page,
! 						 		 RelationGetReservedSpacePerPage(rel),
! 						 		 RelationGetReservedSpacePerTuple(rel)) >= itemsz)
  				break;			/* OK, now we have enough space */
  		}
  
***************
*** 579,585 ****
  	 * so this comparison is correct even though we appear to be accounting
  	 * only for the item and not for its line pointer.
  	 */
! 	if (PageGetFreeSpace(page) < itemsz)
  	{
  		bool		is_root = P_ISROOT(lpageop);
  		bool		is_only = P_LEFTMOST(lpageop) && P_RIGHTMOST(lpageop);
--- 582,590 ----
  	 * so this comparison is correct even though we appear to be accounting
  	 * only for the item and not for its line pointer.
  	 */
! 	if (PageGetFreeSpace(page,
! 						 RelationGetReservedSpacePerPage(rel),
! 						 RelationGetReservedSpacePerTuple(rel)) < itemsz)
  	{
  		bool		is_root = P_ISROOT(lpageop);
  		bool		is_only = P_LEFTMOST(lpageop) && P_RIGHTMOST(lpageop);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/access/nbtree/nbtsort.c pgsql_spacereserve/src/backend/access/nbtree/nbtsort.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/access/nbtree/nbtsort.c	2009-01-23 14:53:17.680045695 +0100
--- pgsql_spacereserve/src/backend/access/nbtree/nbtsort.c	2009-01-23 14:53:17.788658467 +0100
***************
*** 462,468 ****
  	nblkno = state->btps_blkno;
  	last_off = state->btps_lastoff;
  
! 	pgspc = PageGetFreeSpace(npage);
  	itupsz = IndexTupleDSize(*itup);
  	itupsz = MAXALIGN(itupsz);
  
--- 462,470 ----
  	nblkno = state->btps_blkno;
  	last_off = state->btps_lastoff;
  
! 	pgspc = PageGetFreeSpace(npage,
! 						 	 RelationGetReservedSpacePerPage(wstate->index),
! 						 	 RelationGetReservedSpacePerTuple(wstate->index));
  	itupsz = IndexTupleDSize(*itup);
  	itupsz = MAXALIGN(itupsz);
  
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/commands/vacuum.c pgsql_spacereserve/src/backend/commands/vacuum.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/commands/vacuum.c	2009-01-23 14:53:17.735830104 +0100
--- pgsql_spacereserve/src/backend/commands/vacuum.c	2009-01-23 14:53:17.789307050 +0100
***************
*** 3834,3839 ****
--- 3834,3845 ----
  	Size		freespace = PageGetExactFreeSpace(page);
  	Size		targetfree;
  
+ 	/* we need to reserve space, it could reserved more space because we count
+ 	   all line pointers, not only normal */
+ 	freespace -= (RelationGetReservedSpacePerPage(relation)+
+ 				  PageGetMaxOffsetNumber(page)*RelationGetReservedSpacePerTuple(relation));
+ 
+ 	freespace = freespace < 0 ? 0 : freespace;
  	targetfree = RelationGetTargetPageFreeSpace(relation,
  												HEAP_DEFAULT_FILLFACTOR);
  	if (freespace > targetfree)
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/commands/vacuumlazy.c pgsql_spacereserve/src/backend/commands/vacuumlazy.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/commands/vacuumlazy.c	2009-01-23 14:53:17.749343111 +0100
--- pgsql_spacereserve/src/backend/commands/vacuumlazy.c	2009-01-23 14:53:17.789497947 +0100
***************
*** 387,393 ****
  				PageInit(page, BufferGetPageSize(buf), 0);
  				empty_pages++;
  			}
! 			freespace = PageGetHeapFreeSpace(page);
  			MarkBufferDirty(buf);
  			UnlockReleaseBuffer(buf);
  
--- 387,395 ----
  				PageInit(page, BufferGetPageSize(buf), 0);
  				empty_pages++;
  			}
! 			freespace = PageGetHeapFreeSpace(page,
! 						 	 				 RelationGetReservedSpacePerPage(onerel),
! 						 	 				 RelationGetReservedSpacePerTuple(onerel));
  			MarkBufferDirty(buf);
  			UnlockReleaseBuffer(buf);
  
***************
*** 398,404 ****
  		if (PageIsEmpty(page))
  		{
  			empty_pages++;
! 			freespace = PageGetHeapFreeSpace(page);
  
  			if (!PageIsAllVisible(page))
  			{
--- 400,408 ----
  		if (PageIsEmpty(page))
  		{
  			empty_pages++;
! 			freespace = PageGetHeapFreeSpace(page,
! 						 	 				 RelationGetReservedSpacePerPage(onerel),
! 						 	 				 RelationGetReservedSpacePerTuple(onerel));
  
  			if (!PageIsAllVisible(page))
  			{
***************
*** 623,629 ****
  			vacuumed_pages++;
  		}
  
! 		freespace = PageGetHeapFreeSpace(page);
  
  		/* Update the all-visible flag on the page */
  		if (!PageIsAllVisible(page) && all_visible)
--- 627,635 ----
  			vacuumed_pages++;
  		}
  
! 		freespace = PageGetHeapFreeSpace(page,
! 						 	 			 RelationGetReservedSpacePerPage(onerel),
! 						 	 			 RelationGetReservedSpacePerTuple(onerel));
  
  		/* Update the all-visible flag on the page */
  		if (!PageIsAllVisible(page) && all_visible)
***************
*** 764,770 ****
  
  		/* Now that we've compacted the page, record its available space */
  		page = BufferGetPage(buf);
! 		freespace = PageGetHeapFreeSpace(page);
  
  		UnlockReleaseBuffer(buf);
  		RecordPageWithFreeSpace(onerel, tblk, freespace);
--- 770,778 ----
  
  		/* Now that we've compacted the page, record its available space */
  		page = BufferGetPage(buf);
! 		freespace = PageGetHeapFreeSpace(page,
! 						 	 			 RelationGetReservedSpacePerPage(onerel),
! 						 	 			 RelationGetReservedSpacePerTuple(onerel));
  
  		UnlockReleaseBuffer(buf);
  		RecordPageWithFreeSpace(onerel, tblk, freespace);
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/backend/storage/page/bufpage.c pgsql_spacereserve/src/backend/storage/page/bufpage.c
*** pgsql_spacereserve.7b2d095bfec6/src/backend/storage/page/bufpage.c	2009-01-23 14:53:17.759702299 +0100
--- pgsql_spacereserve/src/backend/storage/page/bufpage.c	2009-01-23 14:53:17.789881019 +0100
***************
*** 482,497 ****
   * PageGetHeapFreeSpace on heap pages.
   */
  Size
! PageGetFreeSpace(Page page)
  {
  	int			space;
  
  	/*
  	 * Use signed arithmetic here so that we behave sensibly if pd_lower >
  	 * pd_upper.
  	 */
  	space = (int) ((PageHeader) page)->pd_upper -
! 		(int) ((PageHeader) page)->pd_lower;
  
  	if (space < (int) sizeof(ItemIdData))
  		return 0;
--- 482,502 ----
   * PageGetHeapFreeSpace on heap pages.
   */
  Size
! PageGetFreeSpace(Page page, int rs_perpage, int rs_pertuple)
  {
  	int			space;
+ 	int			reserved_space;
+ 
+ 	/* Count reserved space. It is used for in-place upgrade. Because this functions
+ 	   is usually called before PageAddItem we need to count with new item */
+ 	reserved_space = rs_perpage+rs_pertuple*(PageGetMaxOffsetNumber(page)+1);
  
  	/*
  	 * Use signed arithmetic here so that we behave sensibly if pd_lower >
  	 * pd_upper.
  	 */
  	space = (int) ((PageHeader) page)->pd_upper -
! 		(int) ((PageHeader) page)->pd_lower - reserved_space;
  
  	if (space < (int) sizeof(ItemIdData))
  		return 0;
***************
*** 539,549 ****
   * on the number of line pointers, we make this extra check.)
   */
  Size
! PageGetHeapFreeSpace(Page page)
  {
  	Size		space;
  
! 	space = PageGetFreeSpace(page);
  	if (space > 0)
  	{
  		OffsetNumber offnum,
--- 544,554 ----
   * on the number of line pointers, we make this extra check.)
   */
  Size
! PageGetHeapFreeSpace(Page page, int rs_perpage, int rs_pertuple)
  {
  	Size		space;
  
! 	space = PageGetFreeSpace(page, rs_perpage, rs_pertuple);
  	if (space > 0)
  	{
  		OffsetNumber offnum,
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/include/access/gist_private.h pgsql_spacereserve/src/include/access/gist_private.h
*** pgsql_spacereserve.7b2d095bfec6/src/include/access/gist_private.h	2009-01-23 14:53:17.774314511 +0100
--- pgsql_spacereserve/src/include/access/gist_private.h	2009-01-23 14:53:17.790859139 +0100
***************
*** 279,285 ****
  
  extern Datum gistoptions(PG_FUNCTION_ARGS);
  extern bool gistfitpage(IndexTuple *itvec, int len);
! extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
  extern void gistcheckpage(Relation rel, Buffer buf);
  extern Buffer gistNewBuffer(Relation r);
  extern void gistfillbuffer(Page page, IndexTuple *itup, int len,
--- 279,285 ----
  
  extern Datum gistoptions(PG_FUNCTION_ARGS);
  extern bool gistfitpage(IndexTuple *itvec, int len);
! extern bool gistnospace(Relation rel, Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
  extern void gistcheckpage(Relation rel, Buffer buf);
  extern Buffer gistNewBuffer(Relation r);
  extern void gistfillbuffer(Page page, IndexTuple *itup, int len,
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/include/access/reloptions.h pgsql_spacereserve/src/include/access/reloptions.h
*** pgsql_spacereserve.7b2d095bfec6/src/include/access/reloptions.h	2009-01-23 14:53:17.774952860 +0100
--- pgsql_spacereserve/src/include/access/reloptions.h	2009-01-23 14:53:17.791013450 +0100
***************
*** 32,37 ****
--- 32,38 ----
  /* kinds supported by reloptions */
  typedef enum relopt_kind
  {
+ 	RELOPT_KIND_ALL,
  	RELOPT_KIND_HEAP,
  	/* XXX do we need a separate kind for TOAST tables? */
  	RELOPT_KIND_BTREE,
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/include/storage/bufpage.h pgsql_spacereserve/src/include/storage/bufpage.h
*** pgsql_spacereserve.7b2d095bfec6/src/include/storage/bufpage.h	2009-01-23 14:53:17.782874050 +0100
--- pgsql_spacereserve/src/include/storage/bufpage.h	2009-01-23 14:53:17.791850824 +0100
***************
*** 361,366 ****
--- 361,374 ----
  #define PageClearPrunable(page) \
  	(((PageHeader) (page))->pd_prune_xid = InvalidTransactionId)
  
+ /* PageGetMaxDataSpace
+  * It returns maximal possible amount of space for data on the page. If page has
+  * already item pointers we take them as a non-data space. The reason is that we
+  * cannot reclaim this space for data if it is marked as dead, beacause indexes
+  * can have a pointer on this item.
+  */
+ #define PageGetMaxDataSpace(page) \
+ 	( ((PageHeader) (page))->pd_special - MAXALIGN(((PageHeader) (page))->pd_lower) )
  
  /* ----------------------------------------------------------------
   *		extern declarations
***************
*** 376,384 ****
  extern Page PageGetTempPageCopySpecial(Page page);
  extern void PageRestoreTempPage(Page tempPage, Page oldPage);
  extern void PageRepairFragmentation(Page page);
! extern Size PageGetFreeSpace(Page page);
  extern Size PageGetExactFreeSpace(Page page);
! extern Size PageGetHeapFreeSpace(Page page);
  extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
  extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);
  
--- 384,392 ----
  extern Page PageGetTempPageCopySpecial(Page page);
  extern void PageRestoreTempPage(Page tempPage, Page oldPage);
  extern void PageRepairFragmentation(Page page);
! extern Size PageGetFreeSpace(Page page, int rs_perpage, int rs_pertuple);
  extern Size PageGetExactFreeSpace(Page page);
! extern Size PageGetHeapFreeSpace(Page page, int rs_perpage, int rs_pertuple);
  extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
  extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);
  
diff -Nrc pgsql_spacereserve.7b2d095bfec6/src/include/utils/rel.h pgsql_spacereserve/src/include/utils/rel.h
*** pgsql_spacereserve.7b2d095bfec6/src/include/utils/rel.h	2009-01-23 14:53:17.784845739 +0100
--- pgsql_spacereserve/src/include/utils/rel.h	2009-01-23 14:53:17.792111594 +0100
***************
*** 218,223 ****
--- 218,225 ----
  {
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
  	int			fillfactor;		/* page fill factor in percent (0..100) */
+ 	int			rs_perpage;  	/* page reserved space per page for in-place upgrade in bytes */
+ 	int			rs_pertuple;  	/* page reserved space per tuple for in-place upgrade in bytes */
  } StdRdOptions;
  
  #define HEAP_MIN_FILLFACTOR			10
***************
*** 232,237 ****
--- 234,255 ----
  	 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
  
  /*
+  * RelationGetReservedSpacePerPage
+  *		Returns the relation's reserved space per page.
+  */
+ #define RelationGetReservedSpacePerPage(relation) \
+ 	((relation)->rd_options ? \
+ 	 ((StdRdOptions *) (relation)->rd_options)->rs_perpage : 0)
+ 
+ /*
+  * RelationGetReservedSpacePerTuple
+  *		Returns the relation's reserved space per tuple.
+  */
+ #define RelationGetReservedSpacePerTuple(relation) \
+ 	((relation)->rd_options ? \
+ 	 ((StdRdOptions *) (relation)->rd_options)->rs_pertuple : 0)
+ 
+ /*
   * RelationGetTargetPageUsage
   *		Returns the relation's desired space usage per page in bytes.
   */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to