From 1331cc6c1ad2beba7985523508d6722dc865c337 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 11 May 2012 09:46:42 -0400
Subject: [PATCH] Prevent loss of init fork when truncating an unlogged table.

Fixes bug #6635, reported by Akira Kurosawa.
---
 src/backend/catalog/heap.c       | 32 +++++++++++++++++---------------
 src/backend/commands/tablecmds.c |  4 ++++
 src/include/catalog/heap.h       |  2 ++
 3 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d029a25c11a..2734ec86582 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1306,24 +1306,10 @@ heap_create_with_catalog(const char *relname,
 	if (oncommit != ONCOMMIT_NOOP)
 		register_on_commit_action(relid, oncommit);
 
-	/*
-	 * If this is an unlogged relation, it needs an init fork so that it can
-	 * be correctly reinitialized on restart.  Since we're going to do an
-	 * immediate sync, we only need to xlog this if archiving or streaming is
-	 * enabled.  And the immediate sync is required, because otherwise there's
-	 * no guarantee that this will hit the disk before the next checkpoint
-	 * moves the redo pointer.
-	 */
 	if (relpersistence == RELPERSISTENCE_UNLOGGED)
 	{
 		Assert(relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE);
-
-		RelationOpenSmgr(new_rel_desc);
-		smgrcreate(new_rel_desc->rd_smgr, INIT_FORKNUM, false);
-		if (XLogIsNeeded())
-			log_smgrcreate(&new_rel_desc->rd_smgr->smgr_rnode.node,
-						   INIT_FORKNUM);
-		smgrimmedsync(new_rel_desc->rd_smgr, INIT_FORKNUM);
+		heap_create_init_fork(new_rel_desc);
 	}
 
 	/*
@@ -1336,6 +1322,22 @@ heap_create_with_catalog(const char *relname,
 	return relid;
 }
 
+/*
+ * Set up an init fork for an unlogged table so that it can be correctly
+ * reinitialized on restart.  Since we're going to do an immediate sync, we
+ * only need to xlog this if archiving or streaming is enabled.  And the
+ * immediate sync is required, because otherwise there's no guarantee that
+ * this will hit the disk before the next checkpoint moves the redo pointer.
+ */
+void
+heap_create_init_fork(Relation rel)
+{
+	RelationOpenSmgr(rel);
+	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
+	if (XLogIsNeeded())
+		log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	smgrimmedsync(rel->rd_smgr, INIT_FORKNUM);
+}
 
 /*
  *		RelationRemoveInheritance
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c94d9457223..e23a3dab3cd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1155,6 +1155,8 @@ ExecuteTruncate(TruncateStmt *stmt)
 			 * deletion at commit.
 			 */
 			RelationSetNewRelfilenode(rel, RecentXmin);
+			if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+				heap_create_init_fork(rel);
 
 			heap_relid = RelationGetRelid(rel);
 			toast_relid = rel->rd_rel->reltoastrelid;
@@ -1166,6 +1168,8 @@ ExecuteTruncate(TruncateStmt *stmt)
 			{
 				rel = relation_open(toast_relid, AccessExclusiveLock);
 				RelationSetNewRelfilenode(rel, RecentXmin);
+				if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+					heap_create_init_fork(rel);
 				heap_close(rel, NoLock);
 			}
 
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index c0deab73ae6..bc98d5d6fc1 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -69,6 +69,8 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 bool use_user_acl,
 						 bool allow_system_table_mods);
 
+extern void heap_create_init_fork(Relation rel);
+
 extern void heap_drop_with_catalog(Oid relid);
 
 extern void heap_truncate(List *relids);
-- 
GitLab