Generalize TRUNCATE to support truncating multiple tables in one
authorTom Lane
Thu, 27 Jan 2005 03:19:37 +0000 (03:19 +0000)
committerTom Lane
Thu, 27 Jan 2005 03:19:37 +0000 (03:19 +0000)
command.  This is useful because we can allow truncation of tables
referenced by foreign keys, so long as the referencing table is
truncated in the same command.

Alvaro Herrera

14 files changed:
doc/src/sgml/ref/truncate.sgml
src/backend/catalog/heap.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/catalog/heap.h
src/include/commands/tablecmds.h
src/include/nodes/parsenodes.h
src/test/regress/expected/temp.out
src/test/regress/expected/truncate.out
src/test/regress/sql/temp.sql
src/test/regress/sql/truncate.sql

index 8bd220f77a7fb7df20b2aa8cd6807849efce6a5c..03ee825ad52409c0a54d24a0c4dbdef701b052c2 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -11,7 +11,7 @@ PostgreSQL documentation
 
  
   TRUNCATE
-  empty a table
+  empty a table or set of tables
  
 
  
@@ -20,7 +20,7 @@ PostgreSQL documentation
 
  
 
-TRUNCATE [ TABLE ] name
+TRUNCATE [ TABLE ] name [, ...]
 
  
 
@@ -28,10 +28,10 @@ TRUNCATE [ TABLE ] name
   Description
 
   
-   TRUNCATE quickly removes all rows from a
-   table. It has the same effect as an unqualified
-   DELETE but since it does not actually scan the
-   table it is faster. This is most useful on large tables.
+   TRUNCATE quickly removes all rows from a set of
+   tables. It has the same effect as an unqualified
+   DELETE on each table, but since it does not actually
+   scan the tables it is faster. This is most useful on large tables.
   
  
   
@@ -43,7 +43,7 @@ TRUNCATE [ TABLE ] name
     name
     
      
-      The name (optionally schema-qualified) of the table to be truncated.
+      The name (optionally schema-qualified) of a table to be truncated.
      
     
    
@@ -54,14 +54,15 @@ TRUNCATE [ TABLE ] name
   Notes
 
   
-   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.
+   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.
   
 
   
    TRUNCATE will not run any user-defined ON
-   DELETE triggers that might exist for the table.
+   DELETE triggers that might exist for the tables.
   
  
 
@@ -69,10 +70,10 @@ TRUNCATE [ TABLE ] name
   Examples
 
   
-   Truncate the table bigtable:
+   Truncate the tablebigtable and fattable:
 
 
-TRUNCATE TABLE bigtable;
+TRUNCATE TABLE bigtable, fattable;
 
   
  
index 99dc1cf2086b7806b0935cdf03f29ebe6b146ed5..2bf73a5984e276630aadc85d1f4bbce52efa3d1d 100644 (file)
@@ -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);
index 89dd8dffae015371e5841448428b9107cfc22338..6abf4ab65e151fef541915f0c4f0c6afdeedeec5 100644 (file)
@@ -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? */
+   }
 }
 
 /*
index db69cb0a27845a2e72ca5efa08583982c72e53d3..f138402b3c2573a36bb199c324393f488ca3660a 100644 (file)
@@ -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;
 }
index d2caee60e1c3337c5c336ea45c55443c9308fdf6..8430eddf69c96418111371a5c35ab0b63a945d0f 100644 (file)
@@ -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;
 }
index 766fe912d5523c0fbf47a990d05bd99f4583d082..757a17e891bfc19cf175fdea22c3a22d06a7493e 100644 (file)
@@ -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;
                }
        ;
index 81d557c54ed07e4dce8dbac59ac40b23a921b7eb..006f904f72e0648124f972e425dfb0ca2f7278e6 100644 (file)
@@ -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;
 
index bc31c8d777891f58ff7fc21d769dbbc3e9fc6bfc..7a7352c69382b0e3d784b35ad87c81e5da04f66d 100644 (file)
@@ -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,
index 75da81f00006f597d09278b518dca102572e9ffb..5ec8c9a57d1596dc5033729efb17b3cf83acb637 100644 (file)
@@ -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,
index 14df21516d7780066627df68b6e28c4d054c8454..f5c38619786346f2345793c49520e5ccdd9a5b9e 100644 (file)
@@ -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;
 
 /* ----------------------
index 650f5f704edf2ca7ed8167c78d1719ee1e0abb87..897ae751bd9f6560db1c29e026bbf269c798999a 100644 (file)
@@ -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.
index a3a00502512d38096ff2ab817def91b87a26fa47..c03deef1e8eea9d99e8020a27ab0743073f60761 100644 (file)
@@ -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
index 397d00bfd94e3682e8896eee2abe605832498a45..972d511ab7635bf659bdabee4b8884831db1f9dd 100644 (file)
@@ -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;
index 79e229feac6136cb2acc774ce7e3a62b4ec45052..09a08e5652a82f5e68605747adf7f349bbd8daaf 100644 (file)
@@ -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;