@@ -1901,6 +1901,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
19011901 }
19021902 list_free (relation -> rd_indexlist );
19031903 bms_free (relation -> rd_indexattr );
1904+ bms_free (relation -> rd_keyattr );
1905+ bms_free (relation -> rd_idattr );
19041906 FreeTriggerDesc (relation -> trigdesc );
19051907 if (relation -> rd_options )
19061908 pfree (relation -> rd_options );
@@ -2547,6 +2549,7 @@ AtEOXact_cleanup(Relation relation, bool isCommit)
25472549 list_free (relation -> rd_indexlist );
25482550 relation -> rd_indexlist = NIL ;
25492551 relation -> rd_oidindex = InvalidOid ;
2552+ relation -> rd_replidindex = InvalidOid ;
25502553 relation -> rd_indexvalid = 0 ;
25512554 }
25522555}
@@ -2645,6 +2648,7 @@ AtEOSubXact_cleanup(Relation relation, bool isCommit,
26452648 list_free (relation -> rd_indexlist );
26462649 relation -> rd_indexlist = NIL ;
26472650 relation -> rd_oidindex = InvalidOid ;
2651+ relation -> rd_replidindex = InvalidOid ;
26482652 relation -> rd_indexvalid = 0 ;
26492653 }
26502654}
@@ -3596,6 +3600,10 @@ CheckConstraintFetch(Relation relation)
35963600 * of the index list. rd_oidindex is valid when rd_indexvalid isn't zero;
35973601 * it is the pg_class OID of a unique index on OID when the relation has one,
35983602 * and InvalidOid if there is no such index.
3603+ *
3604+ * In exactly the same way, we update rd_replidindex, which is the pg_class
3605+ * OID of an index to be used as the relation's replication identity index,
3606+ * or InvalidOid if there is no such index.
35993607 */
36003608List *
36013609RelationGetIndexList (Relation relation )
@@ -3667,8 +3675,8 @@ RelationGetIndexList(Relation relation)
36673675
36683676 /*
36693677 * Invalid, non-unique, non-immediate or predicate indexes aren't
3670- * interesting for neither oid indexes nor replication identity
3671- * indexes, so don't check them.
3678+ * interesting for either oid indexes or replication identity indexes,
3679+ * so don't check them.
36723680 */
36733681 if (!IndexIsValid (index ) || !index -> indisunique ||
36743682 !index -> indimmediate ||
@@ -3681,35 +3689,29 @@ RelationGetIndexList(Relation relation)
36813689 indclass -> values [0 ] == OID_BTREE_OPS_OID )
36823690 oidIndex = index -> indexrelid ;
36833691
3684- /* always prefer primary keys */
3692+ /* remember primary key index if any */
36853693 if (index -> indisprimary )
36863694 pkeyIndex = index -> indexrelid ;
36873695
3688- /* explicitly chosen index */
3696+ /* remember explicitly chosen replica index */
36893697 if (index -> indisreplident )
36903698 candidateIndex = index -> indexrelid ;
36913699 }
36923700
36933701 systable_endscan (indscan );
36943702
3695- /* primary key */
3696- if (replident == REPLICA_IDENTITY_DEFAULT &&
3697- OidIsValid (pkeyIndex ))
3698- relation -> rd_replidindex = pkeyIndex ;
3699- /* explicitly chosen index */
3700- else if (replident == REPLICA_IDENTITY_INDEX &&
3701- OidIsValid (candidateIndex ))
3702- relation -> rd_replidindex = candidateIndex ;
3703- /* nothing */
3704- else
3705- relation -> rd_replidindex = InvalidOid ;
3706-
37073703 heap_close (indrel , AccessShareLock );
37083704
37093705 /* Now save a copy of the completed list in the relcache entry. */
37103706 oldcxt = MemoryContextSwitchTo (CacheMemoryContext );
37113707 relation -> rd_indexlist = list_copy (result );
37123708 relation -> rd_oidindex = oidIndex ;
3709+ if (replident == REPLICA_IDENTITY_DEFAULT && OidIsValid (pkeyIndex ))
3710+ relation -> rd_replidindex = pkeyIndex ;
3711+ else if (replident == REPLICA_IDENTITY_INDEX && OidIsValid (candidateIndex ))
3712+ relation -> rd_replidindex = candidateIndex ;
3713+ else
3714+ relation -> rd_replidindex = InvalidOid ;
37133715 relation -> rd_indexvalid = 1 ;
37143716 MemoryContextSwitchTo (oldcxt );
37153717
@@ -3767,7 +3769,8 @@ insert_ordered_oid(List *list, Oid datum)
37673769 * correctly with respect to the full index set. It is up to the caller
37683770 * to ensure that a correct rd_indexattr set has been cached before first
37693771 * calling RelationSetIndexList; else a subsequent inquiry might cause a
3770- * wrong rd_indexattr set to get computed and cached.
3772+ * wrong rd_indexattr set to get computed and cached. Likewise, we do not
3773+ * touch rd_keyattr or rd_idattr.
37713774 */
37723775void
37733776RelationSetIndexList (Relation relation , List * indexIds , Oid oidIndex )
@@ -3783,6 +3786,8 @@ RelationSetIndexList(Relation relation, List *indexIds, Oid oidIndex)
37833786 list_free (relation -> rd_indexlist );
37843787 relation -> rd_indexlist = indexIds ;
37853788 relation -> rd_oidindex = oidIndex ;
3789+ /* For the moment, assume the target rel hasn't got a replica index */
3790+ relation -> rd_replidindex = InvalidOid ;
37863791 relation -> rd_indexvalid = 2 ; /* mark list as forced */
37873792 /* Flag relation as needing eoxact cleanup (to reset the list) */
37883793 EOXactListAdd (relation );
@@ -3816,6 +3821,27 @@ RelationGetOidIndex(Relation relation)
38163821 return relation -> rd_oidindex ;
38173822}
38183823
3824+ /*
3825+ * RelationGetReplicaIndex -- get OID of the relation's replica identity index
3826+ *
3827+ * Returns InvalidOid if there is no such index.
3828+ */
3829+ Oid
3830+ RelationGetReplicaIndex (Relation relation )
3831+ {
3832+ List * ilist ;
3833+
3834+ if (relation -> rd_indexvalid == 0 )
3835+ {
3836+ /* RelationGetIndexList does the heavy lifting. */
3837+ ilist = RelationGetIndexList (relation );
3838+ list_free (ilist );
3839+ Assert (relation -> rd_indexvalid != 0 );
3840+ }
3841+
3842+ return relation -> rd_replidindex ;
3843+ }
3844+
38193845/*
38203846 * RelationGetIndexExpressions -- get the index expressions for an index
38213847 *
@@ -3954,8 +3980,8 @@ RelationGetIndexPredicate(Relation relation)
39543980 * predicates.)
39553981 *
39563982 * Depending on attrKind, a bitmap covering the attnums for all index columns,
3957- * for all key columns or for all the columns the configured replica identity
3958- * are returned.
3983+ * for all potential foreign key columns, or for all columns in the configured
3984+ * replica identity index is returned.
39593985 *
39603986 * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that
39613987 * we can include system attributes (e.g., OID) in the bitmap representation.
@@ -3974,22 +4000,25 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
39744000 Bitmapset * uindexattrs ; /* columns in unique indexes */
39754001 Bitmapset * idindexattrs ; /* columns in the replica identity */
39764002 List * indexoidlist ;
4003+ Oid relreplindex ;
39774004 ListCell * l ;
39784005 MemoryContext oldcxt ;
39794006
39804007 /* Quick exit if we already computed the result. */
39814008 if (relation -> rd_indexattr != NULL )
4009+ {
39824010 switch (attrKind )
39834011 {
3984- case INDEX_ATTR_BITMAP_IDENTITY_KEY :
3985- return bms_copy (relation -> rd_idattr );
3986- case INDEX_ATTR_BITMAP_KEY :
3987- return bms_copy (relation -> rd_keyattr );
39884012 case INDEX_ATTR_BITMAP_ALL :
39894013 return bms_copy (relation -> rd_indexattr );
4014+ case INDEX_ATTR_BITMAP_KEY :
4015+ return bms_copy (relation -> rd_keyattr );
4016+ case INDEX_ATTR_BITMAP_IDENTITY_KEY :
4017+ return bms_copy (relation -> rd_idattr );
39904018 default :
39914019 elog (ERROR , "unknown attrKind %u" , attrKind );
39924020 }
4021+ }
39934022
39944023 /* Fast path if definitely no indexes */
39954024 if (!RelationGetForm (relation )-> relhasindex )
@@ -4004,6 +4033,15 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40044033 if (indexoidlist == NIL )
40054034 return NULL ;
40064035
4036+ /*
4037+ * Copy the rd_replidindex value computed by RelationGetIndexList before
4038+ * proceeding. This is needed because a relcache flush could occur inside
4039+ * index_open below, resetting the fields managed by RelationGetIndexList.
4040+ * (The values we're computing will still be valid, assuming that caller
4041+ * has a sufficient lock on the relation.)
4042+ */
4043+ relreplindex = relation -> rd_replidindex ;
4044+
40074045 /*
40084046 * For each index, add referenced attributes to indexattrs.
40094047 *
@@ -4026,7 +4064,6 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40264064 bool isKey ; /* candidate key */
40274065 bool isIDKey ; /* replica identity index */
40284066
4029-
40304067 indexDesc = index_open (indexOid , AccessShareLock );
40314068
40324069 /* Extract index key information from the index's pg_index row */
@@ -4038,7 +4075,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40384075 indexInfo -> ii_Predicate == NIL ;
40394076
40404077 /* Is this index the configured (or default) replica identity? */
4041- isIDKey = indexOid == relation -> rd_replidindex ;
4078+ isIDKey = ( indexOid == relreplindex ) ;
40424079
40434080 /* Collect simple attribute references */
40444081 for (i = 0 ; i < indexInfo -> ii_NumIndexAttrs ; i ++ )
@@ -4050,13 +4087,13 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40504087 indexattrs = bms_add_member (indexattrs ,
40514088 attrnum - FirstLowInvalidHeapAttributeNumber );
40524089
4053- if (isIDKey )
4054- idindexattrs = bms_add_member (idindexattrs ,
4055- attrnum - FirstLowInvalidHeapAttributeNumber );
4056-
40574090 if (isKey )
40584091 uindexattrs = bms_add_member (uindexattrs ,
40594092 attrnum - FirstLowInvalidHeapAttributeNumber );
4093+
4094+ if (isIDKey )
4095+ idindexattrs = bms_add_member (idindexattrs ,
4096+ attrnum - FirstLowInvalidHeapAttributeNumber );
40604097 }
40614098 }
40624099
@@ -4071,22 +4108,28 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
40714108
40724109 list_free (indexoidlist );
40734110
4074- /* Now save a copy of the bitmap in the relcache entry. */
4111+ /*
4112+ * Now save copies of the bitmaps in the relcache entry. We intentionally
4113+ * set rd_indexattr last, because that's the one that signals validity of
4114+ * the values; if we run out of memory before making that copy, we won't
4115+ * leave the relcache entry looking like the other ones are valid but
4116+ * empty.
4117+ */
40754118 oldcxt = MemoryContextSwitchTo (CacheMemoryContext );
4076- relation -> rd_indexattr = bms_copy (indexattrs );
40774119 relation -> rd_keyattr = bms_copy (uindexattrs );
40784120 relation -> rd_idattr = bms_copy (idindexattrs );
4121+ relation -> rd_indexattr = bms_copy (indexattrs );
40794122 MemoryContextSwitchTo (oldcxt );
40804123
40814124 /* We return our original working copy for caller to play with */
40824125 switch (attrKind )
40834126 {
4084- case INDEX_ATTR_BITMAP_IDENTITY_KEY :
4085- return idindexattrs ;
4086- case INDEX_ATTR_BITMAP_KEY :
4087- return uindexattrs ;
40884127 case INDEX_ATTR_BITMAP_ALL :
40894128 return indexattrs ;
4129+ case INDEX_ATTR_BITMAP_KEY :
4130+ return uindexattrs ;
4131+ case INDEX_ATTR_BITMAP_IDENTITY_KEY :
4132+ return idindexattrs ;
40904133 default :
40914134 elog (ERROR , "unknown attrKind %u" , attrKind );
40924135 return NULL ;
@@ -4630,8 +4673,11 @@ load_relcache_init_file(bool shared)
46304673 rel -> rd_refcnt = 0 ;
46314674 rel -> rd_indexvalid = 0 ;
46324675 rel -> rd_indexlist = NIL ;
4633- rel -> rd_indexattr = NULL ;
46344676 rel -> rd_oidindex = InvalidOid ;
4677+ rel -> rd_replidindex = InvalidOid ;
4678+ rel -> rd_indexattr = NULL ;
4679+ rel -> rd_keyattr = NULL ;
4680+ rel -> rd_idattr = NULL ;
46354681 rel -> rd_createSubid = InvalidSubTransactionId ;
46364682 rel -> rd_newRelfilenodeSubid = InvalidSubTransactionId ;
46374683 rel -> rd_amcache = NULL ;
0 commit comments