diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 1e5a11220c..68b1f12e08 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.95 2002/11/18 17:12:07 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.96 2002/11/23 04:05:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,12 +20,13 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catalog.h"
+#include "catalog/catname.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
-#include "catalog/catname.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_constraint.h"
 #include "commands/cluster.h"
 #include "commands/tablecmds.h"
 #include "miscadmin.h"
@@ -63,7 +64,6 @@ typedef struct
 
 static Oid	make_new_heap(Oid OIDOldHeap, const char *NewName);
 static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
-static List *get_indexattr_list(Relation OldHeap, Oid OldIndex);
 static void recreate_indexattr(Oid OIDOldHeap, List *indexes);
 static void swap_relfilenodes(Oid r1, Oid r2);
 static void cluster_rel(relToCluster *rv);
@@ -92,11 +92,8 @@ static MemoryContext cluster_context = NULL;
 void
 cluster_rel(relToCluster *rvtc)
 {
-	Oid			OIDNewHeap;
 	Relation	OldHeap,
 				OldIndex;
-	char		NewHeapName[NAMEDATALEN];
-	ObjectAddress object;
 	List	   *indexes;
 
 	/* Check for user-requested abort. */
@@ -172,6 +169,22 @@ cluster_rel(relToCluster *rvtc)
 	index_close(OldIndex);
 	heap_close(OldHeap, NoLock);
 
+	/* rebuild_rel does all the dirty work */
+	rebuild_rel(rvtc->tableOid, rvtc->indexOid, indexes, true);
+}
+
+void
+rebuild_rel(Oid tableOid, Oid indexOid, List *indexes, bool dataCopy)
+{
+	Oid			OIDNewHeap;
+	char		NewHeapName[NAMEDATALEN];
+	ObjectAddress object;
+
+	/*
+	 * If dataCopy is true, we assume that we will be basing the
+	 * copy off an index for cluster operations.
+	 */
+	Assert(!dataCopy || indexOid != NULL);
 	/*
 	 * Create the new heap, using a temporary name in the same namespace
 	 * as the existing table.  NOTE: there is some risk of collision with
@@ -180,10 +193,9 @@ cluster_rel(relToCluster *rvtc)
 	 * namespace from the old, or we will have problems with the TEMP
 	 * status of temp tables.
 	 */
-	snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", rvtc->tableOid);
-
-	OIDNewHeap = make_new_heap(rvtc->tableOid, NewHeapName);
+	snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", tableOid);
 
+	OIDNewHeap = make_new_heap(tableOid, NewHeapName);
 	/*
 	 * We don't need CommandCounterIncrement() because make_new_heap did
 	 * it.
@@ -192,13 +204,14 @@ cluster_rel(relToCluster *rvtc)
 	/*
 	 * Copy the heap data into the new table in the desired order.
 	 */
-	copy_heap_data(OIDNewHeap, rvtc->tableOid, rvtc->indexOid);
+	if (dataCopy)
+		copy_heap_data(OIDNewHeap, tableOid, indexOid);
 
 	/* To make the new heap's data visible (probably not needed?). */
 	CommandCounterIncrement();
 
 	/* Swap the relfilenodes of the old and new heaps. */
-	swap_relfilenodes(rvtc->tableOid, OIDNewHeap);
+	swap_relfilenodes(tableOid, OIDNewHeap);
 
 	CommandCounterIncrement();
 
@@ -219,7 +232,7 @@ cluster_rel(relToCluster *rvtc)
 	 * Recreate each index on the relation.  We do not need
 	 * CommandCounterIncrement() because recreate_indexattr does it.
 	 */
-	recreate_indexattr(rvtc->tableOid, indexes);
+	recreate_indexattr(tableOid, indexes);
 }
 
 /*
@@ -322,7 +335,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
  * Get the necessary info about the indexes of the relation and
  * return a list of IndexAttrs structures.
  */
-static List *
+List *
 get_indexattr_list(Relation OldHeap, Oid OldIndex)
 {
 	List	   *indexes = NIL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e3c3d0c290..c545b261a5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.55 2002/11/23 03:59:07 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.56 2002/11/23 04:05:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
+#include "commands/cluster.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
@@ -360,7 +361,7 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
  *		Removes all the rows from a relation.
  *
  * Note: This routine only does safety and permissions checks;
- * heap_truncate does the actual work.
+ * rebuild_rel in cluster.c does the actual work.
  */
 void
 TruncateRelation(const RangeVar *relation)
@@ -371,6 +372,7 @@ TruncateRelation(const RangeVar *relation)
 	Relation	fkeyRel;
 	SysScanDesc fkeyScan;
 	HeapTuple	tuple;
+	List	   *indexes;
 
 	/* Grab exclusive lock in preparation for truncate */
 	rel = heap_openrv(relation, AccessExclusiveLock);
@@ -399,16 +401,6 @@ TruncateRelation(const RangeVar *relation)
 	if (!pg_class_ownercheck(relid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
 
-	/*
-	 * Truncate within a transaction block is dangerous, because if
-	 * the transaction is later rolled back we have no way to undo
-	 * truncation of the relation's physical file.  Disallow it except for
-	 * a rel created in the current xact (which would be deleted on abort,
-	 * anyway).
-	 */
-	if (!rel->rd_isnew)
-		PreventTransactionChain((void *) relation, "TRUNCATE TABLE");
-
 	/*
 	 * Don't allow truncate on temp tables of other backends ... their
 	 * local buffer manager is not going to cope.
@@ -438,7 +430,8 @@ TruncateRelation(const RangeVar *relation)
 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
 
 		if (con->contype == 'f' && con->conrelid != relid)
-			elog(ERROR, "TRUNCATE cannot be used as table %s references this one via foreign key constraint %s",
+			elog(ERROR, "TRUNCATE cannot be used as table %s references "
+						"this one via foreign key constraint %s",
 				 get_rel_name(con->conrelid),
 				 NameStr(con->conname));
 	}
@@ -446,11 +439,17 @@ TruncateRelation(const RangeVar *relation)
 	systable_endscan(fkeyScan);
 	heap_close(fkeyRel, AccessShareLock);
 
+	/* Save the information of all indexes on the relation. */
+	indexes = get_indexattr_list(rel, InvalidOid);
+
 	/* Keep the lock until transaction commit */
 	heap_close(rel, NoLock);
 
-	/* Do the real work */
-	heap_truncate(relid);
+	/*
+	 * Do the real work using the same technique as cluster, but
+	 * without the code copy portion
+	 */
+	rebuild_rel(relid, NULL, indexes, false);
 }
 
 /*----------
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 2490278b6c..c83db66725 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Id: cluster.h,v 1.16 2002/11/15 03:09:39 momjian Exp $
+ * $Id: cluster.h,v 1.17 2002/11/23 04:05:52 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,4 +19,9 @@
  */
 extern void cluster(ClusterStmt *stmt);
 
+extern List *get_indexattr_list(Relation OldHeap, Oid OldIndex);
+extern void rebuild_rel(Oid tableOid, Oid indexOid,
+					    List *indexes, bool dataCopy);
+
+
 #endif   /* CLUSTER_H */
diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out
index 8751b1fdb7..2aa2f3810e 100644
--- a/src/test/regress/expected/truncate.out
+++ b/src/test/regress/expected/truncate.out
@@ -10,7 +10,21 @@ SELECT * FROM truncate_a;
     2
 (2 rows)
 
+-- Roll truncate back
+BEGIN;
 TRUNCATE truncate_a;
+ROLLBACK;
+SELECT * FROM truncate_a;
+ col1 
+------
+    1
+    2
+(2 rows)
+
+-- Commit the truncate this time
+BEGIN;
+TRUNCATE truncate_a;
+COMMIT;
 SELECT * FROM truncate_a;
  col1 
 ------
diff --git a/src/test/regress/sql/truncate.sql b/src/test/regress/sql/truncate.sql
index 5333113a9e..79e229feac 100644
--- a/src/test/regress/sql/truncate.sql
+++ b/src/test/regress/sql/truncate.sql
@@ -3,7 +3,15 @@ CREATE TABLE truncate_a (col1 integer primary key);
 INSERT INTO truncate_a VALUES (1);
 INSERT INTO truncate_a VALUES (2);
 SELECT * FROM truncate_a;
+-- Roll truncate back
+BEGIN;
 TRUNCATE truncate_a;
+ROLLBACK;
+SELECT * FROM truncate_a;
+-- Commit the truncate this time
+BEGIN;
+TRUNCATE truncate_a;
+COMMIT;
 SELECT * FROM truncate_a;
 
 -- Test foreign constraint check