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