summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2009-08-23 19:23:41 +0000
committerTom Lane2009-08-23 19:23:41 +0000
commitac3017349f2517dff44885fd26821a812a440c25 (patch)
treeb3b884f368a11a0ed6a284dff3603bfccf0110fa
parentd44de130b908e0568213fcfb9ccd8cdd35d82e48 (diff)
Make TRUNCATE do truncate-in-place when processing a relation that was created
or previously truncated in the current (sub)transaction. This is safe since if the (sub)transaction later rolls back, we'd just discard the rel's current physical file anyway. This avoids unreasonable growth in the number of transient files when a relation is repeatedly truncated. Per a performance gripe a couple weeks ago from Todd Cook.
-rw-r--r--src/backend/catalog/heap.c53
-rw-r--r--src/backend/commands/tablecmds.c65
-rw-r--r--src/include/catalog/heap.h2
3 files changed, 82 insertions, 38 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3c0133bcbd..ae406ae6ab 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2342,18 +2342,9 @@ heap_truncate(List *relids)
{
Oid rid = lfirst_oid(cell);
Relation rel;
- Oid toastrelid;
rel = heap_open(rid, AccessExclusiveLock);
relations = lappend(relations, rel);
-
- /* If there is a toast table, add it to the list too */
- toastrelid = rel->rd_rel->reltoastrelid;
- if (OidIsValid(toastrelid))
- {
- rel = heap_open(toastrelid, AccessExclusiveLock);
- relations = lappend(relations, rel);
- }
}
/* Don't allow truncate on tables that are referenced by foreign keys */
@@ -2364,20 +2355,48 @@ heap_truncate(List *relids)
{
Relation rel = lfirst(cell);
- /* Truncate the actual file (and discard buffers) */
- RelationTruncate(rel, 0);
+ /* Truncate the relation */
+ heap_truncate_one_rel(rel);
- /* If this relation has indexes, truncate the indexes too */
- RelationTruncateIndexes(rel);
-
- /*
- * Close the relation, but keep exclusive lock on it until commit.
- */
+ /* Close the relation, but keep exclusive lock on it until commit */
heap_close(rel, NoLock);
}
}
/*
+ * heap_truncate_one_rel
+ *
+ * This routine deletes all data within the specified relation.
+ *
+ * This is not transaction-safe, because the truncation is done immediately
+ * and cannot be rolled back later. Caller is responsible for having
+ * checked permissions etc, and must have obtained AccessExclusiveLock.
+ */
+void
+heap_truncate_one_rel(Relation rel)
+{
+ Oid toastrelid;
+
+ /* Truncate the actual file (and discard buffers) */
+ RelationTruncate(rel, 0);
+
+ /* If the relation has indexes, truncate the indexes too */
+ RelationTruncateIndexes(rel);
+
+ /* If there is a toast table, truncate that too */
+ toastrelid = rel->rd_rel->reltoastrelid;
+ if (OidIsValid(toastrelid))
+ {
+ Relation toastrel = heap_open(toastrelid, AccessExclusiveLock);
+
+ RelationTruncate(toastrel, 0);
+ RelationTruncateIndexes(toastrel);
+ /* keep the lock... */
+ heap_close(toastrel, NoLock);
+ }
+}
+
+/*
* heap_truncate_check_FKs
* Check for foreign keys referencing a list of relations that
* are to be truncated, and raise error if there are any
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7029c3c1e8..89dd19b264 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -775,6 +775,7 @@ ExecuteTruncate(TruncateStmt *stmt)
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
+ SubTransactionId mySubid;
ListCell *cell;
/*
@@ -944,36 +945,58 @@ ExecuteTruncate(TruncateStmt *stmt)
/*
* OK, truncate each table.
*/
+ mySubid = GetCurrentSubTransactionId();
+
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);
- Oid heap_relid;
- Oid toast_relid;
/*
- * Create a new empty storage file for the relation, and assign it as
- * the relfilenode value. The old storage file is scheduled for
- * deletion at commit.
+ * Normally, we need a transaction-safe truncation here. However,
+ * if the table was either created in the current (sub)transaction
+ * or has a new relfilenode in the current (sub)transaction, then
+ * we can just truncate it in-place, because a rollback would
+ * cause the whole table or the current physical file to be
+ * thrown away anyway.
*/
- setNewRelfilenode(rel, RecentXmin);
-
- heap_relid = RelationGetRelid(rel);
- toast_relid = rel->rd_rel->reltoastrelid;
-
- /*
- * The same for the toast table, if any.
- */
- if (OidIsValid(toast_relid))
+ if (rel->rd_createSubid == mySubid ||
+ rel->rd_newRelfilenodeSubid == mySubid)
{
- rel = relation_open(toast_relid, AccessExclusiveLock);
- setNewRelfilenode(rel, RecentXmin);
- heap_close(rel, NoLock);
+ /* Immediate, non-rollbackable truncation is OK */
+ heap_truncate_one_rel(rel);
}
+ else
+ {
+ Oid heap_relid;
+ Oid toast_relid;
- /*
- * Reconstruct the indexes to match, and we're done.
- */
- reindex_relation(heap_relid, true);
+ /*
+ * Need the full transaction-safe pushups.
+ *
+ * Create a new empty storage file for the relation, and assign it
+ * as the relfilenode value. The old storage file is scheduled for
+ * deletion at commit.
+ */
+ setNewRelfilenode(rel, RecentXmin);
+
+ heap_relid = RelationGetRelid(rel);
+ toast_relid = rel->rd_rel->reltoastrelid;
+
+ /*
+ * The same for the toast table, if any.
+ */
+ if (OidIsValid(toast_relid))
+ {
+ rel = relation_open(toast_relid, AccessExclusiveLock);
+ setNewRelfilenode(rel, RecentXmin);
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * Reconstruct the indexes to match, and we're done.
+ */
+ reindex_relation(heap_relid, true);
+ }
}
/*
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index d618319e98..d7fdc9b1f9 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -62,6 +62,8 @@ extern void heap_drop_with_catalog(Oid relid);
extern void heap_truncate(List *relids);
+extern void heap_truncate_one_rel(Relation rel);
+
extern void heap_truncate_check_FKs(List *relations, bool tempTables);
extern List *heap_truncate_find_FKs(List *relationIds);