Skip to content

Commit 13503eb

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 6ec9e99 commit 13503eb

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
*/
@@ -523,6 +535,13 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
523535
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
524536
errmsg("cannot access temporary indexes of other sessions")));
525537

538+
/* see pgstatindex_impl */
539+
if (!rel->rd_index->indisvalid)
540+
ereport(ERROR,
541+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
542+
errmsg("index \"%s\" is not valid",
543+
RelationGetRelationName(rel))));
544+
526545
/*
527546
* Read metapage
528547
*/
@@ -600,6 +619,13 @@ pgstathashindex(PG_FUNCTION_ARGS)
600619
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
601620
errmsg("cannot access temporary indexes of other sessions")));
602621

622+
/* see pgstatindex_impl */
623+
if (!rel->rd_index->indisvalid)
624+
ereport(ERROR,
625+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
626+
errmsg("index \"%s\" is not valid",
627+
RelationGetRelationName(rel))));
628+
603629
/* Get the information we need from the metapage. */
604630
memset(&stats, 0, sizeof(stats));
605631
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
@@ -259,6 +259,13 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
259259
}
260260
else if (rel->rd_rel->relkind == RELKIND_INDEX)
261261
{
262+
/* see pgstatindex_impl */
263+
if (!rel->rd_index->indisvalid)
264+
ereport(ERROR,
265+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
266+
errmsg("index \"%s\" is not valid",
267+
RelationGetRelationName(rel))));
268+
262269
switch (rel->rd_rel->relam)
263270
{
264271
case BTREE_AM_OID:

src/backend/access/brin/brin.c

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

1103-
/* OK, do it */
1104-
brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
1103+
/* see gin_clean_pending_list() */
1104+
if (indexRel->rd_index->indisvalid)
1105+
brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL);
1106+
else
1107+
ereport(DEBUG1,
1108+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1109+
errmsg("index \"%s\" is not valid",
1110+
RelationGetRelationName(indexRel))));
11051111

11061112
/* Roll back any GUC changes executed by index functions */
11071113
AtEOXact_GUC(false, save_nestlevel);
@@ -1183,12 +1189,21 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
11831189
errmsg("could not open parent table of index \"%s\"",
11841190
RelationGetRelationName(indexRel))));
11851191

1186-
/* the revmap does the hard work */
1187-
do
1192+
/* see gin_clean_pending_list() */
1193+
if (indexRel->rd_index->indisvalid)
11881194
{
1189-
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1195+
/* the revmap does the hard work */
1196+
do
1197+
{
1198+
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1199+
}
1200+
while (!done);
11901201
}
1191-
while (!done);
1202+
else
1203+
ereport(DEBUG1,
1204+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1205+
errmsg("index \"%s\" is not valid",
1206+
RelationGetRelationName(indexRel))));
11921207

11931208
relation_close(indexRel, ShareUpdateExclusiveLock);
11941209
relation_close(heapRel, ShareUpdateExclusiveLock);

src/backend/access/gin/ginfast.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,6 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
10331033
Oid indexoid = PG_GETARG_OID(0);
10341034
Relation indexRel = index_open(indexoid, RowExclusiveLock);
10351035
IndexBulkDeleteResult stats;
1036-
GinState ginstate;
10371036

10381037
if (RecoveryInProgress())
10391038
ereport(ERROR,
@@ -1065,8 +1064,26 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
10651064
RelationGetRelationName(indexRel));
10661065

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

10711088
index_close(indexRel, RowExclusiveLock);
10721089

0 commit comments

Comments
 (0)