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
This commit is contained in:
Noah Misch 2023-10-30 14:46:05 -07:00
parent 6ec9e9975e
commit 13503eb590
4 changed files with 74 additions and 9 deletions

View File

@ -237,6 +237,18 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary tables of other sessions"))); 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 * Read metapage
*/ */
@ -523,6 +535,13 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary indexes of other sessions"))); 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 * Read metapage
*/ */
@ -600,6 +619,13 @@ pgstathashindex(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary indexes of other sessions"))); 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. */ /* Get the information we need from the metapage. */
memset(&stats, 0, sizeof(stats)); memset(&stats, 0, sizeof(stats));
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);

View File

@ -259,6 +259,13 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
} }
else if (rel->rd_rel->relkind == RELKIND_INDEX) else if (rel->rd_rel->relkind == 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) switch (rel->rd_rel->relam)
{ {
case BTREE_AM_OID: case BTREE_AM_OID:

View File

@ -1100,8 +1100,14 @@ brin_summarize_range(PG_FUNCTION_ARGS)
errmsg("could not open parent table of index \"%s\"", errmsg("could not open parent table of index \"%s\"",
RelationGetRelationName(indexRel)))); RelationGetRelationName(indexRel))));
/* OK, do it */ /* see gin_clean_pending_list() */
brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL); 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 */ /* Roll back any GUC changes executed by index functions */
AtEOXact_GUC(false, save_nestlevel); AtEOXact_GUC(false, save_nestlevel);
@ -1183,12 +1189,21 @@ brin_desummarize_range(PG_FUNCTION_ARGS)
errmsg("could not open parent table of index \"%s\"", errmsg("could not open parent table of index \"%s\"",
RelationGetRelationName(indexRel)))); RelationGetRelationName(indexRel))));
/* the revmap does the hard work */ /* see gin_clean_pending_list() */
do 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(indexRel, ShareUpdateExclusiveLock);
relation_close(heapRel, ShareUpdateExclusiveLock); relation_close(heapRel, ShareUpdateExclusiveLock);

View File

@ -1033,7 +1033,6 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
Oid indexoid = PG_GETARG_OID(0); Oid indexoid = PG_GETARG_OID(0);
Relation indexRel = index_open(indexoid, RowExclusiveLock); Relation indexRel = index_open(indexoid, RowExclusiveLock);
IndexBulkDeleteResult stats; IndexBulkDeleteResult stats;
GinState ginstate;
if (RecoveryInProgress()) if (RecoveryInProgress())
ereport(ERROR, ereport(ERROR,
@ -1065,8 +1064,26 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
RelationGetRelationName(indexRel)); RelationGetRelationName(indexRel));
memset(&stats, 0, sizeof(stats)); 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); index_close(indexRel, RowExclusiveLock);