diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 010845536825b3892ee0336647c84260792deaa4..2d97dba701408fca2b0b2d645594689bef3ecc36 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.357 2009/08/02 22:14:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.358 2009/08/23 19:23:40 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -2342,18 +2342,9 @@ heap_truncate(List *relids)
 	{
 		Oid			rid = lfirst_oid(cell);
 		Relation	rel;
-		Oid			toastrelid;
 
 		rel = heap_open(rid, AccessExclusiveLock);
 		relations = lappend(relations, rel);
-
-		/* If there is a toast table, add it to the list too */
-		toastrelid = rel->rd_rel->reltoastrelid;
-		if (OidIsValid(toastrelid))
-		{
-			rel = heap_open(toastrelid, AccessExclusiveLock);
-			relations = lappend(relations, rel);
-		}
 	}
 
 	/* Don't allow truncate on tables that are referenced by foreign keys */
@@ -2364,19 +2355,47 @@ heap_truncate(List *relids)
 	{
 		Relation	rel = lfirst(cell);
 
-		/* Truncate the actual file (and discard buffers) */
-		RelationTruncate(rel, 0);
+		/* Truncate the relation */
+		heap_truncate_one_rel(rel);
 
-		/* If this relation has indexes, truncate the indexes too */
-		RelationTruncateIndexes(rel);
-
-		/*
-		 * Close the relation, but keep exclusive lock on it until commit.
-		 */
+		/* Close the relation, but keep exclusive lock on it until commit */
 		heap_close(rel, NoLock);
 	}
 }
 
+/*
+ *	 heap_truncate_one_rel
+ *
+ *	 This routine deletes all data within the specified relation.
+ *
+ * This is not transaction-safe, because the truncation is done immediately
+ * and cannot be rolled back later.  Caller is responsible for having
+ * checked permissions etc, and must have obtained AccessExclusiveLock.
+ */
+void
+heap_truncate_one_rel(Relation rel)
+{
+	Oid			toastrelid;
+
+	/* Truncate the actual file (and discard buffers) */
+	RelationTruncate(rel, 0);
+
+	/* If the relation has indexes, truncate the indexes too */
+	RelationTruncateIndexes(rel);
+
+	/* If there is a toast table, truncate that too */
+	toastrelid = rel->rd_rel->reltoastrelid;
+	if (OidIsValid(toastrelid))
+	{
+		Relation	toastrel = heap_open(toastrelid, AccessExclusiveLock);
+
+		RelationTruncate(toastrel, 0);
+		RelationTruncateIndexes(toastrel);
+		/* keep the lock... */
+		heap_close(toastrel, NoLock);
+	}
+}
+
 /*
  * heap_truncate_check_FKs
  *		Check for foreign keys referencing a list of relations that
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 07bc3932003b10e52135319c32f3d1700334433c..a36cbc51aff79f94368a61f2a13635f04d400781 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.297 2009/08/12 23:00:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.298 2009/08/23 19:23:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -775,6 +775,7 @@ ExecuteTruncate(TruncateStmt *stmt)
 	EState	   *estate;
 	ResultRelInfo *resultRelInfos;
 	ResultRelInfo *resultRelInfo;
+	SubTransactionId mySubid;
 	ListCell   *cell;
 
 	/*
@@ -944,36 +945,58 @@ ExecuteTruncate(TruncateStmt *stmt)
 	/*
 	 * OK, truncate each table.
 	 */
+	mySubid = GetCurrentSubTransactionId();
+
 	foreach(cell, rels)
 	{
 		Relation	rel = (Relation) lfirst(cell);
-		Oid			heap_relid;
-		Oid			toast_relid;
 
 		/*
-		 * Create a new empty storage file for the relation, and assign it as
-		 * the relfilenode value.	The old storage file is scheduled for
-		 * deletion at commit.
+		 * Normally, we need a transaction-safe truncation here.  However,
+		 * if the table was either created in the current (sub)transaction
+		 * or has a new relfilenode in the current (sub)transaction, then
+		 * we can just truncate it in-place, because a rollback would
+		 * cause the whole table or the current physical file to be
+		 * thrown away anyway.
 		 */
-		setNewRelfilenode(rel, RecentXmin);
-
-		heap_relid = RelationGetRelid(rel);
-		toast_relid = rel->rd_rel->reltoastrelid;
-
-		/*
-		 * The same for the toast table, if any.
-		 */
-		if (OidIsValid(toast_relid))
+		if (rel->rd_createSubid == mySubid ||
+			rel->rd_newRelfilenodeSubid == mySubid)
 		{
-			rel = relation_open(toast_relid, AccessExclusiveLock);
-			setNewRelfilenode(rel, RecentXmin);
-			heap_close(rel, NoLock);
+			/* Immediate, non-rollbackable truncation is OK */
+			heap_truncate_one_rel(rel);
 		}
+		else
+		{
+			Oid			heap_relid;
+			Oid			toast_relid;
 
-		/*
-		 * Reconstruct the indexes to match, and we're done.
-		 */
-		reindex_relation(heap_relid, true);
+			/*
+			 * Need the full transaction-safe pushups.
+			 *
+			 * Create a new empty storage file for the relation, and assign it
+			 * as the relfilenode value. The old storage file is scheduled for
+			 * deletion at commit.
+			 */
+			setNewRelfilenode(rel, RecentXmin);
+
+			heap_relid = RelationGetRelid(rel);
+			toast_relid = rel->rd_rel->reltoastrelid;
+
+			/*
+			 * The same for the toast table, if any.
+			 */
+			if (OidIsValid(toast_relid))
+			{
+				rel = relation_open(toast_relid, AccessExclusiveLock);
+				setNewRelfilenode(rel, RecentXmin);
+				heap_close(rel, NoLock);
+			}
+
+			/*
+			 * Reconstruct the indexes to match, and we're done.
+			 */
+			reindex_relation(heap_relid, true);
+		}
 	}
 
 	/*
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 2d6eb3c34ad392e140c26b2fd94489f70fac1d05..1d7449f1f10d4db075b483e0578af652e5db3c35 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.91 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.92 2009/08/23 19:23:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,6 +62,8 @@ extern void heap_drop_with_catalog(Oid relid);
 
 extern void heap_truncate(List *relids);
 
+extern void heap_truncate_one_rel(Relation rel);
+
 extern void heap_truncate_check_FKs(List *relations, bool tempTables);
 
 extern List *heap_truncate_find_FKs(List *relationIds);