Diagnose !indisvalid in more SQL functions.
authorNoah Misch <[email protected]>
Mon, 30 Oct 2023 21:46:05 +0000 (14:46 -0700)
committerNoah Misch <[email protected]>
Mon, 30 Oct 2023 21:46:05 +0000 (14:46 -0700)
pgstatindex failed with ERRCODE_DATA_CORRUPTED, of the "can't-happen"
class XX.  The other functions succeeded on an empty index; they might
have malfunctioned if the failed index build left torn I/O or other
complex state.  Report an ERROR in statistics functions pgstatindex,
pgstatginindex, pgstathashindex, and pgstattuple.  Report DEBUG1 and
skip all index I/O in maintenance functions brin_desummarize_range,
brin_summarize_new_values, brin_summarize_range, and
gin_clean_pending_list.  Back-patch to v11 (all supported versions).

Discussion: https://postgr.es/m/20231001195309[email protected]

contrib/pgstattuple/pgstatindex.c
contrib/pgstattuple/pgstattuple.c
src/backend/access/brin/brin.c
src/backend/access/gin/ginfast.c

index d69ac1c93df2aa076f0a7b8520014e28346fb442..8e5a4d6a663ea7994172c2e361df5a9d2cb0fbc1 100644 (file)
@@ -237,6 +237,18 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot access temporary tables of other sessions")));
 
+   /*
+    * A !indisready index could lead to ERRCODE_DATA_CORRUPTED later, so exit
+    * early.  We're capable of assessing an indisready&&!indisvalid index,
+    * but the results could be confusing.  For example, the index's size
+    * could be too low for a valid index of the table.
+    */
+   if (!rel->rd_index->indisvalid)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(rel))));
+
    /*
     * Read metapage
     */
@@ -523,6 +535,13 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot access temporary indexes of other sessions")));
 
+   /* see pgstatindex_impl */
+   if (!rel->rd_index->indisvalid)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(rel))));
+
    /*
     * Read metapage
     */
@@ -600,6 +619,13 @@ pgstathashindex(PG_FUNCTION_ARGS)
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("cannot access temporary indexes of other sessions")));
 
+   /* see pgstatindex_impl */
+   if (!rel->rd_index->indisvalid)
+       ereport(ERROR,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(rel))));
+
    /* Get the information we need from the metapage. */
    memset(&stats, 0, sizeof(stats));
    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
index 93b7834b774a4b2ca84adda3b48991e6adb04937..3bd8b96197f86d1b3df9c26c8671825177e9aaf2 100644 (file)
@@ -259,6 +259,13 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
    }
    else if (rel->rd_rel->relkind == RELKIND_INDEX)
    {
+       /* see pgstatindex_impl */
+       if (!rel->rd_index->indisvalid)
+           ereport(ERROR,
+                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                    errmsg("index \"%s\" is not valid",
+                           RelationGetRelationName(rel))));
+
        switch (rel->rd_rel->relam)
        {
            case BTREE_AM_OID:
index af392bc032bc8506d5658c284d9087ce924ccce2..25338a90e2926676339c8d0988b6d810e24e91c6 100644 (file)
@@ -1100,8 +1100,14 @@ brin_summarize_range(PG_FUNCTION_ARGS)
                 errmsg("could not open parent table of index \"%s\"",
                        RelationGetRelationName(indexRel))));
 
-   /* OK, do it */
-   brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
+   /* see gin_clean_pending_list() */
+   if (indexRel->rd_index->indisvalid)
+       brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
+   else
+       ereport(DEBUG1,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(indexRel))));
 
    /* Roll back any GUC changes executed by index functions */
    AtEOXact_GUC(false, save_nestlevel);
@@ -1183,12 +1189,21 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
                 errmsg("could not open parent table of index \"%s\"",
                        RelationGetRelationName(indexRel))));
 
-   /* the revmap does the hard work */
-   do
+   /* see gin_clean_pending_list() */
+   if (indexRel->rd_index->indisvalid)
    {
-       done = brinRevmapDesummarizeRange(indexRel, heapBlk);
+       /* the revmap does the hard work */
+       do
+       {
+           done = brinRevmapDesummarizeRange(indexRel, heapBlk);
+       }
+       while (!done);
    }
-   while (!done);
+   else
+       ereport(DEBUG1,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(indexRel))));
 
    relation_close(indexRel, ShareUpdateExclusiveLock);
    relation_close(heapRel, ShareUpdateExclusiveLock);
index c8fe7c78a7ac61437ea6bf68316f035bd0064f49..8798fbe96352fba615421d4557a505e942dad549 100644 (file)
@@ -1033,7 +1033,6 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
    Oid         indexoid = PG_GETARG_OID(0);
    Relation    indexRel = index_open(indexoid, RowExclusiveLock);
    IndexBulkDeleteResult stats;
-   GinState    ginstate;
 
    if (RecoveryInProgress())
        ereport(ERROR,
@@ -1065,8 +1064,26 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
                       RelationGetRelationName(indexRel));
 
    memset(&stats, 0, sizeof(stats));
-   initGinState(&ginstate, indexRel);
-   ginInsertCleanup(&ginstate, true, true, true, &stats);
+
+   /*
+    * Can't assume anything about the content of an !indisready index.  Make
+    * those a no-op, not an error, so users can just run this function on all
+    * indexes of the access method.  Since an indisready&&!indisvalid index
+    * is merely awaiting missed aminsert calls, we're capable of processing
+    * it.  Decline to do so, out of an abundance of caution.
+    */
+   if (indexRel->rd_index->indisvalid)
+   {
+       GinState    ginstate;
+
+       initGinState(&ginstate, indexRel);
+       ginInsertCleanup(&ginstate, true, true, true, &stats);
+   }
+   else
+       ereport(DEBUG1,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("index \"%s\" is not valid",
+                       RelationGetRelationName(indexRel))));
 
    index_close(indexRel, RowExclusiveLock);