]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Diagnose !indisvalid in more SQL functions.
authorNoah Misch <noah@leadboat.com>
Mon, 30 Oct 2023 21:46:05 +0000 (14:46 -0700)
committerNoah Misch <noah@leadboat.com>
Mon, 30 Oct 2023 21:46:09 +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.a3@google.com

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

index db396c8c4b705eb831855b29b3242bddc05ad147..7cb89ff1633b1f38aa88342ca7edbe2687345da3 100644 (file)
@@ -236,6 +236,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
         */
@@ -537,6 +549,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
         */
@@ -614,6 +633,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 6d67bd8271c63c732360910c865956809593f18e..218b1f45cddfc30424814dada73fb881254094f5 100644 (file)
@@ -259,6 +259,13 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
                case RELKIND_SEQUENCE:
                        return pgstat_heap(rel, fcinfo);
                case 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 9cc9ef087ff286466c7bb350208601baaca1327a..fddd418c37f386fb3c9e4a2fbbb83cc5eccc5684 100644 (file)
@@ -1036,8 +1036,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);
@@ -1122,12 +1128,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 6dcc2dabafa3db3d93ffc3603c707988e99e2e9b..21a045be0a31d64f411ff6cc9b0c0cbf9a6f8b01 100644 (file)
@@ -1018,7 +1018,6 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
        Oid                     indexoid = PG_GETARG_OID(0);
        Relation        indexRel = index_open(indexoid, AccessShareLock);
        IndexBulkDeleteResult stats;
-       GinState        ginstate;
 
        if (RecoveryInProgress())
                ereport(ERROR,
@@ -1050,8 +1049,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, AccessShareLock);