? src/backend/commands/.cluster.c.swp Index: src/backend/commands/cluster.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/cluster.c,v retrieving revision 1.94 diff -c -r1.94 cluster.c *** src/backend/commands/cluster.c 2002/11/15 03:09:35 1.94 --- src/backend/commands/cluster.c 2002/11/15 20:51:27 *************** *** 20,31 **** #include "access/genam.h" #include "access/heapam.h" #include "catalog/catalog.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 "commands/cluster.h" #include "commands/tablecmds.h" #include "miscadmin.h" --- 20,32 ---- #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/namespace.h" + #include "catalog/pg_constraint.h" #include "commands/cluster.h" #include "commands/tablecmds.h" #include "miscadmin.h" *************** *** 69,74 **** --- 70,77 ---- static void cluster_rel(relToCluster *rv); static bool check_cluster_ownership(Oid relOid); static List *get_tables_to_cluster(Oid owner); + static void rebuild_rel(Oid tableOid, Oid indexOid, + List *indexes, bool dataCopy); static MemoryContext cluster_context = NULL; *************** *** 92,102 **** void cluster_rel(relToCluster *rvtc) { - Oid OIDNewHeap; Relation OldHeap, OldIndex; - char NewHeapName[NAMEDATALEN]; - ObjectAddress object; List *indexes; /* Check for user-requested abort. */ --- 95,102 ---- *************** *** 172,178 **** --- 172,194 ---- 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 * user relnames. Working around this seems more trouble than it's *************** *** 180,189 **** * 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); /* * We don't need CommandCounterIncrement() because make_new_heap did * it. --- 196,204 ---- * namespace from the old, or we will have problems with the TEMP * status of temp tables. */ ! 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,204 **** /* * Copy the heap data into the new table in the desired order. */ ! copy_heap_data(OIDNewHeap, rvtc->tableOid, rvtc->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); CommandCounterIncrement(); --- 207,220 ---- /* * Copy the heap data into the new table in the desired order. */ ! 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(tableOid, OIDNewHeap); CommandCounterIncrement(); *************** *** 219,225 **** * Recreate each index on the relation. We do not need * CommandCounterIncrement() because recreate_indexattr does it. */ ! recreate_indexattr(rvtc->tableOid, indexes); } /* --- 235,241 ---- * Recreate each index on the relation. We do not need * CommandCounterIncrement() because recreate_indexattr does it. */ ! recreate_indexattr(tableOid, indexes); } /* *************** *** 848,850 **** --- 864,960 ---- relation_close(indRelation, RowExclusiveLock); return rvs; } + + /* + * TruncateRelation + * + * Transaction safe truncate is essentially transaction safe cluster + * without the datacopy stage. Let clusters rebuild_rel() do the + * work while this function performs safety and permission checks, as + * well as lock the relation. + */ + void + TruncateRelation(const RangeVar *relation) + { + Relation rel; + Oid relid; + ScanKeyData key; + Relation fkeyRel; + SysScanDesc fkeyScan; + HeapTuple tuple; + List *indexes; + + /* Grab exclusive lock in preparation for truncate */ + rel = heap_openrv(relation, AccessExclusiveLock); + relid = RelationGetRelid(rel); + + /* Only allow truncate on regular tables */ + if (rel->rd_rel->relkind != RELKIND_RELATION) + { + /* special errors for backwards compatibility */ + if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence", + RelationGetRelationName(rel)); + if (rel->rd_rel->relkind == RELKIND_VIEW) + elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view", + RelationGetRelationName(rel)); + /* else a generic error message will do */ + elog(ERROR, "TRUNCATE can only be used on tables. '%s' is not a table", + RelationGetRelationName(rel)); + } + + /* Permissions checks */ + if (!allowSystemTableMods && IsSystemRelation(rel)) + elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table", + RelationGetRelationName(rel)); + + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, 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))) + elog(ERROR, "TRUNCATE cannot be used on temp tables of other processes"); + + /* + * Don't allow truncate on tables which are referenced by foreign keys + */ + fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); + + ScanKeyEntryInitialize(&key, 0, + Anum_pg_constraint_confrelid, + F_OIDEQ, + ObjectIdGetDatum(relid)); + + fkeyScan = systable_beginscan(fkeyRel, 0, false, + SnapshotNow, 1, &key); + + /* + * First foreign key found with us as the reference should throw an + * error. + */ + while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) + { + 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", + get_rel_name(con->conrelid), + NameStr(con->conname)); + } + + systable_endscan(fkeyScan); + heap_close(fkeyRel, AccessShareLock); + + indexes = get_indexattr_list(rel, InvalidOid); + + /* Keep the lock until transaction commit */ + heap_close(rel, NoLock); + + /* Do the real work */ + rebuild_rel(relid, NULL, indexes, false); + } + Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/tablecmds.c,v retrieving revision 1.54 diff -c -r1.54 tablecmds.c *** src/backend/commands/tablecmds.c 2002/11/15 02:50:05 1.54 --- src/backend/commands/tablecmds.c 2002/11/15 20:51:30 *************** *** 29,34 **** --- 29,35 ---- #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" *************** *** 353,456 **** object.objectSubId = 0; performDeletion(&object, behavior); - } - - /* - * TruncateRelation - * Removes all the rows from a relation. - * - * Note: This routine only does safety and permissions checks; - * heap_truncate does the actual work. - */ - void - TruncateRelation(const RangeVar *relation) - { - Relation rel; - Oid relid; - ScanKeyData key; - Relation fkeyRel; - SysScanDesc fkeyScan; - HeapTuple tuple; - - /* Grab exclusive lock in preparation for truncate */ - rel = heap_openrv(relation, AccessExclusiveLock); - relid = RelationGetRelid(rel); - - /* Only allow truncate on regular tables */ - if (rel->rd_rel->relkind != RELKIND_RELATION) - { - /* special errors for backwards compatibility */ - if (rel->rd_rel->relkind == RELKIND_SEQUENCE) - elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence", - RelationGetRelationName(rel)); - if (rel->rd_rel->relkind == RELKIND_VIEW) - elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view", - RelationGetRelationName(rel)); - /* else a generic error message will do */ - elog(ERROR, "TRUNCATE can only be used on tables. '%s' is not a table", - RelationGetRelationName(rel)); - } - - /* Permissions checks */ - if (!allowSystemTableMods && IsSystemRelation(rel)) - elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table", - RelationGetRelationName(rel)); - - 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. - */ - if (isOtherTempNamespace(RelationGetNamespace(rel))) - elog(ERROR, "TRUNCATE cannot be used on temp tables of other processes"); - - /* - * Don't allow truncate on tables which are referenced by foreign keys - */ - fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); - - ScanKeyEntryInitialize(&key, 0, - Anum_pg_constraint_confrelid, - F_OIDEQ, - ObjectIdGetDatum(relid)); - - fkeyScan = systable_beginscan(fkeyRel, 0, false, - SnapshotNow, 1, &key); - - /* - * First foreign key found with us as the reference should throw an - * error. - */ - while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) - { - 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", - get_rel_name(con->conrelid), - NameStr(con->conname)); - } - - systable_endscan(fkeyScan); - heap_close(fkeyRel, AccessShareLock); - - /* Keep the lock until transaction commit */ - heap_close(rel, NoLock); - - /* Do the real work */ - heap_truncate(relid); } /*---------- --- 354,359 ---- Index: src/include/commands/cluster.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/commands/cluster.h,v retrieving revision 1.16 diff -c -r1.16 cluster.h *** src/include/commands/cluster.h 2002/11/15 03:09:39 1.16 --- src/include/commands/cluster.h 2002/11/15 20:51:31 *************** *** 19,22 **** --- 19,24 ---- */ extern void cluster(ClusterStmt *stmt); + extern void TruncateRelation(const RangeVar *relation); + #endif /* CLUSTER_H */ Index: src/include/commands/tablecmds.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/commands/tablecmds.h,v retrieving revision 1.10 diff -c -r1.10 tablecmds.h *** src/include/commands/tablecmds.h 2002/11/11 22:19:24 1.10 --- src/include/commands/tablecmds.h 2002/11/15 20:51:31 *************** *** 51,58 **** extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior); - extern void TruncateRelation(const RangeVar *relation); - extern void renameatt(Oid myrelid, const char *oldattname, const char *newattname, --- 51,56 ---- Index: src/test/regress/expected/truncate.out =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/truncate.out,v retrieving revision 1.2 diff -c -r1.2 truncate.out *** src/test/regress/expected/truncate.out 2002/08/22 14:23:36 1.2 --- src/test/regress/expected/truncate.out 2002/11/15 20:51:32 *************** *** 10,16 **** --- 10,30 ---- 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: src/test/regress/sql/truncate.sql =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/sql/truncate.sql,v retrieving revision 1.1 diff -c -r1.1 truncate.sql *** src/test/regress/sql/truncate.sql 2002/08/22 04:51:06 1.1 --- src/test/regress/sql/truncate.sql 2002/11/15 20:51:32 *************** *** 3,9 **** --- 3,17 ---- 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