diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml
index 8bd220f77a7fb7df20b2aa8cd6807849efce6a5c..03ee825ad52409c0a54d24a0c4dbdef701b052c2 100644
--- a/doc/src/sgml/ref/truncate.sgml
+++ b/doc/src/sgml/ref/truncate.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.17 2004/03/23 13:21:41 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.18 2005/01/27 03:17:08 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -11,7 +11,7 @@ PostgreSQL documentation
 
  <refnamediv>
   <refname>TRUNCATE</refname>
-  <refpurpose>empty a table</refpurpose>
+  <refpurpose>empty a table or set of tables</refpurpose>
  </refnamediv>
 
  <indexterm zone="sql-truncate">
@@ -20,7 +20,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
+TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...]
 </synopsis>
  </refsynopsisdiv>
 
@@ -28,10 +28,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
   <title>Description</title>
 
   <para>
-   <command>TRUNCATE</command> quickly removes all rows from a
-   table. It has the same effect as an unqualified
-   <command>DELETE</command> but since it does not actually scan the
-   table it is faster. This is most useful on large tables.
+   <command>TRUNCATE</command> quickly removes all rows from a set of
+   tables. It has the same effect as an unqualified
+   <command>DELETE</command> on each table, but since it does not actually
+   scan the tables it is faster. This is most useful on large tables.
   </para>
  </refsect1>
   
@@ -43,7 +43,7 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
     <term><replaceable class="PARAMETER">name</replaceable></term>
     <listitem>
      <para>
-      The name (optionally schema-qualified) of the table to be truncated.
+      The name (optionally schema-qualified) of a table to be truncated.
      </para>
     </listitem>
    </varlistentry>
@@ -54,14 +54,15 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
   <title>Notes</title>
 
   <para>
-   <command>TRUNCATE</> cannot be used if there are foreign-key references
-   to the table from other tables.  Checking validity in such cases would
-   require table scans, and the whole point is not to do one.
+   <command>TRUNCATE</> cannot be used on a table that has foreign-key
+   references from other tables, unless all such tables are also truncated
+   in the same command.  Checking validity in such cases would require table
+   scans, and the whole point is not to do one.
   </para>
 
   <para>
    <command>TRUNCATE</> will not run any user-defined <literal>ON
-   DELETE</literal> triggers that might exist for the table.
+   DELETE</literal> triggers that might exist for the tables.
   </para>
  </refsect1>
 
@@ -69,10 +70,10 @@ TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable>
   <title>Examples</title>
 
   <para>
-   Truncate the table <literal>bigtable</literal>:
+   Truncate the tables <literal>bigtable</literal> and <literal>fattable</literal>:
 
 <programlisting>
-TRUNCATE TABLE bigtable;
+TRUNCATE TABLE bigtable, fattable;
 </programlisting>
   </para>
  </refsect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 99dc1cf2086b7806b0935cdf03f29ebe6b146ed5..2bf73a5984e276630aadc85d1f4bbce52efa3d1d 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.279 2005/01/10 20:02:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.280 2005/01/27 03:17:17 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1985,99 +1985,149 @@ RelationTruncateIndexes(Oid heapId)
 /*
  *	 heap_truncate
  *
- *	 This routine deletes all data within the specified relation.
+ *	 This routine deletes all data within all the specified relations.
  *
  * This is not transaction-safe!  There is another, transaction-safe
- * implementation in commands/cluster.c.  We now use this only for
+ * implementation in commands/tablecmds.c.  We now use this only for
  * ON COMMIT truncation of temporary tables, where it doesn't matter.
  */
 void
-heap_truncate(Oid rid)
+heap_truncate(List *relids)
 {
-	Relation	rel;
-	Oid			toastrelid;
+	List	   *relations = NIL;
+	ListCell   *cell;
+
+	/* Open relations for processing, and grab exclusive access on each */
+	foreach(cell, relids)
+	{
+		Oid			rid = lfirst_oid(cell);
+		Relation	rel;
+		Oid			toastrelid;
 
-	/* Open relation for processing, and grab exclusive access on it. */
-	rel = heap_open(rid, AccessExclusiveLock);
+		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 */
-	heap_truncate_check_FKs(rel);
+	heap_truncate_check_FKs(relations, true);
 
-	/*
-	 * Release any buffers associated with this relation.  If they're
-	 * dirty, they're just dropped without bothering to flush to disk.
-	 */
-	DropRelationBuffers(rel);
+	/* OK to do it */
+	foreach(cell, relations)
+	{
+		Relation	rel = lfirst(cell);
 
-	/* Now truncate the actual data */
-	RelationTruncate(rel, 0);
+		/*
+		 * Release any buffers associated with this relation.  If they're
+		 * dirty, they're just dropped without bothering to flush to disk.
+		 */
+		DropRelationBuffers(rel);
 
-	/* If this relation has indexes, truncate the indexes too */
-	RelationTruncateIndexes(rid);
+		/* Now truncate the actual data */
+		RelationTruncate(rel, 0);
 
-	/* If it has a toast table, recursively truncate that too */
-	toastrelid = rel->rd_rel->reltoastrelid;
-	if (OidIsValid(toastrelid))
-		heap_truncate(toastrelid);
+		/* If this relation has indexes, truncate the indexes too */
+		RelationTruncateIndexes(RelationGetRelid(rel));
 
-	/*
-	 * Close the relation, but keep exclusive lock on it until commit.
-	 */
-	heap_close(rel, NoLock);
+		/*
+		 * Close the relation, but keep exclusive lock on it until commit.
+		 */
+		heap_close(rel, NoLock);
+	}
 }
 
 /*
  * heap_truncate_check_FKs
- *		Check for foreign keys referencing a relation that's to be truncated
+ *		Check for foreign keys referencing a list of relations that
+ *		are to be truncated
  *
  * We disallow such FKs (except self-referential ones) since the whole point
  * of TRUNCATE is to not scan the individual rows to be thrown away.
  *
  * This is split out so it can be shared by both implementations of truncate.
- * Caller should already hold a suitable lock on the relation.
+ * Caller should already hold a suitable lock on the relations.
+ *
+ * tempTables is only used to select an appropriate error message.
  */
 void
-heap_truncate_check_FKs(Relation rel)
+heap_truncate_check_FKs(List *relations, bool tempTables)
 {
-	Oid			relid = RelationGetRelid(rel);
-	ScanKeyData key;
+	List	   *oids = NIL;
+	ListCell   *cell;
 	Relation	fkeyRel;
 	SysScanDesc fkeyScan;
 	HeapTuple	tuple;
 
 	/*
-	 * Fast path: if the relation has no triggers, it surely has no FKs
-	 * either.
+	 * Build a list of OIDs of the interesting relations.
+	 *
+	 * If a relation has no triggers, then it can neither have FKs nor be
+	 * referenced by a FK from another table, so we can ignore it.
 	 */
-	if (rel->rd_rel->reltriggers == 0)
+	foreach(cell, relations)
+	{
+		Relation	rel = lfirst(cell);
+
+		if (rel->rd_rel->reltriggers != 0)
+			oids = lappend_oid(oids, RelationGetRelid(rel));
+	}
+
+	/*
+	 * Fast path: if no relation has triggers, none has FKs either.
+	 */
+	if (oids == NIL)
 		return;
 
 	/*
-	 * Otherwise, must scan pg_constraint.	Right now, this is a seqscan
+	 * Otherwise, must scan pg_constraint.	Right now, it is a seqscan
 	 * because there is no available index on confrelid.
 	 */
 	fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock);
 
-	ScanKeyInit(&key,
-				Anum_pg_constraint_confrelid,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(relid));
-
 	fkeyScan = systable_beginscan(fkeyRel, NULL, false,
-								  SnapshotNow, 1, &key);
+								  SnapshotNow, 0, NULL);
 
 	while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan)))
 	{
 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
 
-		if (con->contype == CONSTRAINT_FOREIGN && con->conrelid != relid)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("cannot truncate a table referenced in a foreign key constraint"),
-					 errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".",
-							   get_rel_name(con->conrelid),
-							   RelationGetRelationName(rel),
-							   NameStr(con->conname))));
+		/* Not a foreign key */
+		if (con->contype != CONSTRAINT_FOREIGN)
+			continue;
+
+		/* Not for one of our list of tables */
+		if (! list_member_oid(oids, con->confrelid))
+			continue;
+
+		/* The referencer should be in our list too */
+		if (! list_member_oid(oids, con->conrelid))
+		{
+			if (tempTables)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("unsupported ON COMMIT and foreign key combination"),
+						 errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\", but they do not have the same ON COMMIT setting.",
+								   get_rel_name(con->conrelid),
+								   get_rel_name(con->confrelid),
+								   NameStr(con->conname))));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot truncate a table referenced in a foreign key constraint"),
+						 errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".",
+								   get_rel_name(con->conrelid),
+								   get_rel_name(con->confrelid),
+								   NameStr(con->conname)),
+						 errhint("Truncate table \"%s\" at the same time.",
+								 get_rel_name(con->conrelid))));
+		}
 	}
 
 	systable_endscan(fkeyScan);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 89dd8dffae015371e5841448428b9107cfc22338..6abf4ab65e151fef541915f0c4f0c6afdeedeec5 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.143 2005/01/24 23:21:57 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.144 2005/01/27 03:17:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -525,88 +525,113 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
 }
 
 /*
- * TruncateRelation
- *		Removes all the rows from a relation.
+ * ExecuteTruncate
+ * 		Executes a TRUNCATE command.
+ *
+ * This is a multi-relation truncate.  It first opens and grabs exclusive
+ * locks on all relations involved, checking permissions and otherwise
+ * verifying that the relation is OK for truncation.  When they are all
+ * open, it checks foreign key references on them, namely that FK references
+ * are all internal to the group that's being truncated.  Finally all
+ * relations are truncated and reindexed.
  */
 void
-TruncateRelation(const RangeVar *relation)
+ExecuteTruncate(List *relations)
 {
-	Relation	rel;
-	Oid			heap_relid;
-	Oid			toast_relid;
+	List		*rels = NIL;
+	ListCell	*cell;
 
-	/* Grab exclusive lock in preparation for truncate */
-	rel = heap_openrv(relation, AccessExclusiveLock);
+	foreach(cell, relations)
+	{
+		RangeVar   *rv = lfirst(cell);
+		Relation	rel;
 
-	/* Only allow truncate on regular tables */
-	if (rel->rd_rel->relkind != RELKIND_RELATION)
-		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table",
-						RelationGetRelationName(rel))));
+		/* Grab exclusive lock in preparation for truncate */
+		rel = heap_openrv(rv, AccessExclusiveLock);
 
-	/* Permissions checks */
-	if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
-		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
-					   RelationGetRelationName(rel));
+		/* Only allow truncate on regular tables */
+		if (rel->rd_rel->relkind != RELKIND_RELATION)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("\"%s\" is not a table",
+						 RelationGetRelationName(rel))));
 
-	if (!allowSystemTableMods && IsSystemRelation(rel))
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied: \"%s\" is a system catalog",
-						RelationGetRelationName(rel))));
+		/* Permissions checks */
+		if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
+			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+					RelationGetRelationName(rel));
 
-	/*
-	 * We can never allow truncation of shared or nailed-in-cache
-	 * relations, because we can't support changing their relfilenode
-	 * values.
-	 */
-	if (rel->rd_rel->relisshared || rel->rd_isnailed)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot truncate system relation \"%s\"",
-						RelationGetRelationName(rel))));
+		if (!allowSystemTableMods && IsSystemRelation(rel))
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied: \"%s\" is a system catalog",
+						 RelationGetRelationName(rel))));
 
-	/*
-	 * Don't allow truncate on temp tables of other backends ... their
-	 * local buffer manager is not going to cope.
-	 */
-	if (isOtherTempNamespace(RelationGetNamespace(rel)))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-		  errmsg("cannot truncate temporary tables of other sessions")));
+		/*
+		 * We can never allow truncation of shared or nailed-in-cache
+		 * relations, because we can't support changing their relfilenode
+		 * values.
+		 */
+		if (rel->rd_rel->relisshared || rel->rd_isnailed)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot truncate system relation \"%s\"",
+						 RelationGetRelationName(rel))));
 
-	/*
-	 * Don't allow truncate on tables which are referenced by foreign keys
-	 */
-	heap_truncate_check_FKs(rel);
+		/*
+		 * Don't allow truncate on temp tables of other backends ... their
+		 * local buffer manager is not going to cope.
+		 */
+		if (isOtherTempNamespace(RelationGetNamespace(rel)))
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot truncate temporary tables of other sessions")));
+
+		/* Save it into the list of rels to truncate */
+		rels = lappend(rels, rel);
+	}
 
 	/*
-	 * Okay, here we go: 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.
+	 * Check foreign key references.
 	 */
-	setNewRelfilenode(rel);
-
-	heap_relid = RelationGetRelid(rel);
-	toast_relid = rel->rd_rel->reltoastrelid;
-
-	heap_close(rel, NoLock);
+	heap_truncate_check_FKs(rels, false);
 
 	/*
-	 * The same for the toast table, if any.
+	 * OK, truncate each table.
 	 */
-	if (OidIsValid(toast_relid))
+	foreach(cell, rels)
 	{
-		rel = relation_open(toast_relid, AccessExclusiveLock);
+		Relation	rel = 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.
+		 */
 		setNewRelfilenode(rel);
+
+		heap_relid = RelationGetRelid(rel);
+		toast_relid = rel->rd_rel->reltoastrelid;
+
 		heap_close(rel, NoLock);
-	}
 
-	/*
-	 * Reconstruct the indexes to match, and we're done.
-	 */
-	reindex_relation(heap_relid, true);
+		/*
+		 * The same for the toast table, if any.
+		 */
+		if (OidIsValid(toast_relid))
+		{
+			rel = relation_open(toast_relid, AccessExclusiveLock);
+			setNewRelfilenode(rel);
+			heap_close(rel, NoLock);
+		}
+
+		/*
+		 * Reconstruct the indexes to match, and we're done.
+		 */
+		reindex_relation(heap_relid, true);
+	}
 }
 
 /*----------
@@ -6013,6 +6038,7 @@ void
 PreCommit_on_commit_actions(void)
 {
 	ListCell   *l;
+	List	   *oids_to_truncate = NIL;
 
 	foreach(l, on_commits)
 	{
@@ -6029,8 +6055,7 @@ PreCommit_on_commit_actions(void)
 				/* Do nothing (there shouldn't be such entries, actually) */
 				break;
 			case ONCOMMIT_DELETE_ROWS:
-				heap_truncate(oc->relid);
-				CommandCounterIncrement();		/* XXX needed? */
+				oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
 				break;
 			case ONCOMMIT_DROP:
 				{
@@ -6051,6 +6076,11 @@ PreCommit_on_commit_actions(void)
 				}
 		}
 	}
+	if (oids_to_truncate != NIL)
+	{
+		heap_truncate(oids_to_truncate);
+		CommandCounterIncrement();				/* XXX needed? */
+	}
 }
 
 /*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index db69cb0a27845a2e72ca5efa08583982c72e53d3..f138402b3c2573a36bb199c324393f488ca3660a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.295 2004/12/31 21:59:55 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.296 2005/01/27 03:17:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1815,7 +1815,7 @@ _copyTruncateStmt(TruncateStmt *from)
 {
 	TruncateStmt *newnode = makeNode(TruncateStmt);
 
-	COPY_NODE_FIELD(relation);
+	COPY_NODE_FIELD(relations);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d2caee60e1c3337c5c336ea45c55443c9308fdf6..8430eddf69c96418111371a5c35ab0b63a945d0f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.234 2004/12/31 21:59:55 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.235 2005/01/27 03:17:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -891,7 +891,7 @@ _equalDropStmt(DropStmt *a, DropStmt *b)
 static bool
 _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b)
 {
-	COMPARE_NODE_FIELD(relation);
+	COMPARE_NODE_FIELD(relations);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 766fe912d5523c0fbf47a990d05bd99f4583d082..757a17e891bfc19cf175fdea22c3a22d06a7493e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.481 2004/12/31 22:00:27 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.482 2005/01/27 03:17:59 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2681,15 +2681,15 @@ attrs:		'.' attr_name
 /*****************************************************************************
  *
  *		QUERY:
- *				truncate table relname
+ *				truncate table relname1, relname2, ...
  *
  *****************************************************************************/
 
 TruncateStmt:
-			TRUNCATE opt_table qualified_name
+			TRUNCATE opt_table qualified_name_list
 				{
 					TruncateStmt *n = makeNode(TruncateStmt);
-					n->relation = $3;
+					n->relations = $3;
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 81d557c54ed07e4dce8dbac59ac40b23a921b7eb..006f904f72e0648124f972e425dfb0ca2f7278e6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.232 2005/01/24 17:46:16 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.233 2005/01/27 03:18:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -575,7 +575,7 @@ ProcessUtility(Node *parsetree,
 			{
 				TruncateStmt *stmt = (TruncateStmt *) parsetree;
 
-				TruncateRelation(stmt->relation);
+				ExecuteTruncate(stmt->relations);
 			}
 			break;
 
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index bc31c8d777891f58ff7fc21d769dbbc3e9fc6bfc..7a7352c69382b0e3d784b35ad87c81e5da04f66d 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.72 2004/12/31 22:03:24 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.73 2005/01/27 03:18:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,9 +56,9 @@ extern Oid heap_create_with_catalog(const char *relname,
 
 extern void heap_drop_with_catalog(Oid relid);
 
-extern void heap_truncate(Oid rid);
+extern void heap_truncate(List *relids);
 
-extern void heap_truncate_check_FKs(Relation rel);
+extern void heap_truncate_check_FKs(List *relations, bool tempTables);
 
 extern List *AddRelationRawConstraints(Relation rel,
 						  List *rawColDefaults,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 75da81f00006f597d09278b518dca102572e9ffb..5ec8c9a57d1596dc5033729efb17b3cf83acb637 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.21 2004/12/31 22:03:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.22 2005/01/27 03:18:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,7 @@ extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
 
 extern void AlterTableCreateToastTable(Oid relOid, bool silent);
 
-extern void TruncateRelation(const RangeVar *relation);
+extern void ExecuteTruncate(List *relations);
 
 extern void renameatt(Oid myrelid,
 		  const char *oldattname,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 14df21516d7780066627df68b6e28c4d054c8454..f5c38619786346f2345793c49520e5ccdd9a5b9e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.271 2004/12/31 22:03:34 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.272 2005/01/27 03:18:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1283,7 +1283,7 @@ typedef struct DropPropertyStmt
 typedef struct TruncateStmt
 {
 	NodeTag		type;
-	RangeVar   *relation;		/* relation to be truncated */
+	List	   *relations;		/* relations (RangeVars) to be truncated */
 } TruncateStmt;
 
 /* ----------------------
diff --git a/src/test/regress/expected/temp.out b/src/test/regress/expected/temp.out
index 650f5f704edf2ca7ed8167c78d1719ee1e0abb87..897ae751bd9f6560db1c29e026bbf269c798999a 100644
--- a/src/test/regress/expected/temp.out
+++ b/src/test/regress/expected/temp.out
@@ -82,3 +82,30 @@ ERROR:  relation "temptest" does not exist
 -- ON COMMIT is only allowed for TEMP
 CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS;
 ERROR:  ON COMMIT can only be used on temporary tables
+-- Test foreign keys
+BEGIN;
+CREATE TEMP TABLE temptest1(col int PRIMARY KEY);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "temptest1_pkey" for table "temptest1"
+CREATE TEMP TABLE temptest2(col int REFERENCES temptest1)
+  ON COMMIT DELETE ROWS;
+INSERT INTO temptest1 VALUES (1);
+INSERT INTO temptest2 VALUES (1);
+COMMIT;
+SELECT * FROM temptest1;
+ col 
+-----
+   1
+(1 row)
+
+SELECT * FROM temptest2;
+ col 
+-----
+(0 rows)
+
+BEGIN;
+CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "temptest3_pkey" for table "temptest3"
+CREATE TEMP TABLE temptest4(col int REFERENCES temptest3);
+COMMIT;
+ERROR:  unsupported ON COMMIT and foreign key combination
+DETAIL:  Table "temptest4" references "temptest3" via foreign key constraint "temptest4_col_fkey", but they do not have the same ON COMMIT setting.
diff --git a/src/test/regress/expected/truncate.out b/src/test/regress/expected/truncate.out
index a3a00502512d38096ff2ab817def91b87a26fa47..c03deef1e8eea9d99e8020a27ab0743073f60761 100644
--- a/src/test/regress/expected/truncate.out
+++ b/src/test/regress/expected/truncate.out
@@ -30,23 +30,84 @@ SELECT * FROM truncate_a;
 ------
 (0 rows)
 
--- Test foreign constraint check
-CREATE TABLE truncate_b(col1 integer references truncate_a);
+-- Test foreign-key checks
+CREATE TABLE trunc_b (a int REFERENCES truncate_a);
+CREATE TABLE trunc_c (a serial PRIMARY KEY);
+NOTICE:  CREATE TABLE will create implicit sequence "trunc_c_a_seq" for serial column "trunc_c.a"
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "trunc_c_pkey" for table "trunc_c"
+CREATE TABLE trunc_d (a int REFERENCES trunc_c);
+CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c);
+TRUNCATE TABLE truncate_a;		-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey".
+HINT:  Truncate table "trunc_b" at the same time.
+TRUNCATE TABLE truncate_a,trunc_b;		-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_e" references "truncate_a" via foreign key constraint "trunc_e_a_fkey".
+HINT:  Truncate table "trunc_e" at the same time.
+TRUNCATE TABLE truncate_a,trunc_b,trunc_e;	-- ok
+TRUNCATE TABLE truncate_a,trunc_e;		-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey".
+HINT:  Truncate table "trunc_b" at the same time.
+TRUNCATE TABLE trunc_c;		-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_d" references "trunc_c" via foreign key constraint "trunc_d_a_fkey".
+HINT:  Truncate table "trunc_d" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d;		-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_e" references "trunc_c" via foreign key constraint "trunc_e_b_fkey".
+HINT:  Truncate table "trunc_e" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e;	-- ok
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a;	-- fail
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey".
+HINT:  Truncate table "trunc_b" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b;	-- ok
+-- circular references
+ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c;
+-- Add some data to verify that truncating actually works ...
+INSERT INTO trunc_c VALUES (1);
 INSERT INTO truncate_a VALUES (1);
-SELECT * FROM truncate_a;
- col1 
-------
-    1
-(1 row)
-
-TRUNCATE truncate_a;
+INSERT INTO trunc_b VALUES (1);
+INSERT INTO trunc_d VALUES (1);
+INSERT INTO trunc_e VALUES (1,1);
+TRUNCATE TABLE trunc_c;
 ERROR:  cannot truncate a table referenced in a foreign key constraint
-DETAIL:  Table "truncate_b" references "truncate_a" via foreign key constraint "truncate_b_col1_fkey".
-SELECT * FROM truncate_a;
+DETAIL:  Table "trunc_d" references "trunc_c" via foreign key constraint "trunc_d_a_fkey".
+HINT:  Truncate table "trunc_d" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d;
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_e" references "trunc_c" via foreign key constraint "trunc_e_b_fkey".
+HINT:  Truncate table "trunc_e" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e;
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "truncate_a" references "trunc_c" via foreign key constraint "truncate_a_col1_fkey".
+HINT:  Truncate table "truncate_a" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a;
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "trunc_b" references "truncate_a" via foreign key constraint "trunc_b_a_fkey".
+HINT:  Truncate table "trunc_b" at the same time.
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b;
+-- Verify that truncating did actually work
+SELECT * FROM truncate_a
+   UNION ALL
+ SELECT * FROM trunc_c
+   UNION ALL
+ SELECT * FROM trunc_b
+   UNION ALL
+ SELECT * FROM trunc_d;
  col1 
 ------
-    1
-(1 row)
+(0 rows)
+
+SELECT * FROM trunc_e;
+ a | b 
+---+---
+(0 rows)
 
-DROP TABLE truncate_b;
-DROP TABLE truncate_a;
+DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;
+NOTICE:  drop cascades to constraint trunc_e_a_fkey on table trunc_e
+NOTICE:  drop cascades to constraint trunc_b_a_fkey on table trunc_b
+NOTICE:  drop cascades to constraint trunc_e_b_fkey on table trunc_e
+NOTICE:  drop cascades to constraint trunc_d_a_fkey on table trunc_d
diff --git a/src/test/regress/sql/temp.sql b/src/test/regress/sql/temp.sql
index 397d00bfd94e3682e8896eee2abe605832498a45..972d511ab7635bf659bdabee4b8884831db1f9dd 100644
--- a/src/test/regress/sql/temp.sql
+++ b/src/test/regress/sql/temp.sql
@@ -83,3 +83,19 @@ SELECT * FROM temptest;
 -- ON COMMIT is only allowed for TEMP
 
 CREATE TABLE temptest(col int) ON COMMIT DELETE ROWS;
+
+-- Test foreign keys
+BEGIN;
+CREATE TEMP TABLE temptest1(col int PRIMARY KEY);
+CREATE TEMP TABLE temptest2(col int REFERENCES temptest1)
+  ON COMMIT DELETE ROWS;
+INSERT INTO temptest1 VALUES (1);
+INSERT INTO temptest2 VALUES (1);
+COMMIT;
+SELECT * FROM temptest1;
+SELECT * FROM temptest2;
+
+BEGIN;
+CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE temptest4(col int REFERENCES temptest3);
+COMMIT;
diff --git a/src/test/regress/sql/truncate.sql b/src/test/regress/sql/truncate.sql
index 79e229feac6136cb2acc774ce7e3a62b4ec45052..09a08e5652a82f5e68605747adf7f349bbd8daaf 100644
--- a/src/test/regress/sql/truncate.sql
+++ b/src/test/regress/sql/truncate.sql
@@ -14,12 +14,45 @@ TRUNCATE truncate_a;
 COMMIT;
 SELECT * FROM truncate_a;
 
--- Test foreign constraint check
-CREATE TABLE truncate_b(col1 integer references truncate_a);
+-- Test foreign-key checks
+CREATE TABLE trunc_b (a int REFERENCES truncate_a);
+CREATE TABLE trunc_c (a serial PRIMARY KEY);
+CREATE TABLE trunc_d (a int REFERENCES trunc_c);
+CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c);
+
+TRUNCATE TABLE truncate_a;		-- fail
+TRUNCATE TABLE truncate_a,trunc_b;		-- fail
+TRUNCATE TABLE truncate_a,trunc_b,trunc_e;	-- ok
+TRUNCATE TABLE truncate_a,trunc_e;		-- fail
+TRUNCATE TABLE trunc_c;		-- fail
+TRUNCATE TABLE trunc_c,trunc_d;		-- fail
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e;	-- ok
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a;	-- fail
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b;	-- ok
+
+-- circular references
+ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c;
+
+-- Add some data to verify that truncating actually works ...
+INSERT INTO trunc_c VALUES (1);
 INSERT INTO truncate_a VALUES (1);
-SELECT * FROM truncate_a;
-TRUNCATE truncate_a;
-SELECT * FROM truncate_a;
+INSERT INTO trunc_b VALUES (1);
+INSERT INTO trunc_d VALUES (1);
+INSERT INTO trunc_e VALUES (1,1);
+TRUNCATE TABLE trunc_c;
+TRUNCATE TABLE trunc_c,trunc_d;
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e;
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a;
+TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b;
+
+-- Verify that truncating did actually work
+SELECT * FROM truncate_a
+   UNION ALL
+ SELECT * FROM trunc_c
+   UNION ALL
+ SELECT * FROM trunc_b
+   UNION ALL
+ SELECT * FROM trunc_d;
+SELECT * FROM trunc_e;
 
-DROP TABLE truncate_b;
-DROP TABLE truncate_a;
+DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE;