*** postgresql-9.2.2/src/backend/access/heap/heapam.c	2012-12-03 12:16:10.000000000 -0800
--- postgresql-9.2.2dg/src/backend/access/heap/heapam.c	2012-12-12 01:55:58.174653706 -0800
***************
*** 2158,2163 ****
--- 2158,2164 ----
  		Buffer		buffer;
  		Buffer		vmbuffer = InvalidBuffer;
  		bool		all_visible_cleared = false;
+ 		bool		page_is_empty;
  		int			nthispage;
  
  		/*
***************
*** 2173,2299 ****
  		START_CRIT_SECTION();
  
  		/* Put as many tuples as fit on this page */
  		for (nthispage = 0; ndone + nthispage < ntuples; nthispage++)
  		{
  			HeapTuple	heaptup = heaptuples[ndone + nthispage];
  
! 			if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
  				break;
! 
  			RelationPutHeapTuple(relation, buffer, heaptup);
  		}
  
- 		if (PageIsAllVisible(page))
- 		{
- 			all_visible_cleared = true;
- 			PageClearAllVisible(page);
- 			visibilitymap_clear(relation,
- 								BufferGetBlockNumber(buffer),
- 								vmbuffer);
- 		}
- 
  		/*
! 		 * XXX Should we set PageSetPrunable on this page ? See heap_insert()
  		 */
! 
! 		MarkBufferDirty(buffer);
! 
! 		/* XLOG stuff */
! 		if (needwal)
  		{
! 			XLogRecPtr	recptr;
! 			xl_heap_multi_insert *xlrec;
! 			XLogRecData rdata[2];
! 			uint8		info = XLOG_HEAP2_MULTI_INSERT;
! 			char	   *tupledata;
! 			int			totaldatalen;
! 			char	   *scratchptr = scratch;
! 			bool		init;
  
  			/*
! 			 * If the page was previously empty, we can reinit the page
! 			 * instead of restoring the whole thing.
  			 */
- 			init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- 					PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
  
! 			/* allocate xl_heap_multi_insert struct from the scratch area */
! 			xlrec = (xl_heap_multi_insert *) scratchptr;
! 			scratchptr += SizeOfHeapMultiInsert;
  
! 			/*
! 			 * Allocate offsets array. Unless we're reinitializing the page,
! 			 * in that case the tuples are stored in order starting at
! 			 * FirstOffsetNumber and we don't need to store the offsets
! 			 * explicitly.
! 			 */
! 			if (!init)
! 				scratchptr += nthispage * sizeof(OffsetNumber);
  
! 			/* the rest of the scratch space is used for tuple data */
! 			tupledata = scratchptr;
  
! 			xlrec->all_visible_cleared = all_visible_cleared;
! 			xlrec->node = relation->rd_node;
! 			xlrec->blkno = BufferGetBlockNumber(buffer);
! 			xlrec->ntuples = nthispage;
! 
! 			/*
! 			 * Write out an xl_multi_insert_tuple and the tuple data itself
! 			 * for each tuple.
! 			 */
! 			for (i = 0; i < nthispage; i++)
! 			{
! 				HeapTuple	heaptup = heaptuples[ndone + i];
! 				xl_multi_insert_tuple *tuphdr;
! 				int			datalen;
  
  				if (!init)
! 					xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
! 				/* xl_multi_insert_tuple needs two-byte alignment. */
! 				tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
! 				scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
! 
! 				tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
! 				tuphdr->t_infomask = heaptup->t_data->t_infomask;
! 				tuphdr->t_hoff = heaptup->t_data->t_hoff;
! 
! 				/* write bitmap [+ padding] [+ oid] + data */
! 				datalen = heaptup->t_len - offsetof(HeapTupleHeaderData, t_bits);
! 				memcpy(scratchptr,
! 					   (char *) heaptup->t_data + offsetof(HeapTupleHeaderData, t_bits),
! 					   datalen);
! 				tuphdr->datalen = datalen;
! 				scratchptr += datalen;
! 			}
! 			totaldatalen = scratchptr - tupledata;
! 			Assert((scratchptr - scratch) < BLCKSZ);
  
! 			rdata[0].data = (char *) xlrec;
! 			rdata[0].len = tupledata - scratch;
! 			rdata[0].buffer = InvalidBuffer;
! 			rdata[0].next = &rdata[1];
! 
! 			rdata[1].data = tupledata;
! 			rdata[1].len = totaldatalen;
! 			rdata[1].buffer = buffer;
! 			rdata[1].buffer_std = true;
! 			rdata[1].next = NULL;
  
! 			/*
! 			 * If we're going to reinitialize the whole page using the WAL
! 			 * record, hide buffer reference from XLogInsert.
! 			 */
! 			if (init)
! 			{
! 				rdata[1].buffer = InvalidBuffer;
! 				info |= XLOG_HEAP_INIT_PAGE;
! 			}
  
! 			recptr = XLogInsert(RM_HEAP2_ID, info, rdata);
  
! 			PageSetLSN(page, recptr);
! 			PageSetTLI(page, ThisTimeLineID);
  		}
  
  		END_CRIT_SECTION();
--- 2174,2309 ----
  		START_CRIT_SECTION();
  
  		/* Put as many tuples as fit on this page */
+ 		page_is_empty = PageGetMaxOffsetNumber(page) == 0;
  		for (nthispage = 0; ndone + nthispage < ntuples; nthispage++)
  		{
  			HeapTuple	heaptup = heaptuples[ndone + nthispage];
  
! 			if (PageGetHeapFreeSpace(page) <
! 			    MAXALIGN(heaptup->t_len) + (page_is_empty ? 0 : saveFreeSpace))
  				break;
! 			page_is_empty = false;
  			RelationPutHeapTuple(relation, buffer, heaptup);
  		}
  
  		/*
! 		 * If nthispage > 0 then we modified the (possibly new) page,
! 		 * otherwise there was not enough space for even one new tuple.
  		 */
! 		if (nthispage > 0)
  		{
! 			if (PageIsAllVisible(page))
! 			{
! 				all_visible_cleared = true;
! 				PageClearAllVisible(page);
! 				visibilitymap_clear(relation,
! 									BufferGetBlockNumber(buffer),
! 									vmbuffer);
! 			}
  
  			/*
! 			 * XXX Should we set PageSetPrunable on this page ? See heap_insert()
  			 */
  
! 			MarkBufferDirty(buffer);
  
! 			/* XLOG stuff */
! 			if (needwal)
! 			{
! 				XLogRecPtr	recptr;
! 				xl_heap_multi_insert *xlrec;
! 				XLogRecData rdata[2];
! 				uint8		info = XLOG_HEAP2_MULTI_INSERT;
! 				char	   *tupledata;
! 				int			totaldatalen;
! 				char	   *scratchptr = scratch;
! 				bool		init;
  
! 				/*
! 				 * If the page was previously empty, we can reinit the page
! 				 * instead of restoring the whole thing.
! 				 */
! 				init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
! 						PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
  
! 				/* allocate xl_heap_multi_insert struct from the scratch area */
! 				xlrec = (xl_heap_multi_insert *) scratchptr;
! 				scratchptr += SizeOfHeapMultiInsert;
  
+ 				/*
+ 				 * Allocate offsets array. Unless we're reinitializing the page,
+ 				 * in that case the tuples are stored in order starting at
+ 				 * FirstOffsetNumber and we don't need to store the offsets
+ 				 * explicitly.
+ 				 */
  				if (!init)
! 					scratchptr += nthispage * sizeof(OffsetNumber);
  
! 				/* the rest of the scratch space is used for tuple data */
! 				tupledata = scratchptr;
  
! 				xlrec->all_visible_cleared = all_visible_cleared;
! 				xlrec->node = relation->rd_node;
! 				xlrec->blkno = BufferGetBlockNumber(buffer);
! 				xlrec->ntuples = nthispage;
  
! 				/*
! 				 * Write out an xl_multi_insert_tuple and the tuple data itself
! 				 * for each tuple.
! 				 */
! 				for (i = 0; i < nthispage; i++)
! 				{
! 					HeapTuple	heaptup = heaptuples[ndone + i];
! 					xl_multi_insert_tuple *tuphdr;
! 					int			datalen;
! 
! 					if (!init)
! 						xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
! 					/* xl_multi_insert_tuple needs two-byte alignment. */
! 					tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
! 					scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
! 
! 					tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
! 					tuphdr->t_infomask = heaptup->t_data->t_infomask;
! 					tuphdr->t_hoff = heaptup->t_data->t_hoff;
! 
! 					/* write bitmap [+ padding] [+ oid] + data */
! 					datalen = heaptup->t_len - offsetof(HeapTupleHeaderData, t_bits);
! 					memcpy(scratchptr,
! 						   (char *) heaptup->t_data + offsetof(HeapTupleHeaderData, t_bits),
! 						   datalen);
! 					tuphdr->datalen = datalen;
! 					scratchptr += datalen;
! 				}
! 				totaldatalen = scratchptr - tupledata;
! 				Assert((scratchptr - scratch) < BLCKSZ);
! 
! 				rdata[0].data = (char *) xlrec;
! 				rdata[0].len = tupledata - scratch;
! 				rdata[0].buffer = InvalidBuffer;
! 				rdata[0].next = &rdata[1];
! 
! 				rdata[1].data = tupledata;
! 				rdata[1].len = totaldatalen;
! 				rdata[1].buffer = buffer;
! 				rdata[1].buffer_std = true;
! 				rdata[1].next = NULL;
! 
! 				/*
! 				 * If we're going to reinitialize the whole page using the WAL
! 				 * record, hide buffer reference from XLogInsert.
! 				 */
! 				if (init)
! 				{
! 					rdata[1].buffer = InvalidBuffer;
! 					info |= XLOG_HEAP_INIT_PAGE;
! 				}
! 
! 				recptr = XLogInsert(RM_HEAP2_ID, info, rdata);
  
! 				PageSetLSN(page, recptr);
! 				PageSetTLI(page, ThisTimeLineID);
! 			}
  		}
  
  		END_CRIT_SECTION();
***************
*** 2304,2310 ****
  
  		ndone += nthispage;
  	}
- 
  	/*
  	 * If tuples are cachable, mark them for invalidation from the caches in
  	 * case we abort.  Note it is OK to do this after releasing the buffer,
--- 2314,2319 ----
