amcheck: Skip unlogged relations during recovery.
authorPeter Geoghegan <pg@bowt.ie>
Mon, 12 Aug 2019 22:21:32 +0000 (15:21 -0700)
committerPeter Geoghegan <pg@bowt.ie>
Mon, 12 Aug 2019 22:21:32 +0000 (15:21 -0700)
contrib/amcheck failed to consider the possibility that unlogged
relations will not have any main relation fork files when running in hot
standby mode.  This led to low-level "can't happen" errors that complain
about the absence of a relfilenode file.

To fix, simply skip verification of unlogged index relations during
recovery.  In passing, add a direct check for the presence of a main
fork just before verification proper begins, so that we cleanly verify
the presence of the main relation fork file.

Author: Andrey Borodin, Peter Geoghegan
Reported-By: Andrey Borodin
Diagnosed-By: Andrey Borodin
Discussion: https://postgr.es/m/DA9B33AC-53CB-4643-96D4-7A0BBC037FA1@yandex-team.ru
Backpatch: 10-, where amcheck was introduced.

contrib/amcheck/verify_nbtree.c

index 55a3a4bbe0c2a0b967243bfd8038eb91d6d89a62..05e7d678ed4baeb73a265ecb49659fb897afe9e6 100644 (file)
@@ -35,6 +35,7 @@
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
+#include "storage/smgr.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
 
@@ -128,6 +129,7 @@ PG_FUNCTION_INFO_V1(bt_index_parent_check);
 static void bt_index_check_internal(Oid indrelid, bool parentcheck,
                                                                        bool heapallindexed, bool rootdescend);
 static inline void btree_index_checkable(Relation rel);
+static inline bool btree_index_mainfork_expected(Relation rel);
 static void bt_check_every_level(Relation rel, Relation heaprel,
                                                                 bool heapkeyspace, bool readonly, bool heapallindexed,
                                                                 bool rootdescend);
@@ -225,7 +227,6 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed,
        Oid                     heapid;
        Relation        indrel;
        Relation        heaprel;
-       bool            heapkeyspace;
        LOCKMODE        lockmode;
 
        if (parentcheck)
@@ -275,10 +276,22 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed,
        /* Relation suitable for checking as B-Tree? */
        btree_index_checkable(indrel);
 
-       /* Check index, possibly against table it is an index on */
-       heapkeyspace = _bt_heapkeyspace(indrel);
-       bt_check_every_level(indrel, heaprel, heapkeyspace, parentcheck,
-                                                heapallindexed, rootdescend);
+       if (btree_index_mainfork_expected(indrel))
+       {
+               bool    heapkeyspace;
+
+               RelationOpenSmgr(indrel);
+               if (!smgrexists(indrel->rd_smgr, MAIN_FORKNUM))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INDEX_CORRUPTED),
+                                        errmsg("index \"%s\" lacks a main relation fork",
+                                                       RelationGetRelationName(indrel))));
+
+               /* Check index, possibly against table it is an index on */
+               heapkeyspace = _bt_heapkeyspace(indrel);
+               bt_check_every_level(indrel, heaprel, heapkeyspace, parentcheck,
+                                                        heapallindexed, rootdescend);
+       }
 
        /*
         * Release locks early. That's ok here because nothing in the called
@@ -324,6 +337,28 @@ btree_index_checkable(Relation rel)
                                 errdetail("Index is not valid.")));
 }
 
+/*
+ * Check if B-Tree index relation should have a file for its main relation
+ * fork.  Verification uses this to skip unlogged indexes when in hot standby
+ * mode, where there is simply nothing to verify.
+ *
+ * NB: Caller should call btree_index_checkable() before calling here.
+ */
+static inline bool
+btree_index_mainfork_expected(Relation rel)
+{
+       if (rel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED ||
+               !RecoveryInProgress())
+               return true;
+
+       ereport(NOTICE,
+                       (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
+                        errmsg("cannot verify unlogged index \"%s\" during recovery, skipping",
+                                       RelationGetRelationName(rel))));
+
+       return false;
+}
+
 /*
  * Main entry point for B-Tree SQL-callable functions. Walks the B-Tree in
  * logical order, verifying invariants as it goes.  Optionally, verification