diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7223679033..76fd938ce3 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -3474,6 +3474,17 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot reindex temporary tables of other sessions"))); + /* + * Don't allow reindex of an invalid index on TOAST table. This is a + * leftover from a failed REINDEX CONCURRENTLY, and if rebuilt it would + * not be possible to drop it anymore. + */ + if (IsToastNamespace(RelationGetNamespace(iRel)) && + !get_index_isvalid(indexId)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot reindex invalid index on TOAST table"))); + /* * Also check for active uses of the index in the current transaction; we * don't want to reindex underneath an open indexscan. @@ -3723,6 +3734,23 @@ reindex_relation(Oid relid, int flags, int options) foreach(indexId, indexIds) { Oid indexOid = lfirst_oid(indexId); + Oid indexNamespaceId = get_rel_namespace(indexOid); + + /* + * Skip any invalid indexes on a TOAST table. These can only be + * duplicate leftovers from a failed REINDEX CONCURRENTLY, and if + * rebuilt it would not be possible to drop them anymore. + */ + if (IsToastNamespace(indexNamespaceId) && + !get_index_isvalid(indexOid)) + { + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot reindex invalid index \"%s.%s\" on TOAST table, skipping", + get_namespace_name(indexNamespaceId), + get_rel_name(indexOid)))); + continue; + } reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS), persistence, options); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 3f3a89fe92..6d696dd672 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -2868,6 +2868,16 @@ ReindexRelationConcurrently(Oid relationOid, int options) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot reindex system catalogs concurrently"))); + /* + * Don't allow reindex for an invalid index on TOAST table, as + * if rebuild it would not be possible to drop it. + */ + if (IsToastNamespace(get_rel_namespace(relationOid)) && + !get_index_isvalid(relationOid)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot reindex invalid index on TOAST table concurrently"))); + /* Save the list of relation OIDs in private context */ oldcontext = MemoryContextSwitchTo(private_context); diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 3da90cb72a..400e7689fe 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3227,3 +3227,26 @@ get_index_column_opclass(Oid index_oid, int attno) return opclass; } + +/* + * get_index_isvalid + * + * Given the index OID, return pg_index.indisvalid. + */ +bool +get_index_isvalid(Oid index_oid) +{ + bool isvalid; + HeapTuple tuple; + Form_pg_index rd_index; + + tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for index %u", index_oid); + + rd_index = (Form_pg_index) GETSTRUCT(tuple); + isvalid = rd_index->indisvalid; + ReleaseSysCache(tuple); + + return isvalid; +} diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index f132d39458..131d10eab0 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -181,6 +181,7 @@ extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); extern Oid get_index_column_opclass(Oid index_oid, int attno); +extern bool get_index_isvalid(Oid index_oid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */