]> 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 2c807537266c183111beb146a91c916fb1ef608a..ff099727f6fc448ca6fcb2aedc127d8d68de64b5 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
         */
@@ -538,6 +550,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
         */
@@ -615,6 +634,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 70af43ebd5a6bdf67c109b4e1d7774e1fbc15f51..e063ad83ff4076b8c409fc9846ff136cc817f0b6 100644 (file)
@@ -260,6 +260,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 eb318303c7d90128e8a11a298e4a33a11c7c986d..81f819ecc45d6125c092857b83ba9f6993c3c061 100644 (file)
@@ -1039,8 +1039,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);
@@ -1125,12 +1131,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 4dcfdf2ac631dd870a92caa0a348f399922f2bbe..39eb9e14cd49c8ee288ac5c44a47a5e370e58d8d 100644 (file)
@@ -1041,7 +1041,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,
@@ -1073,8 +1072,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);