mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Transaction safe Truncate
Rod Taylor
This commit is contained in:
@ -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 @@
|
|||||||
#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
|
|||||||
|
|
||||||
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;
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
* 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)
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
#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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
|||||||
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);
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user