From 423a21396f1571b38424b9ef3280cdf529384f50 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Mon, 15 Jul 2019 17:51:03 -0700
Subject: [PATCH] Fix nbtree metapage cache upgrade bug.

It's not okay to update a cached copy of an indexes metapage metadata at
a point where it isn't also being updated on disk.

Discussion: https://postgr.es/m/CAH2-Wzmj6pz98qZ6+Ro-=tHvyBJ6q0yxHV8QLOr6O0mE20Nw9Q@mail.gmail.com
---
 src/backend/access/nbtree/nbtinsert.c |  3 ++-
 src/backend/access/nbtree/nbtpage.c   | 20 ++++++++++++--------
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 602f8849d4..46a1167d65 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1135,6 +1135,7 @@ _bt_insertonpg(Relation rel,
 
 			if (BufferIsValid(metabuf))
 			{
+				/* _bt_upgrademetapage() should have been called */
 				Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
 				xlmeta.version = metad->btm_version;
 				xlmeta.root = metad->btm_root;
@@ -2160,7 +2161,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
 		XLogRegisterBuffer(0, rootbuf, REGBUF_WILL_INIT);
 		XLogRegisterBuffer(1, lbuf, REGBUF_STANDARD);
 		XLogRegisterBuffer(2, metabuf, REGBUF_WILL_INIT | REGBUF_STANDARD);
-
+		/* _bt_upgrademetapage() should have been called */
 		Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
 		md.version = metad->btm_version;
 		md.root = rootblknum;
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 50455db9af..1da710ba90 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -135,14 +135,12 @@ _bt_cachemetadata(Relation rel, BTMetaPageData *input)
 	else
 	{
 		/*
-		 * Upgrade meta-data: copy available information from meta-page and
-		 * fill new fields with default values.
-		 *
-		 * Note that we cannot upgrade to version 4+ without a REINDEX, since
-		 * extensive on-disk changes are required.
+		 * Copy available information from meta-page and fill new fields from
+		 * later versions with default values defensively.  The cached version
+		 * number should not change, though.
 		 */
 		memcpy(cached_metad, input, offsetof(BTMetaPageData, btm_oldest_btpo_xact));
-		cached_metad->btm_version = BTREE_NOVAC_VERSION;
+		cached_metad->btm_version = input->btm_version;
 		cached_metad->btm_oldest_btpo_xact = InvalidTransactionId;
 		cached_metad->btm_last_cleanup_num_heap_tuples = -1.0;
 	}
@@ -241,6 +239,7 @@ _bt_update_meta_cleanup_info(Relation rel, TransactionId oldestBtpoXact,
 		XLogBeginInsert();
 		XLogRegisterBuffer(0, metabuf, REGBUF_WILL_INIT | REGBUF_STANDARD);
 
+		/* _bt_upgrademetapage() should have been called */
 		Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
 		md.version = metad->btm_version;
 		md.root = metad->btm_root;
@@ -439,6 +438,7 @@ _bt_getroot(Relation rel, int access)
 			XLogRegisterBuffer(0, rootbuf, REGBUF_WILL_INIT);
 			XLogRegisterBuffer(2, metabuf, REGBUF_WILL_INIT | REGBUF_STANDARD);
 
+			/* _bt_upgrademetapage() should have been called */
 			Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
 			md.version = metad->btm_version;
 			md.root = rootblkno;
@@ -660,7 +660,7 @@ _bt_getrootheight(Relation rel)
 		_bt_cachemetadata(rel, metad);
 		/* We shouldn't have cached it if any of these fail */
 		Assert(metad->btm_magic == BTREE_MAGIC);
-		Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
+		Assert(metad->btm_version >= BTREE_MIN_VERSION);
 		Assert(metad->btm_fastroot != P_NONE);
 		_bt_relbuf(rel, metabuf);
 	}
@@ -711,7 +711,7 @@ _bt_heapkeyspace(Relation rel)
 		_bt_cachemetadata(rel, metad);
 		/* We shouldn't have cached it if any of these fail */
 		Assert(metad->btm_magic == BTREE_MAGIC);
-		Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
+		Assert(metad->btm_version >= BTREE_MIN_VERSION);
 		Assert(metad->btm_fastroot != P_NONE);
 		_bt_relbuf(rel, metabuf);
 	}
@@ -2094,6 +2094,10 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty)
 		{
 			XLogRegisterBuffer(4, metabuf, REGBUF_WILL_INIT | REGBUF_STANDARD);
 
+			/*
+			 * _bt_upgrademetapage() should have been called when root needs
+			 * to be updated
+			 */
 			Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
 			xlmeta.version = metad->btm_version;
 			xlmeta.root = metad->btm_root;
-- 
2.17.1

