Commit d46f3de3 authored by Bruce Momjian's avatar Bruce Momjian

Transaction safe Truncate

Rod Taylor
parent a2b4a707
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -20,12 +20,13 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/heap.h" #include "catalog/heap.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/catname.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "commands/cluster.h" #include "commands/cluster.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -63,7 +64,6 @@ typedef struct ...@@ -63,7 +64,6 @@ typedef struct
static Oid make_new_heap(Oid OIDOldHeap, const char *NewName); static Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); 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 recreate_indexattr(Oid OIDOldHeap, List *indexes);
static void swap_relfilenodes(Oid r1, Oid r2); static void swap_relfilenodes(Oid r1, Oid r2);
static void cluster_rel(relToCluster *rv); static void cluster_rel(relToCluster *rv);
...@@ -92,11 +92,8 @@ static MemoryContext cluster_context = NULL; ...@@ -92,11 +92,8 @@ static MemoryContext cluster_context = NULL;
void void
cluster_rel(relToCluster *rvtc) cluster_rel(relToCluster *rvtc)
{ {
Oid OIDNewHeap;
Relation OldHeap, Relation OldHeap,
OldIndex; OldIndex;
char NewHeapName[NAMEDATALEN];
ObjectAddress object;
List *indexes; List *indexes;
/* Check for user-requested abort. */ /* Check for user-requested abort. */
...@@ -172,6 +169,22 @@ cluster_rel(relToCluster *rvtc) ...@@ -172,6 +169,22 @@ cluster_rel(relToCluster *rvtc)
index_close(OldIndex); index_close(OldIndex);
heap_close(OldHeap, NoLock); 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 * Create the new heap, using a temporary name in the same namespace
* as the existing table. NOTE: there is some risk of collision with * as the existing table. NOTE: there is some risk of collision with
...@@ -180,10 +193,9 @@ cluster_rel(relToCluster *rvtc) ...@@ -180,10 +193,9 @@ cluster_rel(relToCluster *rvtc)
* namespace from the old, or we will have problems with the TEMP * namespace from the old, or we will have problems with the TEMP
* status of temp tables. * status of temp tables.
*/ */
snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", rvtc->tableOid); snprintf(NewHeapName, NAMEDATALEN, "pg_temp_%u", tableOid);
OIDNewHeap = make_new_heap(rvtc->tableOid, NewHeapName);
OIDNewHeap = make_new_heap(tableOid, NewHeapName);
/* /*
* We don't need CommandCounterIncrement() because make_new_heap did * We don't need CommandCounterIncrement() because make_new_heap did
* it. * it.
...@@ -192,13 +204,14 @@ cluster_rel(relToCluster *rvtc) ...@@ -192,13 +204,14 @@ cluster_rel(relToCluster *rvtc)
/* /*
* Copy the heap data into the new table in the desired order. * 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?). */ /* To make the new heap's data visible (probably not needed?). */
CommandCounterIncrement(); CommandCounterIncrement();
/* Swap the relfilenodes of the old and new heaps. */ /* Swap the relfilenodes of the old and new heaps. */
swap_relfilenodes(rvtc->tableOid, OIDNewHeap); swap_relfilenodes(tableOid, OIDNewHeap);
CommandCounterIncrement(); CommandCounterIncrement();
...@@ -219,7 +232,7 @@ cluster_rel(relToCluster *rvtc) ...@@ -219,7 +232,7 @@ cluster_rel(relToCluster *rvtc)
* Recreate each index on the relation. We do not need * Recreate each index on the relation. We do not need
* CommandCounterIncrement() because recreate_indexattr does it. * 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) ...@@ -322,7 +335,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* Get the necessary info about the indexes of the relation and * Get the necessary info about the indexes of the relation and
* return a list of IndexAttrs structures. * return a list of IndexAttrs structures.
*/ */
static List * List *
get_indexattr_list(Relation OldHeap, Oid OldIndex) get_indexattr_list(Relation OldHeap, Oid OldIndex)
{ {
List *indexes = NIL; List *indexes = NIL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -29,6 +29,7 @@
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_trigger.h" #include "catalog/pg_trigger.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/cluster.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/executor.h" #include "executor/executor.h"
...@@ -360,7 +361,7 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior) ...@@ -360,7 +361,7 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
* Removes all the rows from a relation. * Removes all the rows from a relation.
* *
* Note: This routine only does safety and permissions checks; * 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 void
TruncateRelation(const RangeVar *relation) TruncateRelation(const RangeVar *relation)
...@@ -371,6 +372,7 @@ TruncateRelation(const RangeVar *relation) ...@@ -371,6 +372,7 @@ TruncateRelation(const RangeVar *relation)
Relation fkeyRel; Relation fkeyRel;
SysScanDesc fkeyScan; SysScanDesc fkeyScan;
HeapTuple tuple; HeapTuple tuple;
List *indexes;
/* Grab exclusive lock in preparation for truncate */ /* Grab exclusive lock in preparation for truncate */
rel = heap_openrv(relation, AccessExclusiveLock); rel = heap_openrv(relation, AccessExclusiveLock);
...@@ -399,16 +401,6 @@ TruncateRelation(const RangeVar *relation) ...@@ -399,16 +401,6 @@ TruncateRelation(const RangeVar *relation)
if (!pg_class_ownercheck(relid, GetUserId())) if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); 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 * Don't allow truncate on temp tables of other backends ... their
* local buffer manager is not going to cope. * local buffer manager is not going to cope.
...@@ -438,7 +430,8 @@ TruncateRelation(const RangeVar *relation) ...@@ -438,7 +430,8 @@ TruncateRelation(const RangeVar *relation)
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
if (con->contype == 'f' && con->conrelid != relid) 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), get_rel_name(con->conrelid),
NameStr(con->conname)); NameStr(con->conname));
} }
...@@ -446,11 +439,17 @@ TruncateRelation(const RangeVar *relation) ...@@ -446,11 +439,17 @@ TruncateRelation(const RangeVar *relation)
systable_endscan(fkeyScan); systable_endscan(fkeyScan);
heap_close(fkeyRel, AccessShareLock); 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 */ /* Keep the lock until transaction commit */
heap_close(rel, NoLock); 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);
} }
/*---------- /*----------
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California * 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 @@ ...@@ -19,4 +19,9 @@
*/ */
extern void cluster(ClusterStmt *stmt); 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 */ #endif /* CLUSTER_H */
...@@ -10,7 +10,21 @@ SELECT * FROM truncate_a; ...@@ -10,7 +10,21 @@ SELECT * FROM truncate_a;
2 2
(2 rows) (2 rows)
-- Roll truncate back
BEGIN;
TRUNCATE truncate_a; 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; SELECT * FROM truncate_a;
col1 col1
------ ------
......
...@@ -3,7 +3,15 @@ CREATE TABLE truncate_a (col1 integer primary key); ...@@ -3,7 +3,15 @@ CREATE TABLE truncate_a (col1 integer primary key);
INSERT INTO truncate_a VALUES (1); INSERT INTO truncate_a VALUES (1);
INSERT INTO truncate_a VALUES (2); INSERT INTO truncate_a VALUES (2);
SELECT * FROM truncate_a; SELECT * FROM truncate_a;
-- Roll truncate back
BEGIN;
TRUNCATE truncate_a; TRUNCATE truncate_a;
ROLLBACK;
SELECT * FROM truncate_a;
-- Commit the truncate this time
BEGIN;
TRUNCATE truncate_a;
COMMIT;
SELECT * FROM truncate_a; SELECT * FROM truncate_a;
-- Test foreign constraint check -- Test foreign constraint check
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment