From 0a64b45152b593c5eb95f2e88fbce7fbfe84ae7b Mon Sep 17 00:00:00 2001
From: Teodor Sigaev <teodor@sigaev.ru>
Date: Thu, 5 Apr 2018 17:56:00 +0300
Subject: [PATCH] Fix handling of non-upgraded B-tree metapages

857f9c36 bumps B-tree metapage version while upgrade is performed "on the fly"
when needed. However, some asserts fired when old version metapage was
cached to rel->rd_amcache. Despite new metadata fields are never used from
rel->rd_amcache, that needs to be fixed. This patch introduces metadata
upgrade during its caching, which fills unavailable fields with their default
values. contrib/pageinspect is also patched to handle non-upgraded metapages
in the same way.

Author: Alexander Korotkov
---
 contrib/pageinspect/btreefuncs.c    | 17 +++++++++--
 src/backend/access/nbtree/nbtpage.c | 47 +++++++++++++++++++++++++----
 2 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 5133653791..558a8c41f4 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -555,8 +555,21 @@ bt_metap(PG_FUNCTION_ARGS)
 	values[j++] = psprintf("%d", metad->btm_level);
 	values[j++] = psprintf("%d", metad->btm_fastroot);
 	values[j++] = psprintf("%d", metad->btm_fastlevel);
-	values[j++] = psprintf("%u", metad->btm_oldest_btpo_xact);
-	values[j++] = psprintf("%lf", metad->btm_last_cleanup_num_heap_tuples);
+
+	/*
+	 * Get values of extended metadata if available, use default values
+	 * otherwise.
+	 */
+	if (metad->btm_version == BTREE_VERSION)
+	{
+		values[j++] = psprintf("%u", metad->btm_oldest_btpo_xact);
+		values[j++] = psprintf("%lf", metad->btm_last_cleanup_num_heap_tuples);
+	}
+	else
+	{
+		values[j++] = "0";
+		values[j++] = "-1";
+	}
 
 	tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 								   values);
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index c98603bbf8..019fe48cb6 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -33,6 +33,7 @@
 #include "storage/predicate.h"
 #include "utils/snapmgr.h"
 
+static void _bt_cachemetadata(Relation rel, BTMetaPageData *metad);
 static bool _bt_mark_page_halfdead(Relation rel, Buffer buf, BTStack stack);
 static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf,
 						 bool *rightsib_empty);
@@ -105,6 +106,44 @@ _bt_upgrademetapage(Page page)
 		((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
 }
 
+/*
+ * Cache metadata from meta page to rel->rd_amcache.
+ */
+static void
+_bt_cachemetadata(Relation rel, BTMetaPageData *metad)
+{
+	/* We assume rel->rd_amcache was already freed by caller */
+	Assert(rel->rd_amcache == NULL);
+	rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt,
+										 sizeof(BTMetaPageData));
+
+	/*
+	 * Meta page should be of supported version (should be already checked by
+	 * caller).
+	 */
+	Assert(metad->btm_version >= BTREE_MIN_VERSION &&
+		   metad->btm_version <= BTREE_VERSION);
+
+	if (metad->btm_version == BTREE_VERSION)
+	{
+		/* Last version of meta-data, no need to upgrade */
+		memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
+	}
+	else
+	{
+		BTMetaPageData *cached_metad = (BTMetaPageData *) rel->rd_amcache;
+
+		/*
+		 * Upgrade meta-data: copy available information from meta-page and
+		 * fill new fields with default values.
+		 */
+		memcpy(rel->rd_amcache, metad, offsetof(BTMetaPageData, btm_oldest_btpo_xact));
+		cached_metad->btm_version = BTREE_VERSION;
+		cached_metad->btm_oldest_btpo_xact = InvalidTransactionId;
+		cached_metad->btm_last_cleanup_num_heap_tuples = -1.0;
+	}
+}
+
 /*
  *	_bt_update_meta_cleanup_info() -- Update cleanup-related information in
  *									  the metapage.
@@ -403,9 +442,7 @@ _bt_getroot(Relation rel, int access)
 		/*
 		 * Cache the metapage data for next time
 		 */
-		rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt,
-											 sizeof(BTMetaPageData));
-		memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
+		_bt_cachemetadata(rel, metad);
 
 		/*
 		 * We are done with the metapage; arrange to release it via first
@@ -604,9 +641,7 @@ _bt_getrootheight(Relation rel)
 		/*
 		 * Cache the metapage data for next time
 		 */
-		rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt,
-											 sizeof(BTMetaPageData));
-		memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
+		_bt_cachemetadata(rel, metad);
 
 		_bt_relbuf(rel, metabuf);
 	}