Skip to content

Commit 975ae05

Browse files
committed
Diagnose !indisvalid in more SQL functions.
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
1 parent 3a205c9 commit 975ae05

File tree

4 files changed

+74
-9
lines changed

4 files changed

+74
-9
lines changed

contrib/pgstattuple/pgstatindex.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
237237
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
238238
errmsg("cannot access temporary tables of other sessions")));
239239

240+
/*
241+
* A !indisready index could lead to ERRCODE_DATA_CORRUPTED later, so exit
242+
* early. We're capable of assessing an indisready&&!indisvalid index,
243+
* but the results could be confusing. For example, the index's size
244+
* could be too low for a valid index of the table.
245+
*/
246+
if (!rel->rd_index->indisvalid)
247+
ereport(ERROR,
248+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
249+
errmsg("index \"%s\" is not valid",
250+
RelationGetRelationName(rel))));
251+
240252
/*
241253
* Read metapage
242254
*/
@@ -538,6 +550,13 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
538550
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
539551
errmsg("cannot access temporary indexes of other sessions")));
540552

553+
/* see pgstatindex_impl */
554+
if (!rel->rd_index->indisvalid)
555+
ereport(ERROR,
556+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
557+
errmsg("index \"%s\" is not valid",
558+
RelationGetRelationName(rel))));
559+
541560
/*
542561
* Read metapage
543562
*/
@@ -615,6 +634,13 @@ pgstathashindex(PG_FUNCTION_ARGS)
615634
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
616635
errmsg("cannot access temporary indexes of other sessions")));
617636

637+
/* see pgstatindex_impl */
638+
if (!rel->rd_index->indisvalid)
639+
ereport(ERROR,
640+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
641+
errmsg("index \"%s\" is not valid",
642+
RelationGetRelationName(rel))));
643+
618644
/* Get the information we need from the metapage. */
619645
memset(&stats, 0, sizeof(stats));
620646
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);

contrib/pgstattuple/pgstattuple.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
260260
case RELKIND_SEQUENCE:
261261
return pgstat_heap(rel, fcinfo);
262262
case RELKIND_INDEX:
263+
/* see pgstatindex_impl */
264+
if (!rel->rd_index->indisvalid)
265+
ereport(ERROR,
266+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
267+
errmsg("index \"%s\" is not valid",
268+
RelationGetRelationName(rel))));
269+
263270
switch (rel->rd_rel->relam)
264271
{
265272
case BTREE_AM_OID:

src/backend/access/brin/brin.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,8 +1039,14 @@ brin_summarize_range(PG_FUNCTION_ARGS)
10391039
errmsg("could not open parent table of index %s",
10401040
RelationGetRelationName(indexRel))));
10411041

1042-
/* OK, do it */
1043-
brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
1042+
/* see gin_clean_pending_list() */
1043+
if (indexRel->rd_index->indisvalid)
1044+
brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
1045+
else
1046+
ereport(DEBUG1,
1047+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1048+
errmsg("index \"%s\" is not valid",
1049+
RelationGetRelationName(indexRel))));
10441050

10451051
/* Roll back any GUC changes executed by index functions */
10461052
AtEOXact_GUC(false, save_nestlevel);
@@ -1125,12 +1131,21 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
11251131
errmsg("could not open parent table of index %s",
11261132
RelationGetRelationName(indexRel))));
11271133

1128-
/* the revmap does the hard work */
1129-
do
1134+
/* see gin_clean_pending_list() */
1135+
if (indexRel->rd_index->indisvalid)
11301136
{
1131-
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1137+
/* the revmap does the hard work */
1138+
do
1139+
{
1140+
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1141+
}
1142+
while (!done);
11321143
}
1133-
while (!done);
1144+
else
1145+
ereport(DEBUG1,
1146+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1147+
errmsg("index \"%s\" is not valid",
1148+
RelationGetRelationName(indexRel))));
11341149

11351150
relation_close(indexRel, ShareUpdateExclusiveLock);
11361151
relation_close(heapRel, ShareUpdateExclusiveLock);

src/backend/access/gin/ginfast.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,6 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
10411041
Oid indexoid = PG_GETARG_OID(0);
10421042
Relation indexRel = index_open(indexoid, RowExclusiveLock);
10431043
IndexBulkDeleteResult stats;
1044-
GinState ginstate;
10451044

10461045
if (RecoveryInProgress())
10471046
ereport(ERROR,
@@ -1073,8 +1072,26 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
10731072
RelationGetRelationName(indexRel));
10741073

10751074
memset(&stats, 0, sizeof(stats));
1076-
initGinState(&ginstate, indexRel);
1077-
ginInsertCleanup(&ginstate, true, true, true, &stats);
1075+
1076+
/*
1077+
* Can't assume anything about the content of an !indisready index. Make
1078+
* those a no-op, not an error, so users can just run this function on all
1079+
* indexes of the access method. Since an indisready&&!indisvalid index
1080+
* is merely awaiting missed aminsert calls, we're capable of processing
1081+
* it. Decline to do so, out of an abundance of caution.
1082+
*/
1083+
if (indexRel->rd_index->indisvalid)
1084+
{
1085+
GinState ginstate;
1086+
1087+
initGinState(&ginstate, indexRel);
1088+
ginInsertCleanup(&ginstate, true, true, true, &stats);
1089+
}
1090+
else
1091+
ereport(DEBUG1,
1092+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1093+
errmsg("index \"%s\" is not valid",
1094+
RelationGetRelationName(indexRel))));
10781095

10791096
index_close(indexRel, RowExclusiveLock);
10801097

0 commit comments

Comments
 (0)