aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/truncate.sgml29
-rw-r--r--src/backend/catalog/heap.c148
-rw-r--r--src/backend/commands/tablecmds.c160
-rw-r--r--src/backend/nodes/copyfuncs.c4
-rw-r--r--src/backend/nodes/equalfuncs.c4
-rw-r--r--src/backend/parser/gram.y8
-rw-r--r--src/backend/tcop/utility.c4
-rw-r--r--src/include/catalog/heap.h6
-rw-r--r--src/include/commands/tablecmds.h4
-rw-r--r--src/include/nodes/parsenodes.h4
-rw-r--r--src/test/regress/expected/temp.out27
-rw-r--r--src/test/regress/expected/truncate.out91
-rw-r--r--src/test/regress/sql/temp.sql16
-rw-r--r--src/test/regress/sql/truncate.sql47
14 files changed, 385 insertions, 167 deletions
diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml
index 8bd220f77a7..03ee825ad52 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 99dc1cf2086..2bf73a5984e 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 89dd8dffae0..6abf4ab65e1 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 db69cb0a278..f138402b3c2 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 d2caee60e1c..8430eddf69c 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 766fe912d55..757a17e891b 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 81d557c54ed..006f904f72e 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 bc31c8d7778..7a7352c6938 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 75da81f0000..5ec8c9a57d1 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 14df21516d7..f5c38619786 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 650f5f704ed..897ae751bd9 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 a3a00502512..c03deef1e8e 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 397d00bfd94..972d511ab76 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 79e229feac6..09a08e5652a 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;