Skip to content

Commit 1a368dd

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 3c6a05b commit 1a368dd

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
@@ -1102,8 +1102,14 @@ brin_summarize_range(PG_FUNCTION_ARGS)
11021102
errmsg("could not open parent table of index \"%s\"",
11031103
RelationGetRelationName(indexRel))));
11041104

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

11081114
/* Roll back any GUC changes executed by index functions */
11091115
AtEOXact_GUC(false, save_nestlevel);
@@ -1185,12 +1191,21 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
11851191
errmsg("could not open parent table of index \"%s\"",
11861192
RelationGetRelationName(indexRel))));
11871193

1188-
/* the revmap does the hard work */
1189-
do
1194+
/* see gin_clean_pending_list() */
1195+
if (indexRel->rd_index->indisvalid)
11901196
{
1191-
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1197+
/* the revmap does the hard work */
1198+
do
1199+
{
1200+
done = brinRevmapDesummarizeRange(indexRel, heapBlk);
1201+
}
1202+
while (!done);
11921203
}
1193-
while (!done);
1204+
else
1205+
ereport(DEBUG1,
1206+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1207+
errmsg("index \"%s\" is not valid",
1208+
RelationGetRelationName(indexRel))));
11941209

11951210
relation_close(indexRel, ShareUpdateExclusiveLock);
11961211
relation_close(heapRel, ShareUpdateExclusiveLock);

src/backend/access/gin/ginfast.c

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

10371036
if (RecoveryInProgress())
10381037
ereport(ERROR,
@@ -1064,8 +1063,26 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
10641063
RelationGetRelationName(indexRel));
10651064

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

10701087
index_close(indexRel, RowExclusiveLock);
10711088

0 commit comments

Comments
 (0)