> Can you test whether using the buffer access strategy is a win or a
> loss? Most of that gain is probably coming from the reduction in
> pinning.

Patch resnapped to HEAD, with straightforward adjustments to
compensate for Heikki's changes to the ReadBuffer interface.  See
attached.

New testing results, now with and without BAS:

--TRUNK--
Time: 17945.523 ms
Time: 18682.172 ms
Time: 17047.841 ms
Time: 16344.442 ms
Time: 18727.417 ms

--PATCHED--
Time: 13323.772 ms
Time: 13869.724 ms
Time: 14043.666 ms
Time: 13934.132 ms
Time: 13193.702 ms

--PATCHED with BAS disabled--
Time: 14460.432 ms
Time: 14745.206 ms
Time: 14345.973 ms
Time: 14601.448 ms
Time: 16535.167 ms

I'm not sure why the BAS seemed to be slowing things down before.
Maybe it's different if we're copying into a pre-existing table, so
that WAL is enabled?  Or it could have just been a fluke - the numbers
were close.  I'll try to run some additional tests if time permits.

...Robert
Index: src/backend/access/heap/heapam.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/heapam.c,v
retrieving revision 1.268
diff -c -r1.268 heapam.c
*** src/backend/access/heap/heapam.c	31 Oct 2008 19:40:26 -0000	1.268
--- src/backend/access/heap/heapam.c	1 Nov 2008 17:17:16 -0000
***************
*** 1798,1803 ****
--- 1798,1827 ----
  	}
  }
  
+ /*
+  * GetBulkInsertState - set up for a bulk insert
+  */
+ BulkInsertState
+ GetBulkInsertState(void)
+ {
+ 	BulkInsertState bistate;
+ 
+ 	bistate = palloc(sizeof(struct BulkInsertStateData));
+ 	bistate->strategy = GetAccessStrategy(BAS_BULKWRITE);
+ 	bistate->last_pin = InvalidBuffer;
+ 	return bistate;
+ }
+ 
+ /*
+  * FreeBulkInsertState - clean up after finishing a bulk insert
+  */
+ void
+ FreeBulkInsertState(BulkInsertState bistate)
+ {
+ 	if (bistate->last_pin != InvalidBuffer)
+ 		ReleaseBuffer(bistate->last_pin);		
+ 	FreeAccessStrategy(bistate->strategy);
+ }
  
  /*
   *	heap_insert		- insert tuple into a heap
***************
*** 1805,1821 ****
   * The new tuple is stamped with current transaction ID and the specified
   * command ID.
   *
!  * If use_wal is false, the new tuple is not logged in WAL, even for a
!  * non-temp relation.  Safe usage of this behavior requires that we arrange
!  * that all new tuples go into new pages not containing any tuples from other
!  * transactions, and that the relation gets fsync'd before commit.
   * (See also heap_sync() comments)
   *
!  * use_fsm is passed directly to RelationGetBufferForTuple, which see for
!  * more info.
   *
!  * Note that use_wal and use_fsm will be applied when inserting into the
!  * heap's TOAST table, too, if the tuple requires any out-of-line data.
   *
   * The return value is the OID assigned to the tuple (either here or by the
   * caller), or InvalidOid if no OID.  The header fields of *tup are updated
--- 1829,1846 ----
   * The new tuple is stamped with current transaction ID and the specified
   * command ID.
   *
!  * If the HEAP_INSERT_SKIP_WAL option is supplied, the new tuple is not logged
!  * in WAL, even for a non-temp relation.  Safe usage of this behavior requires
!  * that we arrange that all new tuples go into new pages not containing any
!  * tuples from other transactions, and that the relation gets fsync'd before
!  * commit.
   * (See also heap_sync() comments)
   *
!  * The HEAP_INSERT_SKIP_FSM option is passed directly to
!  * RelationGetBufferForTuple, which see for more info.
   *
!  * Note that options will be applied when inserting into the heap's TOAST
!  * table, too, if the tuple requires any out-of-line data.
   *
   * The return value is the OID assigned to the tuple (either here or by the
   * caller), or InvalidOid if no OID.  The header fields of *tup are updated
***************
*** 1825,1831 ****
   */
  Oid
  heap_insert(Relation relation, HeapTuple tup, CommandId cid,
! 			bool use_wal, bool use_fsm)
  {
  	TransactionId xid = GetCurrentTransactionId();
  	HeapTuple	heaptup;
--- 1850,1856 ----
   */
  Oid
  heap_insert(Relation relation, HeapTuple tup, CommandId cid,
! 			int options, BulkInsertState bistate)
  {
  	TransactionId xid = GetCurrentTransactionId();
  	HeapTuple	heaptup;
***************
*** 1877,1890 ****
  		heaptup = tup;
  	}
  	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
! 		heaptup = toast_insert_or_update(relation, tup, NULL,
! 										 use_wal, use_fsm);
  	else
  		heaptup = tup;
  
  	/* Find buffer to insert this tuple into */
  	buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
! 									   InvalidBuffer, use_fsm);
  
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
--- 1902,1914 ----
  		heaptup = tup;
  	}
  	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
! 		heaptup = toast_insert_or_update(relation, tup, NULL, options);
  	else
  		heaptup = tup;
  
  	/* Find buffer to insert this tuple into */
  	buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
! 									   InvalidBuffer, options, bistate);
  
  	/* NO EREPORT(ERROR) from here till changes are logged */
  	START_CRIT_SECTION();
***************
*** 1905,1911 ****
  	MarkBufferDirty(buffer);
  
  	/* XLOG stuff */
! 	if (use_wal && !relation->rd_istemp)
  	{
  		xl_heap_insert xlrec;
  		xl_heap_header xlhdr;
--- 1929,1935 ----
  	MarkBufferDirty(buffer);
  
  	/* XLOG stuff */
! 	if ((options & HEAP_INSERT_SKIP_WAL) == 0 && !relation->rd_istemp)
  	{
  		xl_heap_insert xlrec;
  		xl_heap_header xlhdr;
***************
*** 1963,1969 ****
  
  	END_CRIT_SECTION();
  
! 	UnlockReleaseBuffer(buffer);
  
  	/*
  	 * If tuple is cachable, mark it for invalidation from the caches in case
--- 1987,1996 ----
  
  	END_CRIT_SECTION();
  
! 	/* Release the lock, but keep the buffer pinned if doing bulk insert. */
! 	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! 	if (!bistate)
! 		ReleaseBuffer(buffer);
  
  	/*
  	 * If tuple is cachable, mark it for invalidation from the caches in case
***************
*** 2000,2006 ****
  Oid
  simple_heap_insert(Relation relation, HeapTuple tup)
  {
! 	return heap_insert(relation, tup, GetCurrentCommandId(true), true, true);
  }
  
  /*
--- 2027,2033 ----
  Oid
  simple_heap_insert(Relation relation, HeapTuple tup)
  {
! 	return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
  }
  
  /*
***************
*** 2595,2602 ****
  		if (need_toast)
  		{
  			/* Note we always use WAL and FSM during updates */
! 			heaptup = toast_insert_or_update(relation, newtup, &oldtup,
! 											 true, true);
  			newtupsize = MAXALIGN(heaptup->t_len);
  		}
  		else
--- 2622,2628 ----
  		if (need_toast)
  		{
  			/* Note we always use WAL and FSM during updates */
! 			heaptup = toast_insert_or_update(relation, newtup, &oldtup, 0);
  			newtupsize = MAXALIGN(heaptup->t_len);
  		}
  		else
***************
*** 2623,2629 ****
  		{
  			/* Assume there's no chance to put heaptup on same page. */
  			newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
! 											   buffer, true);
  		}
  		else
  		{
--- 2649,2655 ----
  		{
  			/* Assume there's no chance to put heaptup on same page. */
  			newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
! 											   buffer, 0, NULL);
  		}
  		else
  		{
***************
*** 2640,2646 ****
  				 */
  				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  				newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
! 												   buffer, true);
  			}
  			else
  			{
--- 2666,2672 ----
  				 */
  				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  				newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
! 												   buffer, 0, NULL);
  			}
  			else
  			{
Index: src/backend/access/heap/hio.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/hio.c,v
retrieving revision 1.73
diff -c -r1.73 hio.c
*** src/backend/access/heap/hio.c	30 Sep 2008 10:52:10 -0000	1.73
--- src/backend/access/heap/hio.c	1 Nov 2008 17:17:16 -0000
***************
*** 15,20 ****
--- 15,21 ----
  
  #include "postgres.h"
  
+ #include "access/heapam.h"
  #include "access/hio.h"
  #include "storage/bufmgr.h"
  #include "storage/freespace.h"
***************
*** 80,92 ****
   *	happen if space is freed in that page after heap_update finds there's not
   *	enough there).	In that case, the page will be pinned and locked only once.
   *
!  *	If use_fsm is true (the normal case), we use FSM to help us find free
!  *	space.	If use_fsm is false, we always append a new empty page to the
!  *	end of the relation if the tuple won't fit on the current target page.
   *	This can save some cycles when we know the relation is new and doesn't
   *	contain useful amounts of free space.
   *
!  *	The use_fsm = false case is also useful for non-WAL-logged additions to a
   *	relation, if the caller holds exclusive lock and is careful to invalidate
   *	relation->rd_targblock before the first insertion --- that ensures that
   *	all insertions will occur into newly added pages and not be intermixed
--- 81,93 ----
   *	happen if space is freed in that page after heap_update finds there's not
   *	enough there).	In that case, the page will be pinned and locked only once.
   *
!  *	We normally use FSM to help us find free space.	 However,
!  *	if HEAP_INSERT_SKIP_FSM is specified, we just append a new empty page to
!  *	the end of the relation if the tuple won't fit on the current target page.
   *	This can save some cycles when we know the relation is new and doesn't
   *	contain useful amounts of free space.
   *
!  *	HEAP_INSERT_SKIP_FSM is also useful for non-WAL-logged additions to a
   *	relation, if the caller holds exclusive lock and is careful to invalidate
   *	relation->rd_targblock before the first insertion --- that ensures that
   *	all insertions will occur into newly added pages and not be intermixed
***************
*** 104,110 ****
   */
  Buffer
  RelationGetBufferForTuple(Relation relation, Size len,
! 						  Buffer otherBuffer, bool use_fsm)
  {
  	Buffer		buffer = InvalidBuffer;
  	Page		page;
--- 105,112 ----
   */
  Buffer
  RelationGetBufferForTuple(Relation relation, Size len,
! 						  Buffer otherBuffer, int options,
! 						  BulkInsertState bistate)
  {
  	Buffer		buffer = InvalidBuffer;
  	Page		page;
***************
*** 113,121 ****
--- 115,127 ----
  	BlockNumber targetBlock,
  				otherBlock;
  	bool		needLock;
+ 	bool		use_fsm = (options & HEAP_INSERT_SKIP_FSM) == 0;
  
  	len = MAXALIGN(len);		/* be conservative */
  
+ 	/* Bulk insert is not supported for updates, only inserts. */
+ 	Assert(!bistate || otherBuffer == InvalidBuffer);
+ 
  	/*
  	 * If we're gonna fail for oversize tuple, do it right away
  	 */
***************
*** 186,192 ****
  		 * block if one was given, taking suitable care with lock ordering and
  		 * the possibility they are the same block.
  		 */
! 		if (otherBuffer == InvalidBuffer)
  		{
  			/* easy case */
  			buffer = ReadBuffer(relation, targetBlock);
--- 192,212 ----
  		 * block if one was given, taking suitable care with lock ordering and
  		 * the possibility they are the same block.
  		 */
! 		if (bistate)
! 		{
! 			if (bistate->last_pin == InvalidBuffer)
! 				bistate->last_pin = ReadBufferExtended(relation,
! 					MAIN_FORKNUM, targetBlock, RBM_NORMAL, bistate->strategy);
! 			else if (BufferGetBlockNumber(bistate->last_pin) != targetBlock)
! 			{
! 				ReleaseBuffer(bistate->last_pin);
! 				bistate->last_pin = ReadBufferExtended(relation,
! 					MAIN_FORKNUM, targetBlock, RBM_NORMAL, bistate->strategy);
! 			}
! 			buffer = bistate->last_pin;
! 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
! 		}
! 		else if (otherBuffer == InvalidBuffer)
  		{
  			/* easy case */
  			buffer = ReadBuffer(relation, targetBlock);
***************
*** 240,245 ****
--- 260,267 ----
  			LockBuffer(otherBuffer, BUFFER_LOCK_UNLOCK);
  			ReleaseBuffer(buffer);
  		}
+ 		if (bistate)
+ 			bistate->last_pin = InvalidBuffer;
  
  		/* Without FSM, always fall out of the loop and extend */
  		if (!use_fsm)
***************
*** 274,280 ****
  	 * it worth keeping an accurate file length in shared memory someplace,
  	 * rather than relying on the kernel to do it for us?
  	 */
! 	buffer = ReadBuffer(relation, P_NEW);
  
  	/*
  	 * We can be certain that locking the otherBuffer first is OK, since it
--- 296,311 ----
  	 * it worth keeping an accurate file length in shared memory someplace,
  	 * rather than relying on the kernel to do it for us?
  	 */
! 	if (bistate)
! 	{
! 		if (bistate->last_pin != InvalidBuffer)
! 			ReleaseBuffer(bistate->last_pin);
! 		buffer = ReadBufferExtended(relation, MAIN_FORKNUM, P_NEW,
! 			RBM_NORMAL, bistate->strategy);
! 		bistate->last_pin = buffer;
! 	}
! 	else
! 		buffer = ReadBuffer(relation, P_NEW);
  
  	/*
  	 * We can be certain that locking the otherBuffer first is OK, since it
Index: src/backend/access/heap/rewriteheap.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/rewriteheap.c,v
retrieving revision 1.15
diff -c -r1.15 rewriteheap.c
*** src/backend/access/heap/rewriteheap.c	11 Aug 2008 11:05:10 -0000	1.15
--- src/backend/access/heap/rewriteheap.c	1 Nov 2008 17:17:17 -0000
***************
*** 575,581 ****
  	}
  	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
  		heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL,
! 										 state->rs_use_wal, false);
  	else
  		heaptup = tup;
  
--- 575,583 ----
  	}
  	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
  		heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL,
! 										 HEAP_INSERT_SKIP_FSM |
! 										 (state->rs_use_wal ?
! 									     0 : HEAP_INSERT_SKIP_WAL));
  	else
  		heaptup = tup;
  
Index: src/backend/access/heap/tuptoaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v
retrieving revision 1.89
diff -c -r1.89 tuptoaster.c
*** src/backend/access/heap/tuptoaster.c	19 Jun 2008 00:46:03 -0000	1.89
--- src/backend/access/heap/tuptoaster.c	1 Nov 2008 17:17:18 -0000
***************
*** 74,81 ****
  
  
  static void toast_delete_datum(Relation rel, Datum value);
! static Datum toast_save_datum(Relation rel, Datum value,
! 				 bool use_wal, bool use_fsm);
  static struct varlena *toast_fetch_datum(struct varlena * attr);
  static struct varlena *toast_fetch_datum_slice(struct varlena * attr,
  						int32 sliceoffset, int32 length);
--- 74,80 ----
  
  
  static void toast_delete_datum(Relation rel, Datum value);
! static Datum toast_save_datum(Relation rel, Datum value, int options);
  static struct varlena *toast_fetch_datum(struct varlena * attr);
  static struct varlena *toast_fetch_datum_slice(struct varlena * attr,
  						int32 sliceoffset, int32 length);
***************
*** 400,406 ****
   * Inputs:
   *	newtup: the candidate new tuple to be inserted
   *	oldtup: the old row version for UPDATE, or NULL for INSERT
!  *	use_wal, use_fsm: flags to be passed to heap_insert() for toast rows
   * Result:
   *	either newtup if no toasting is needed, or a palloc'd modified tuple
   *	that is what should actually get stored
--- 399,405 ----
   * Inputs:
   *	newtup: the candidate new tuple to be inserted
   *	oldtup: the old row version for UPDATE, or NULL for INSERT
!  *	options: options to be passed to heap_insert() for toast rows
   * Result:
   *	either newtup if no toasting is needed, or a palloc'd modified tuple
   *	that is what should actually get stored
***************
*** 411,417 ****
   */
  HeapTuple
  toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
! 					   bool use_wal, bool use_fsm)
  {
  	HeapTuple	result_tuple;
  	TupleDesc	tupleDesc;
--- 410,416 ----
   */
  HeapTuple
  toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
! 					   int options)
  {
  	HeapTuple	result_tuple;
  	TupleDesc	tupleDesc;
***************
*** 677,684 ****
  		{
  			old_value = toast_values[i];
  			toast_action[i] = 'p';
! 			toast_values[i] = toast_save_datum(rel, toast_values[i],
! 											   use_wal, use_fsm);
  			if (toast_free[i])
  				pfree(DatumGetPointer(old_value));
  			toast_free[i] = true;
--- 676,682 ----
  		{
  			old_value = toast_values[i];
  			toast_action[i] = 'p';
! 			toast_values[i] = toast_save_datum(rel, toast_values[i], options);
  			if (toast_free[i])
  				pfree(DatumGetPointer(old_value));
  			toast_free[i] = true;
***************
*** 728,735 ****
  		i = biggest_attno;
  		old_value = toast_values[i];
  		toast_action[i] = 'p';
! 		toast_values[i] = toast_save_datum(rel, toast_values[i],
! 										   use_wal, use_fsm);
  		if (toast_free[i])
  			pfree(DatumGetPointer(old_value));
  		toast_free[i] = true;
--- 726,732 ----
  		i = biggest_attno;
  		old_value = toast_values[i];
  		toast_action[i] = 'p';
! 		toast_values[i] = toast_save_datum(rel, toast_values[i], options);
  		if (toast_free[i])
  			pfree(DatumGetPointer(old_value));
  		toast_free[i] = true;
***************
*** 838,845 ****
  		i = biggest_attno;
  		old_value = toast_values[i];
  		toast_action[i] = 'p';
! 		toast_values[i] = toast_save_datum(rel, toast_values[i],
! 										   use_wal, use_fsm);
  		if (toast_free[i])
  			pfree(DatumGetPointer(old_value));
  		toast_free[i] = true;
--- 835,841 ----
  		i = biggest_attno;
  		old_value = toast_values[i];
  		toast_action[i] = 'p';
! 		toast_values[i] = toast_save_datum(rel, toast_values[i], options);
  		if (toast_free[i])
  			pfree(DatumGetPointer(old_value));
  		toast_free[i] = true;
***************
*** 1120,1127 ****
   * ----------
   */
  static Datum
! toast_save_datum(Relation rel, Datum value,
! 				 bool use_wal, bool use_fsm)
  {
  	Relation	toastrel;
  	Relation	toastidx;
--- 1116,1122 ----
   * ----------
   */
  static Datum
! toast_save_datum(Relation rel, Datum value, int options)
  {
  	Relation	toastrel;
  	Relation	toastidx;
***************
*** 1218,1224 ****
  		memcpy(VARDATA(&chunk_data), data_p, chunk_size);
  		toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
  
! 		heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm);
  
  		/*
  		 * Create the index entry.	We cheat a little here by not using
--- 1213,1219 ----
  		memcpy(VARDATA(&chunk_data), data_p, chunk_size);
  		toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
  
! 		heap_insert(toastrel, toasttup, mycid, options, NULL);
  
  		/*
  		 * Create the index entry.	We cheat a little here by not using
Index: src/backend/commands/copy.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.299
diff -c -r1.299 copy.c
*** src/backend/commands/copy.c	12 May 2008 20:01:59 -0000	1.299
--- src/backend/commands/copy.c	1 Nov 2008 17:17:20 -0000
***************
*** 1653,1660 ****
  	MemoryContext oldcontext = CurrentMemoryContext;
  	ErrorContextCallback errcontext;
  	CommandId	mycid = GetCurrentCommandId(true);
! 	bool		use_wal = true; /* by default, use WAL logging */
! 	bool		use_fsm = true; /* by default, use FSM for free space */
  
  	Assert(cstate->rel);
  
--- 1653,1660 ----
  	MemoryContext oldcontext = CurrentMemoryContext;
  	ErrorContextCallback errcontext;
  	CommandId	mycid = GetCurrentCommandId(true);
! 	int options = 0;
! 	BulkInsertState bistate;
  
  	Assert(cstate->rel);
  
***************
*** 1707,1715 ****
  	if (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
  		cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId)
  	{
! 		use_fsm = false;
  		if (!XLogArchivingActive())
! 			use_wal = false;
  	}
  
  	if (pipe)
--- 1707,1715 ----
  	if (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
  		cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId)
  	{
! 		options |= HEAP_INSERT_SKIP_FSM;
  		if (!XLogArchivingActive())
! 			options |= HEAP_INSERT_SKIP_WAL;
  	}
  
  	if (pipe)
***************
*** 1886,1891 ****
--- 1886,1893 ----
  	cstate->cur_attname = NULL;
  	cstate->cur_attval = NULL;
  
+ 	bistate = GetBulkInsertState();
+ 
  	/* Set up callback to identify error line number */
  	errcontext.callback = copy_in_error_callback;
  	errcontext.arg = (void *) cstate;
***************
*** 2111,2117 ****
  				ExecConstraints(resultRelInfo, slot, estate);
  
  			/* OK, store the tuple and create index entries for it */
! 			heap_insert(cstate->rel, tuple, mycid, use_wal, use_fsm);
  
  			if (resultRelInfo->ri_NumIndices > 0)
  				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
--- 2113,2119 ----
  				ExecConstraints(resultRelInfo, slot, estate);
  
  			/* OK, store the tuple and create index entries for it */
! 			heap_insert(cstate->rel, tuple, mycid, options, bistate);
  
  			if (resultRelInfo->ri_NumIndices > 0)
  				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
***************
*** 2129,2134 ****
--- 2131,2137 ----
  	}
  
  	/* Done, clean up */
+ 	FreeBulkInsertState(bistate);
  	error_context_stack = errcontext.previous;
  
  	MemoryContextSwitchTo(oldcontext);
***************
*** 2167,2173 ****
  	 * If we skipped writing WAL, then we need to sync the heap (but not
  	 * indexes since those use WAL anyway)
  	 */
! 	if (!use_wal)
  		heap_sync(cstate->rel);
  }
  
--- 2170,2176 ----
  	 * If we skipped writing WAL, then we need to sync the heap (but not
  	 * indexes since those use WAL anyway)
  	 */
! 	if ((options & HEAP_INSERT_SKIP_WAL) != 0)
  		heap_sync(cstate->rel);
  }
  
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.314
diff -c -r1.314 execMain.c
*** src/backend/executor/execMain.c	31 Oct 2008 21:07:54 -0000	1.314
--- src/backend/executor/execMain.c	1 Nov 2008 17:17:22 -0000
***************
*** 1623,1630 ****
  	 * t_self field.
  	 */
  	newId = heap_insert(resultRelationDesc, tuple,
! 						estate->es_output_cid,
! 						true, true);
  
  	IncrAppended();
  	(estate->es_processed)++;
--- 1623,1629 ----
  	 * t_self field.
  	 */
  	newId = heap_insert(resultRelationDesc, tuple,
! 						estate->es_output_cid, 0, NULL);
  
  	IncrAppended();
  	(estate->es_processed)++;
***************
*** 2621,2627 ****
  	DestReceiver pub;			/* publicly-known function pointers */
  	EState	   *estate;			/* EState we are working with */
  	Relation	rel;			/* Relation to write to */
! 	bool		use_wal;		/* do we need to WAL-log our writes? */
  } DR_intorel;
  
  /*
--- 2620,2627 ----
  	DestReceiver pub;			/* publicly-known function pointers */
  	EState	   *estate;			/* EState we are working with */
  	Relation	rel;			/* Relation to write to */
! 	int			hi_options;		/* heap_insert performance options */
! 	BulkInsertState bistate;	/* bulk insert state */
  } DR_intorel;
  
  /*
***************
*** 2753,2763 ****
  	myState = (DR_intorel *) queryDesc->dest;
  	Assert(myState->pub.mydest == DestIntoRel);
  	myState->estate = estate;
  
  	/*
! 	 * We can skip WAL-logging the insertions, unless PITR is in use.
  	 */
! 	myState->use_wal = XLogArchivingActive();
  	myState->rel = intoRelationDesc;
  
  	/* use_wal off requires rd_targblock be initially invalid */
--- 2753,2766 ----
  	myState = (DR_intorel *) queryDesc->dest;
  	Assert(myState->pub.mydest == DestIntoRel);
  	myState->estate = estate;
+ 	myState->bistate = GetBulkInsertState();
  
  	/*
! 	 * We can skip WAL-logging the insertions, unless PITR is in use.  We
! 	 * can skip the FSM in any case.
  	 */
! 	myState->hi_options = HEAP_INSERT_SKIP_FSM |
! 		(XLogArchivingActive() ? 0 : HEAP_INSERT_SKIP_WAL);
  	myState->rel = intoRelationDesc;
  
  	/* use_wal off requires rd_targblock be initially invalid */
***************
*** 2775,2782 ****
  	/* OpenIntoRel might never have gotten called */
  	if (myState && myState->pub.mydest == DestIntoRel && myState->rel)
  	{
  		/* If we skipped using WAL, must heap_sync before commit */
! 		if (!myState->use_wal)
  			heap_sync(myState->rel);
  
  		/* close rel, but keep lock until commit */
--- 2778,2787 ----
  	/* OpenIntoRel might never have gotten called */
  	if (myState && myState->pub.mydest == DestIntoRel && myState->rel)
  	{
+ 		FreeBulkInsertState(myState->bistate);
+ 
  		/* If we skipped using WAL, must heap_sync before commit */
! 		if ((myState->hi_options & HEAP_INSERT_SKIP_WAL) != 0)
  			heap_sync(myState->rel);
  
  		/* close rel, but keep lock until commit */
***************
*** 2834,2841 ****
  	heap_insert(myState->rel,
  				tuple,
  				myState->estate->es_output_cid,
! 				myState->use_wal,
! 				false);			/* never any point in using FSM */
  
  	/* We know this is a newly created relation, so there are no indexes */
  
--- 2839,2846 ----
  	heap_insert(myState->rel,
  				tuple,
  				myState->estate->es_output_cid,
! 				myState->hi_options,
! 				myState->bistate);
  
  	/* We know this is a newly created relation, so there are no indexes */
  
Index: src/backend/storage/buffer/README
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/storage/buffer/README,v
retrieving revision 1.14
diff -c -r1.14 README
*** src/backend/storage/buffer/README	21 Mar 2008 13:23:28 -0000	1.14
--- src/backend/storage/buffer/README	1 Nov 2008 17:17:23 -0000
***************
*** 235,240 ****
--- 235,242 ----
  buffer, resulting in excessive WAL flushing.  Allowing VACUUM to update
  256KB between WAL flushes should be more efficient.
  
+ Beginning in 8.4, COPY IN and CREATE TABLE AS SELECT also uses a ring buffer,
+ in order to avoid trashing the entire buffer arena.
  
  Background Writer's Processing
  ------------------------------
Index: src/backend/storage/buffer/freelist.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/storage/buffer/freelist.c,v
retrieving revision 1.64
diff -c -r1.64 freelist.c
*** src/backend/storage/buffer/freelist.c	1 Jan 2008 19:45:51 -0000	1.64
--- src/backend/storage/buffer/freelist.c	1 Nov 2008 17:17:23 -0000
***************
*** 387,392 ****
--- 387,395 ----
  		case BAS_VACUUM:
  			ring_size = 256 * 1024 / BLCKSZ;
  			break;
+ 		case BAS_BULKWRITE:
+ 			ring_size = 256 * 1024 / BLCKSZ;
+ 			break;
  
  		default:
  			elog(ERROR, "unrecognized buffer access strategy: %d",
Index: src/include/access/heapam.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/heapam.h,v
retrieving revision 1.139
diff -c -r1.139 heapam.h
*** src/include/access/heapam.h	8 Oct 2008 01:14:44 -0000	1.139
--- src/include/access/heapam.h	1 Nov 2008 17:17:24 -0000
***************
*** 31,36 ****
--- 31,37 ----
  	LockTupleExclusive
  } LockTupleMode;
  
+ typedef struct BulkInsertStateData *BulkInsertState;
  
  /* ----------------
   *		function prototypes for heap access method
***************
*** 86,93 ****
  					ItemPointer tid);
  extern void setLastTid(const ItemPointer tid);
  
  extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
! 			bool use_wal, bool use_fsm);
  extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
  			CommandId cid, Snapshot crosscheck, bool wait);
--- 87,99 ----
  					ItemPointer tid);
  extern void setLastTid(const ItemPointer tid);
  
+ #define HEAP_INSERT_SKIP_WAL   0x0001
+ #define HEAP_INSERT_SKIP_FSM   0x0002
+ 
  extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
! 			int options, BulkInsertState bistate);
! BulkInsertState GetBulkInsertState(void);
! void FreeBulkInsertState(BulkInsertState);
  extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
  			CommandId cid, Snapshot crosscheck, bool wait);
Index: src/include/access/hio.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/hio.h,v
retrieving revision 1.36
diff -c -r1.36 hio.h
*** src/include/access/hio.h	19 Jun 2008 00:46:06 -0000	1.36
--- src/include/access/hio.h	1 Nov 2008 17:17:24 -0000
***************
*** 18,26 ****
  #include "utils/relcache.h"
  #include "storage/buf.h"
  
  extern void RelationPutHeapTuple(Relation relation, Buffer buffer,
  					 HeapTuple tuple);
  extern Buffer RelationGetBufferForTuple(Relation relation, Size len,
! 						  Buffer otherBuffer, bool use_fsm);
  
  #endif   /* HIO_H */
--- 18,33 ----
  #include "utils/relcache.h"
  #include "storage/buf.h"
  
+ /* private to access/heap/heapam.c and access/heap/hio.c */
+ struct BulkInsertStateData {
+ 	BufferAccessStrategy strategy;
+ 	Buffer last_pin;
+ };
+ 
  extern void RelationPutHeapTuple(Relation relation, Buffer buffer,
  					 HeapTuple tuple);
  extern Buffer RelationGetBufferForTuple(Relation relation, Size len,
! 						  Buffer otherBuffer, int options,
! 						  BulkInsertState bistate);
  
  #endif   /* HIO_H */
Index: src/include/access/tuptoaster.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/tuptoaster.h,v
retrieving revision 1.41
diff -c -r1.41 tuptoaster.h
*** src/include/access/tuptoaster.h	13 Jul 2008 20:45:47 -0000	1.41
--- src/include/access/tuptoaster.h	1 Nov 2008 17:17:24 -0000
***************
*** 93,99 ****
   */
  extern HeapTuple toast_insert_or_update(Relation rel,
  					   HeapTuple newtup, HeapTuple oldtup,
! 					   bool use_wal, bool use_fsm);
  
  /* ----------
   * toast_delete -
--- 93,99 ----
   */
  extern HeapTuple toast_insert_or_update(Relation rel,
  					   HeapTuple newtup, HeapTuple oldtup,
! 					   int options);
  
  /* ----------
   * toast_delete -
Index: src/include/storage/bufmgr.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/storage/bufmgr.h,v
retrieving revision 1.116
diff -c -r1.116 bufmgr.h
*** src/include/storage/bufmgr.h	31 Oct 2008 15:05:00 -0000	1.116
--- src/include/storage/bufmgr.h	1 Nov 2008 17:17:25 -0000
***************
*** 28,34 ****
  	BAS_NORMAL,					/* Normal random access */
  	BAS_BULKREAD,				/* Large read-only scan (hint bit updates are
  								 * ok) */
! 	BAS_VACUUM					/* VACUUM */
  } BufferAccessStrategyType;
  
  /* Possible modes for ReadBufferExtended() */
--- 28,35 ----
  	BAS_NORMAL,					/* Normal random access */
  	BAS_BULKREAD,				/* Large read-only scan (hint bit updates are
  								 * ok) */
! 	BAS_VACUUM,					/* VACUUM */
! 	BAS_BULKWRITE				/* Large multi-block write (e.g. COPY IN) */
  } BufferAccessStrategyType;
  
  /* Possible modes for ReadBufferExtended() */
-- 
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