Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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 b1ce0d77d7378cb632ec6728ce93bb6408358b55..f8faefc60ae119c207a915dfac37921ad79a8b14 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 69179d4104d5cbf9d2b4de0966264421225ba893..e524e1622c964e9b32e000579ed4c1483a9e2ae4 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 91d49339f34179638c801d19b89af0bb535ec8a4..b5146c25dd0c9e9df38f50d10e7f7bc7749711ac 100644 (file)
@@ -1031,8 +1031,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);
@@ -1117,12 +1123,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 37f3f7ed96fe4d3411abfc49ea5949e3943c96a3..baac343a1f425aeb538e24b811db58c7a4a563ff 100644 (file)
@@ -1035,7 +1035,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,
@@ -1067,8 +1066,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);