Transaction safe Truncate
authorBruce Momjian
Sat, 23 Nov 2002 04:05:52 +0000 (04:05 +0000)
committerBruce Momjian
Sat, 23 Nov 2002 04:05:52 +0000 (04:05 +0000)
Rod Taylor

src/backend/commands/cluster.c
src/backend/commands/tablecmds.c
src/include/commands/cluster.h
src/test/regress/expected/truncate.out
src/test/regress/sql/truncate.sql

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