v23-only-part-3-0004-Track-and-drop-auxiliary-indexes-in-.patch_
application/octet-stream
Filename: v23-only-part-3-0004-Track-and-drop-auxiliary-indexes-in-.patch_
Type: application/octet-stream
Part: 13
From 302f42e0d666c8034ab75309ef7c1810d191b050 Mon Sep 17 00:00:00 2001
From: Mikhail Nikalayeu <mihailnikalayeu@gmail.com>
Date: Tue, 31 Dec 2024 14:36:31 +0100
Subject: [PATCH v23-only-part-3 4/6] Track and drop auxiliary indexes in
DROP/REINDEX
During concurrent index operations, auxiliary indexes may be left as orphaned objects when errors occur (junk auxiliary indexes).
This patch improves the handling of such auxiliary indexes:
- add auxiliaryForIndexId parameter to index_create() to track dependencies between main and auxiliary indexes
- automatically drop auxiliary indexes when the main index is dropped
- delete junk auxiliary indexes properly during REINDEX operations
---
doc/src/sgml/ref/create_index.sgml | 14 ++-
doc/src/sgml/ref/reindex.sgml | 8 +-
src/backend/catalog/dependency.c | 2 +-
src/backend/catalog/index.c | 71 ++++++++++----
src/backend/catalog/pg_depend.c | 57 +++++++++++
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 38 +++++++-
src/backend/commands/tablecmds.c | 48 +++++++++-
src/backend/nodes/makefuncs.c | 3 +-
src/include/catalog/dependency.h | 1 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/makefuncs.h | 2 +-
src/test/regress/expected/create_index.out | 105 +++++++++++++++++++--
src/test/regress/sql/create_index.sql | 57 ++++++++++-
14 files changed, 367 insertions(+), 42 deletions(-)
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 30db079c8d8..cf14f474946 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -668,10 +668,16 @@ Indexes:
"idx_ccaux" stir (col) INVALID
</programlisting>
- The recommended recovery
- method in such cases is to drop these indexes and try again to perform
- <command>CREATE INDEX CONCURRENTLY</command>. (Another possibility is
- to rebuild the index with <command>REINDEX INDEX CONCURRENTLY</command>).
+ The recommended recovery method in such cases is to drop the index with
+ <command>DROP INDEX</command>. The auxiliary index (suffixed with
+ <literal>ccaux</literal>) will be automatically dropped when the main
+ index is dropped. After dropping the indexes, you can try again to perform
+ <command>CREATE INDEX CONCURRENTLY</command>. (Another possibility is to
+ rebuild the index with <command>REINDEX INDEX CONCURRENTLY</command>,
+ which will also handle cleanup of any invalid auxiliary indexes.)
+ If the only invalid index is one suffixed <literal>ccaux</literal>
+ recommended recovery method is just <literal>DROP INDEX</literal>
+ for that index.
</para>
<para>
diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml
index 4ed3c969012..d62791ff9c3 100644
--- a/doc/src/sgml/ref/reindex.sgml
+++ b/doc/src/sgml/ref/reindex.sgml
@@ -477,11 +477,15 @@ Indexes:
<literal>_ccnew</literal> or <literal>_ccaux</literal>, then it corresponds to the transient or auxiliary
index created during the concurrent operation, and the recommended
recovery method is to drop these indexes using <literal>DROP INDEX</literal>,
- then attempt <command>REINDEX CONCURRENTLY</command> again.
+ then attempt <command>REINDEX CONCURRENTLY</command> again. The auxiliary index
+ (suffixed with <literal>_ccaux</literal>) will be automatically dropped
+ along with its main index.
If the invalid index is instead suffixed <literal>_ccold</literal>,
it corresponds to the original index which could not be dropped;
the recommended recovery method is to just drop said index, since the
- rebuild proper has been successful.
+ rebuild proper has been successful. If the only
+ invalid index is one suffixed <literal>ccaux</literal> recommended
+ recovery method is just <literal>DROP INDEX</literal> for that index.
A nonzero number may be appended to the suffix of the invalid index
names to keep them unique, like <literal>_ccnew1</literal>,
<literal>_ccold2</literal>, etc.
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 7dded634eb8..b579d26aff2 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -286,7 +286,7 @@ performDeletion(const ObjectAddress *object,
* Acquire deletion lock on the target object. (Ideally the caller has
* done this already, but many places are sloppy about it.)
*/
- AcquireDeletionLock(object, 0);
+ AcquireDeletionLock(object, flags);
/*
* Construct a list of objects to delete (ie, the given object plus
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 31cb4885d58..db7802a9a2c 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -775,6 +775,8 @@ index_create(Relation heapRelation,
((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0));
/* partitioned indexes must never be "built" by themselves */
Assert(!partitioned || (flags & INDEX_CREATE_SKIP_BUILD));
+ /* ii_AuxiliaryForIndexId and INDEX_CREATE_AUXILIARY are required both or neither */
+ Assert(OidIsValid(indexInfo->ii_AuxiliaryForIndexId) == auxiliary);
relkind = partitioned ? RELKIND_PARTITIONED_INDEX : RELKIND_INDEX;
is_exclusion = (indexInfo->ii_ExclusionOps != NULL);
@@ -1180,6 +1182,15 @@ index_create(Relation heapRelation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
+ /*
+ * Record dependency on the main index in case of auxiliary index.
+ */
+ if (OidIsValid(indexInfo->ii_AuxiliaryForIndexId))
+ {
+ ObjectAddressSet(referenced, RelationRelationId, indexInfo->ii_AuxiliaryForIndexId);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+
/* placeholder for normal dependencies */
addrs = new_object_addresses();
@@ -1412,7 +1423,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId,
true,
indexRelation->rd_indam->amsummarizing,
oldInfo->ii_WithoutOverlaps,
- false);
+ false,
+ InvalidOid);
/*
* Extract the list of column names and the column numbers for the new
@@ -1580,7 +1592,8 @@ index_concurrently_create_aux(Relation heapRelation, Oid mainIndexId,
true,
false, /* aux are not summarizing */
false, /* aux are not without overlaps */
- true /* auxiliary */);
+ true /* auxiliary */,
+ mainIndexId /* auxiliaryForIndexId */);
/*
* Extract the list of column names and the column numbers for the new
@@ -2616,7 +2629,8 @@ BuildIndexInfo(Relation index)
false,
index->rd_indam->amsummarizing,
indexStruct->indisexclusion && indexStruct->indisunique,
- index->rd_rel->relam == STIR_AM_OID /* auxiliary iff STIR */);
+ index->rd_rel->relam == STIR_AM_OID /* auxiliary iff STIR */,
+ InvalidOid /* auxiliary_for_index_id is set only during build */);
/* fill in attribute numbers */
for (i = 0; i < numAtts; i++)
@@ -2677,7 +2691,8 @@ BuildDummyIndexInfo(Relation index)
false,
index->rd_indam->amsummarizing,
indexStruct->indisexclusion && indexStruct->indisunique,
- index->rd_rel->relam == STIR_AM_OID /* auxiliary iff STIR */);
+ index->rd_rel->relam == STIR_AM_OID /* auxiliary iff STIR */,
+ InvalidOid);
/* fill in attribute numbers */
for (i = 0; i < numAtts; i++)
@@ -3847,6 +3862,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
heapRelation;
Oid heapId;
Oid save_userid;
+ Oid junkAuxIndexId;
int save_sec_context;
int save_nestlevel;
IndexInfo *indexInfo;
@@ -3903,6 +3919,19 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
}
+ /* Check for the auxiliary index for that index, it needs to dropped */
+ junkAuxIndexId = get_auxiliary_index(indexId);
+ if (OidIsValid(junkAuxIndexId))
+ {
+ ObjectAddress object;
+ object.classId = RelationRelationId;
+ object.objectId = junkAuxIndexId;
+ object.objectSubId = 0;
+ performDeletion(&object, DROP_RESTRICT,
+ PERFORM_DELETION_INTERNAL |
+ PERFORM_DELETION_QUIETLY);
+ }
+
/*
* Open the target index relation and get an exclusive lock on it, to
* ensure that no one else is touching this particular index.
@@ -4191,7 +4220,8 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
{
Relation rel;
Oid toast_relid;
- List *indexIds;
+ List *indexIds,
+ *auxIndexIds = NIL;
char persistence;
bool result = false;
ListCell *indexId;
@@ -4280,13 +4310,30 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
else
persistence = rel->rd_rel->relpersistence;
+ foreach(indexId, indexIds)
+ {
+ Oid indexOid = lfirst_oid(indexId);
+ Oid indexAm = get_rel_relam(indexOid);
+
+ /* All STIR indexes are auxiliary indexes */
+ if (indexAm == STIR_AM_OID)
+ {
+ if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
+ RemoveReindexPending(indexOid);
+ auxIndexIds = lappend_oid(auxIndexIds, indexOid);
+ }
+ }
+
/* Reindex all the indexes. */
i = 1;
foreach(indexId, indexIds)
{
Oid indexOid = lfirst_oid(indexId);
Oid indexNamespaceId = get_rel_namespace(indexOid);
- Oid indexAm = get_rel_relam(indexOid);
+
+ /* Auxiliary indexes are going to be dropped during main index rebuild */
+ if (list_member_oid(auxIndexIds, indexOid))
+ continue;
/*
* Skip any invalid indexes on a TOAST table. These can only be
@@ -4312,18 +4359,6 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
continue;
}
- if (indexAm == STIR_AM_OID)
- {
- ereport(WARNING,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("skipping reindex of auxiliary index \"%s.%s\"",
- get_namespace_name(indexNamespaceId),
- get_rel_name(indexOid))));
- if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
- RemoveReindexPending(indexOid);
- continue;
- }
-
reindex_index(stmt, indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
persistence, params);
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index c8b11f887e2..1c275ef373f 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -1035,6 +1035,63 @@ get_index_constraint(Oid indexId)
return constraintId;
}
+/*
+ * get_auxiliary_index
+ * Given the OID of an index, return the OID of its auxiliary
+ * index, or InvalidOid if there is no auxiliary index.
+ */
+Oid
+get_auxiliary_index(Oid indexId)
+{
+ Oid auxiliaryIndexOid = InvalidOid;
+ Relation depRel;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /* Search the dependency table for the index */
+ depRel = table_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(indexId));
+ ScanKeyInit(&key[2],
+ Anum_pg_depend_refobjsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(0));
+
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ NULL, 3, key);
+
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ /*
+ * We assume any AUTO dependency on index with rel_kind
+ * of RELKIND_INDEX is that we are looking for.
+ */
+ if (deprec->classid == RelationRelationId &&
+ (deprec->deptype == DEPENDENCY_AUTO) &&
+ get_rel_relkind(deprec->objid) == RELKIND_INDEX)
+ {
+ auxiliaryIndexOid = deprec->objid;
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+ table_close(depRel, AccessShareLock);
+
+ return auxiliaryIndexOid;
+}
+
/*
* get_index_ref_constraints
* Given the OID of an index, return the OID of all foreign key
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 9cc4f06da9f..3aa657c79cb 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -308,6 +308,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_Am = BTREE_AM_OID;
indexInfo->ii_AmCache = NULL;
indexInfo->ii_Auxiliary = false;
+ indexInfo->ii_AuxiliaryForIndexId = InvalidOid;
indexInfo->ii_Context = CurrentMemoryContext;
collationIds[0] = InvalidOid;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 0c7740e2c85..5fb447195b5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -245,7 +245,7 @@ CheckIndexCompatible(Oid oldId,
indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
accessMethodId, NIL, NIL, false, false,
false, false, amsummarizing,
- isWithoutOverlaps, isauxiliary);
+ isWithoutOverlaps, isauxiliary, InvalidOid);
typeIds = palloc_array(Oid, numberOfAttributes);
collationIds = palloc_array(Oid, numberOfAttributes);
opclassIds = palloc_array(Oid, numberOfAttributes);
@@ -943,7 +943,8 @@ DefineIndex(Oid tableId,
concurrent,
amissummarizing,
stmt->iswithoutoverlaps,
- false);
+ false,
+ InvalidOid);
typeIds = palloc_array(Oid, numberOfAttributes);
collationIds = palloc_array(Oid, numberOfAttributes);
@@ -3683,6 +3684,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
{
Oid indexId;
Oid auxIndexId;
+ Oid junkAuxIndexId;
Oid tableId;
Oid amId;
bool safe; /* for set_indexsafe_procflags */
@@ -4032,6 +4034,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
ReindexIndexInfo *newidx;
Oid newIndexId;
Oid auxIndexId;
+ Oid junkAuxIndexId;
Relation indexRel;
Relation heapRel;
Oid save_userid;
@@ -4039,6 +4042,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
int save_nestlevel;
Relation newIndexRel;
Relation auxIndexRel;
+ Relation junkAuxIndexRel;
LockRelId *lockrelid;
Oid tablespaceid;
@@ -4112,12 +4116,17 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
tablespaceid,
auxConcurrentName);
+ /* Search for auxiliary index for reindexed index, to drop it */
+ junkAuxIndexId = get_auxiliary_index(idx->indexId);
+
/*
* Now open the relation of the new index, a session-level lock is
* also needed on it.
*/
newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock);
auxIndexRel = index_open(auxIndexId, ShareUpdateExclusiveLock);
+ if (OidIsValid(junkAuxIndexId))
+ junkAuxIndexRel = index_open(junkAuxIndexId, ShareUpdateExclusiveLock);
/*
* Save the list of OIDs and locks in private context
@@ -4127,6 +4136,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
newidx = palloc_object(ReindexIndexInfo);
newidx->indexId = newIndexId;
newidx->auxIndexId = auxIndexId;
+ newidx->junkAuxIndexId = junkAuxIndexId;
newidx->safe = idx->safe;
newidx->tableId = idx->tableId;
newidx->amId = idx->amId;
@@ -4148,10 +4158,18 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
lockrelid = palloc_object(LockRelId);
*lockrelid = auxIndexRel->rd_lockInfo.lockRelId;
relationLocks = lappend(relationLocks, lockrelid);
+ if (OidIsValid(junkAuxIndexId))
+ {
+ lockrelid = palloc_object(LockRelId);
+ *lockrelid = junkAuxIndexRel->rd_lockInfo.lockRelId;
+ relationLocks = lappend(relationLocks, lockrelid);
+ }
MemoryContextSwitchTo(oldcontext);
index_close(indexRel, NoLock);
+ if (OidIsValid(junkAuxIndexId))
+ index_close(junkAuxIndexRel, NoLock);
index_close(auxIndexRel, NoLock);
index_close(newIndexRel, NoLock);
@@ -4340,7 +4358,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
/*
* At this moment all target indexes are marked as "ready-to-insert". So,
- * we are free to start process of dropping auxiliary indexes.
+ * we are free to start process of dropping auxiliary indexes - including
+ * just indexes detected earlier.
*/
foreach(lc, newIndexIds)
{
@@ -4363,6 +4382,9 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
*/
PushActiveSnapshot(GetTransactionSnapshot());
index_set_state_flags(newidx->auxIndexId, INDEX_DROP_CLEAR_READY);
+ /* Ensure just index is marked as non-ready */
+ if (OidIsValid(newidx->junkAuxIndexId))
+ index_set_state_flags(newidx->junkAuxIndexId, INDEX_DROP_CLEAR_READY);
PopActiveSnapshot();
CommitTransactionCommand();
@@ -4581,6 +4603,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
PushActiveSnapshot(GetTransactionSnapshot());
index_concurrently_set_dead(newidx->tableId, newidx->auxIndexId);
+ if (OidIsValid(newidx->junkAuxIndexId))
+ index_concurrently_set_dead(newidx->tableId, newidx->junkAuxIndexId);
PopActiveSnapshot();
}
@@ -4632,6 +4656,14 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
object.objectSubId = 0;
add_exact_object_address(&object, objects);
+
+ if (OidIsValid(idx->junkAuxIndexId))
+ {
+
+ object.objectId = idx->junkAuxIndexId;
+ object.objectSubId = 0;
+ add_exact_object_address(&object, objects);
+ }
}
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..71c3b993bd3 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1532,6 +1532,8 @@ RemoveRelations(DropStmt *drop)
ListCell *cell;
int flags = 0;
LOCKMODE lockmode = AccessExclusiveLock;
+ MemoryContext private_context,
+ oldcontext;
/* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
if (drop->concurrent)
@@ -1592,9 +1594,20 @@ RemoveRelations(DropStmt *drop)
relkind = 0; /* keep compiler quiet */
break;
}
+ /*
+ * Create a memory context that will survive forced transaction commits we
+ * may need to do below (in case of concurrent index drop).
+ * Since it is a child of PortalContext, it will go away eventually even if
+ * we suffer an error; there's no need for special abort cleanup logic.
+ */
+ private_context = AllocSetContextCreate(PortalContext,
+ "RemoveRelations",
+ ALLOCSET_SMALL_SIZES);
+ oldcontext = MemoryContextSwitchTo(private_context);
/* Lock and validate each relation; build a list of object addresses */
objects = new_object_addresses();
+ MemoryContextSwitchTo(oldcontext);
foreach(cell, drop->objects)
{
@@ -1646,6 +1659,34 @@ RemoveRelations(DropStmt *drop)
flags |= PERFORM_DELETION_CONCURRENTLY;
}
+ /*
+ * Concurrent index drop requires to be the first transaction. But in
+ * case we have junk auxiliary index - we want to drop it too (and also
+ * in a concurrent way). In this case perform silent internal deletion
+ * of auxiliary index, and restore transaction state. It is fine to do it
+ * in the loop because there is only single element in drop->objects.
+ */
+ if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
+ state.actual_relkind == RELKIND_INDEX)
+ {
+ Oid junkAuxIndexOid = get_auxiliary_index(relOid);
+ if (OidIsValid(junkAuxIndexOid))
+ {
+ ObjectAddress object;
+ object.classId = RelationRelationId;
+ object.objectId = junkAuxIndexOid;
+ object.objectSubId = 0;
+ performDeletion(&object, DROP_RESTRICT,
+ PERFORM_DELETION_CONCURRENTLY |
+ PERFORM_DELETION_INTERNAL |
+ PERFORM_DELETION_QUIETLY);
+ CommitTransactionCommand();
+ StartTransactionCommand();
+ PushActiveSnapshot(GetTransactionSnapshot());
+ PreventInTransactionBlock(true, "DROP INDEX CONCURRENTLY");
+ }
+ }
+
/*
* Concurrent index drop cannot be used with partitioned indexes,
* either.
@@ -1674,12 +1715,17 @@ RemoveRelations(DropStmt *drop)
obj.objectId = relOid;
obj.objectSubId = 0;
+ oldcontext = MemoryContextSwitchTo(private_context);
add_exact_object_address(&obj, objects);
+ MemoryContextSwitchTo(oldcontext);
}
+ /* Deletion may involve multiple commits, so, switch to memory context */
+ oldcontext = MemoryContextSwitchTo(private_context);
performMultipleDeletions(objects, drop->behavior, flags);
+ MemoryContextSwitchTo(oldcontext);
- free_object_addresses(objects);
+ MemoryContextDelete(private_context);
}
/*
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index b556ba4817b..d7be8715d52 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -834,7 +834,7 @@ IndexInfo *
makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
List *predicates, bool unique, bool nulls_not_distinct,
bool isready, bool concurrent, bool summarizing,
- bool withoutoverlaps, bool auxiliary)
+ bool withoutoverlaps, bool auxiliary, Oid auxiliary_for_index_id)
{
IndexInfo *n = makeNode(IndexInfo);
@@ -851,6 +851,7 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
n->ii_Summarizing = summarizing;
n->ii_WithoutOverlaps = withoutoverlaps;
n->ii_Auxiliary = auxiliary;
+ n->ii_AuxiliaryForIndexId = auxiliary_for_index_id;
/* summarizing indexes cannot contain non-key attributes */
Assert(!summarizing || (numkeyattrs == numattrs));
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 0ea7ccf5243..02bcf5e9315 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -180,6 +180,7 @@ extern List *getOwnedSequences(Oid relid);
extern Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok);
extern Oid get_index_constraint(Oid indexId);
+extern Oid get_auxiliary_index(Oid indexId);
extern List *get_index_ref_constraints(Oid indexId);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b399ba5ec40..dfc0ac62022 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -220,6 +220,8 @@ typedef struct IndexInfo
int ii_ParallelWorkers;
/* is auxiliary for concurrent index build? */
bool ii_Auxiliary;
+ /* if creating an auxiliary index, the OID of the main index; otherwise InvalidOid. */
+ Oid ii_AuxiliaryForIndexId;
/* Oid of index AM */
Oid ii_Am;
/* private cache area for index AM */
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 4904748f5fc..35745bc521c 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -100,7 +100,7 @@ extern IndexInfo *makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid,
bool unique, bool nulls_not_distinct,
bool isready, bool concurrent,
bool summarizing, bool withoutoverlaps,
- bool auxiliary);
+ bool auxiliary, Oid auxiliary_for_index_id);
extern Node *makeStringConst(char *str, int location);
extern DefElem *makeDefElem(char *name, Node *arg, int location);
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index a3e85ba1310..85cd088d080 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -3265,20 +3265,109 @@ ERROR: reindex of auxiliary index "aux_index_ind6_ccaux" not supported
REINDEX INDEX CONCURRENTLY aux_index_ind6_ccaux;
ERROR: reindex of auxiliary index "aux_index_ind6_ccaux" not supported
-- This makes the previous failure go away, so the index can become valid.
-DELETE FROM concur_reindex_tab4 WHERE c1 = 1;
-ERROR: relation "concur_reindex_tab4" does not exist
-LINE 1: DELETE FROM concur_reindex_tab4 WHERE c1 = 1;
- ^
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Should be skipped during reindex and dropped
+REINDEX INDEX aux_index_ind6;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+Indexes:
+ "aux_index_ind6" UNIQUE, btree (c1)
+
+DROP INDEX aux_index_ind6;
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+ERROR: could not create unique index "aux_index_ind6"
+DETAIL: Key (c1)=(1) is duplicated.
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
-- Should be skipped during reindex
-REINDEX TABLE aux_index_tab5;
-ERROR: could not create unique index "aux_index_ind6"
-DETAIL: Key (c1)=(1) is duplicated.
--- Should be skipped during concurrent reindex
REINDEX TABLE CONCURRENTLY aux_index_tab5;
WARNING: skipping reindex of invalid index "public.aux_index_ind6"
HINT: Use DROP INDEX or REINDEX INDEX.
WARNING: skipping reindex of auxiliary index "public.aux_index_ind6_ccaux"
NOTICE: table "aux_index_tab5" has no indexes that can be reindexed concurrently
+-- Make sure it is still exists
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+Indexes:
+ "aux_index_ind6" UNIQUE, btree (c1) INVALID
+ "aux_index_ind6_ccaux" stir (c1) INVALID
+
+-- Should be skipped during reindex and dropped
+REINDEX TABLE aux_index_tab5;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+Indexes:
+ "aux_index_ind6" UNIQUE, btree (c1)
+
+DROP INDEX aux_index_ind6;
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+ERROR: could not create unique index "aux_index_ind6"
+DETAIL: Key (c1)=(1) is duplicated.
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Should be skipped during reindex and dropped
+REINDEX INDEX CONCURRENTLY aux_index_ind6;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+Indexes:
+ "aux_index_ind6" UNIQUE, btree (c1)
+
+DROP INDEX aux_index_ind6;
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+ERROR: could not create unique index "aux_index_ind6"
+DETAIL: Key (c1)=(1) is duplicated.
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Drop main index CONCURRENTLY
+DROP INDEX CONCURRENTLY aux_index_ind6;
+-- Make sure auxiliary index dropped too
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+
+DROP INDEX aux_index_ind6;
+ERROR: index "aux_index_ind6" does not exist
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+ERROR: could not create unique index "aux_index_ind6"
+DETAIL: Key (c1)=(1) is duplicated.
+-- Drop main index
+DROP INDEX aux_index_ind6;
+-- Make sure auxiliary index dropped too
+\d aux_index_tab5
+ Table "public.aux_index_tab5"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ c1 | integer | | |
+
DROP TABLE aux_index_tab5;
-- Check handling of indexes with expressions and predicates. The
-- definitions of the rebuilt indexes should match the original
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 7ae8e44019b..6d597790b56 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -1340,11 +1340,62 @@ REINDEX INDEX aux_index_ind6_ccaux;
-- Concurrently also
REINDEX INDEX CONCURRENTLY aux_index_ind6_ccaux;
-- This makes the previous failure go away, so the index can become valid.
-DELETE FROM concur_reindex_tab4 WHERE c1 = 1;
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Should be skipped during reindex and dropped
+REINDEX INDEX aux_index_ind6;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+DROP INDEX aux_index_ind6;
+
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
-- Should be skipped during reindex
-REINDEX TABLE aux_index_tab5;
--- Should be skipped during concurrent reindex
REINDEX TABLE CONCURRENTLY aux_index_tab5;
+-- Make sure it is still exists
+\d aux_index_tab5
+-- Should be skipped during reindex and dropped
+REINDEX TABLE aux_index_tab5;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+DROP INDEX aux_index_ind6;
+
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Should be skipped during reindex and dropped
+REINDEX INDEX CONCURRENTLY aux_index_ind6;
+-- Make sure aux index is dropped
+\d aux_index_tab5
+DROP INDEX aux_index_ind6;
+
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+-- This makes the previous failure go away, so the index can become valid.
+DELETE FROM aux_index_tab5 WHERE c1 = 1;
+-- Drop main index CONCURRENTLY
+DROP INDEX CONCURRENTLY aux_index_ind6;
+-- Make sure auxiliary index dropped too
+\d aux_index_tab5
+DROP INDEX aux_index_ind6;
+
+-- Insert duplicates again
+INSERT INTO aux_index_tab5 VALUES (1), (1);
+-- Create invalid index again
+CREATE UNIQUE INDEX CONCURRENTLY aux_index_ind6 ON aux_index_tab5 (c1);
+-- Drop main index
+DROP INDEX aux_index_ind6;
+-- Make sure auxiliary index dropped too
+\d aux_index_tab5
+
DROP TABLE aux_index_tab5;
-- Check handling of indexes with expressions and predicates. The
--
2.43.0