diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index ee7f814ea89434a0a2e95862cf048c3a30a22733..fcdbd0ee36fa68d46b2b6de9147b55246fbc7a05 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.29 2009/06/17 21:58:49 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.30 2009/07/22 01:21:22 tgl Exp $ -->
 
 <chapter id="storage">
 
@@ -347,7 +347,7 @@ The <acronym>TOAST</> code recognizes four different strategies for storing
       <literal>MAIN</literal> allows compression but not out-of-line
       storage.  (Actually, out-of-line storage will still be performed
       for such columns, but only as a last resort when there is no other
-      way to make the row small enough.)
+      way to make the row small enough to fit on a page.)
      </para>
     </listitem>
    </itemizedlist>
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index a3203085b7a8df0a1d9399d4ad5fe4c0d0263bbc..781bfd2e4862f3439fc2b5680ddebbc8b28f4d16 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.93 2009/06/11 14:48:54 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.94 2009/07/22 01:21:22 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -796,8 +796,12 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 	}
 
 	/*
-	 * Finally we store attributes of type 'm' external, if possible.
+	 * Finally we store attributes of type 'm' externally.  At this point
+	 * we increase the target tuple size, so that 'm' attributes aren't
+	 * stored externally unless really necessary.
 	 */
+	maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
+
 	while (heap_compute_data_size(tupleDesc,
 								  toast_values, toast_isnull) > maxDataLen &&
 		   rel->rd_rel->reltoastrelid != InvalidOid)
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index db2f7e57607138d94f7250b218f174282b4aa308..f8dd7f82f5b0bf98ff5e75c8ca168776a3ecc240 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/access/tuptoaster.h,v 1.43 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/tuptoaster.h,v 1.44 2009/07/22 01:21:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,20 +24,26 @@
 #define TOAST_INDEX_HACK
 
 
+/*
+ * Find the maximum size of a tuple if there are to be N tuples per page.
+ */
+#define MaximumBytesPerTuple(tuplesPerPage)	\
+	MAXALIGN_DOWN((BLCKSZ - \
+				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
+				  / (tuplesPerPage))
+
 /*
  * These symbols control toaster activation.  If a tuple is larger than
  * TOAST_TUPLE_THRESHOLD, we will try to toast it down to no more than
- * TOAST_TUPLE_TARGET bytes.  Both numbers include all tuple header overhead
- * and between-fields alignment padding, but we do *not* consider any
- * end-of-tuple alignment padding; hence the values can be compared directly
- * to a tuple's t_len field.
+ * TOAST_TUPLE_TARGET bytes through compressing compressible fields and
+ * moving EXTENDED and EXTERNAL data out-of-line.
  *
  * The numbers need not be the same, though they currently are.  It doesn't
  * make sense for TARGET to exceed THRESHOLD, but it could be useful to make
  * it be smaller.
  *
  * Currently we choose both values to match the largest tuple size for which
- * TOAST_TUPLES_PER_PAGE tuples can fit on a disk page.
+ * TOAST_TUPLES_PER_PAGE tuples can fit on a heap page.
  *
  * XXX while these can be modified without initdb, some thought needs to be
  * given to needs_toast_table() in toasting.c before unleashing random
@@ -46,13 +52,21 @@
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define TOAST_TUPLE_THRESHOLD	\
-	MAXALIGN_DOWN((BLCKSZ - \
-				   MAXALIGN(SizeOfPageHeaderData + TOAST_TUPLES_PER_PAGE * sizeof(ItemIdData))) \
-				  / TOAST_TUPLES_PER_PAGE)
+#define TOAST_TUPLE_THRESHOLD	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
 
 #define TOAST_TUPLE_TARGET		TOAST_TUPLE_THRESHOLD
 
+/*
+ * The code will also consider moving MAIN data out-of-line, but only as a
+ * last resort if the previous steps haven't reached the target tuple size.
+ * In this phase we use a different target size, currently equal to the
+ * largest tuple that will fit on a heap page.  This is reasonable since
+ * the user has told us to keep the data in-line if at all possible.
+ */
+#define TOAST_TUPLES_PER_PAGE_MAIN	1
+
+#define TOAST_TUPLE_TARGET_MAIN	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
  * compress it (we can't move it out-of-line, however).  Note that this
@@ -72,10 +86,7 @@
  */
 #define EXTERN_TUPLES_PER_PAGE	4		/* tweak only this */
 
-#define EXTERN_TUPLE_MAX_SIZE	\
-	MAXALIGN_DOWN((BLCKSZ - \
-				   MAXALIGN(SizeOfPageHeaderData + EXTERN_TUPLES_PER_PAGE * sizeof(ItemIdData))) \
-				  / EXTERN_TUPLES_PER_PAGE)
+#define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
 #define TOAST_MAX_CHUNK_SIZE	\
 	(EXTERN_TUPLE_MAX_SIZE -							\