Skip to content

Commit d0ce9d0

Browse files
Remove unnecessary checks for indexes for REPLICA IDENTITY FULL tables.
Previously, when selecting an usable index for update/delete for the REPLICA IDENTITY FULL table, in IsIndexOnlyExpression(), we used to check if all index fields are not expressions. However, it was not necessary, because it is enough to check if only the leftmost index field is not an expression (and references the remote table column) and this check has already been done by RemoteRelContainsLeftMostColumnOnIdx(). This commit removes IsIndexOnlyExpression() and RemoteRelContainsLeftMostColumnOnIdx() and all checks for usable indexes for REPLICA IDENTITY FULL tables are now performed by IsIndexUsableForReplicaIdentityFull(). Backpatch this to remain the code consistent. Reported-by: Peter Smith Reviewed-by: Amit Kapila, Önder Kalacı Discussion: https://postgr.es/m/CAHut%2BPsGRE5WSsY0jcLHJEoA17MrbP9yy8FxdjC_ZOAACxbt%2BQ%40mail.gmail.com Backpatch-through: 16
1 parent 71e4cc6 commit d0ce9d0

File tree

4 files changed

+64
-89
lines changed

4 files changed

+64
-89
lines changed

src/backend/executor/execReplication.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,16 +222,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
222222
if (!isIdxSafeToSkipDuplicates)
223223
{
224224
if (eq == NULL)
225-
{
226-
#ifdef USE_ASSERT_CHECKING
227-
/* apply assertions only once for the input idxoid */
228-
IndexInfo *indexInfo = BuildIndexInfo(idxrel);
229-
230-
Assert(IsIndexUsableForReplicaIdentityFull(indexInfo));
231-
#endif
232-
233225
eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
234-
}
235226

236227
if (!tuples_equal(outslot, searchslot, eq))
237228
continue;

src/backend/replication/logical/relation.c

Lines changed: 44 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -734,71 +734,9 @@ logicalrep_partition_open(LogicalRepRelMapEntry *root,
734734
return entry;
735735
}
736736

737-
/*
738-
* Returns true if the given index consists only of expressions such as:
739-
* CREATE INDEX idx ON table(foo(col));
740-
*
741-
* Returns false even if there is one column reference:
742-
* CREATE INDEX idx ON table(foo(col), col_2);
743-
*/
744-
static bool
745-
IsIndexOnlyOnExpression(IndexInfo *indexInfo)
746-
{
747-
for (int i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
748-
{
749-
AttrNumber attnum = indexInfo->ii_IndexAttrNumbers[i];
750-
751-
if (AttributeNumberIsValid(attnum))
752-
return false;
753-
}
754-
755-
return true;
756-
}
757-
758-
/*
759-
* Returns true if the attrmap contains the leftmost column of the index.
760-
* Otherwise returns false.
761-
*
762-
* attrmap is a map of local attributes to remote ones. We can consult this
763-
* map to check whether the local index attribute has a corresponding remote
764-
* attribute.
765-
*/
766-
static bool
767-
RemoteRelContainsLeftMostColumnOnIdx(IndexInfo *indexInfo, AttrMap *attrmap)
768-
{
769-
AttrNumber keycol;
770-
771-
Assert(indexInfo->ii_NumIndexAttrs >= 1);
772-
773-
keycol = indexInfo->ii_IndexAttrNumbers[0];
774-
if (!AttributeNumberIsValid(keycol))
775-
return false;
776-
777-
if (attrmap->maplen <= AttrNumberGetAttrOffset(keycol))
778-
return false;
779-
780-
return attrmap->attnums[AttrNumberGetAttrOffset(keycol)] >= 0;
781-
}
782-
783737
/*
784738
* Returns the oid of an index that can be used by the apply worker to scan
785-
* the relation. The index must be btree or hash, non-partial, and the leftmost
786-
* field must be a column (not an expression) that references the remote
787-
* relation column. These limitations help to keep the index scan similar
788-
* to PK/RI index scans.
789-
*
790-
* Note that the limitations of index scans for replica identity full only
791-
* adheres to a subset of the limitations of PK/RI. For example, we support
792-
* columns that are marked as [NULL] or we are not interested in the [NOT
793-
* DEFERRABLE] aspect of constraints here. It works for us because we always
794-
* compare the tuples for non-PK/RI index scans. See
795-
* RelationFindReplTupleByIndex().
796-
*
797-
* XXX: See IsIndexUsableForReplicaIdentityFull() to know the challenges in
798-
* supporting indexes other than btree and hash. For partial indexes, the
799-
* required changes are likely to be larger. If none of the tuples satisfy
800-
* the expression for the index scan, we fall-back to sequential execution,
801-
* which might not be a good idea in some cases.
739+
* the relation.
802740
*
803741
* We expect to call this function when REPLICA IDENTITY FULL is defined for
804742
* the remote relation.
@@ -815,31 +753,41 @@ FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
815753
{
816754
Oid idxoid = lfirst_oid(lc);
817755
bool isUsableIdx;
818-
bool containsLeftMostCol;
819756
Relation idxRel;
820757
IndexInfo *idxInfo;
821758

822759
idxRel = index_open(idxoid, AccessShareLock);
823760
idxInfo = BuildIndexInfo(idxRel);
824-
isUsableIdx = IsIndexUsableForReplicaIdentityFull(idxInfo);
825-
containsLeftMostCol =
826-
RemoteRelContainsLeftMostColumnOnIdx(idxInfo, attrmap);
761+
isUsableIdx = IsIndexUsableForReplicaIdentityFull(idxInfo, attrmap);
827762
index_close(idxRel, AccessShareLock);
828763

829764
/* Return the first eligible index found */
830-
if (isUsableIdx && containsLeftMostCol)
765+
if (isUsableIdx)
831766
return idxoid;
832767
}
833768

834769
return InvalidOid;
835770
}
836771

837772
/*
838-
* Returns true if the index is usable for replica identity full. For details,
839-
* see FindUsableIndexForReplicaIdentityFull.
773+
* Returns true if the index is usable for replica identity full.
840774
*
841-
* Currently, only Btree and Hash indexes can be returned as usable. This
842-
* is due to following reasons:
775+
* The index must be btree or hash, non-partial, and the leftmost field must be
776+
* a column (not an expression) that references the remote relation column. These
777+
* limitations help to keep the index scan similar to PK/RI index scans.
778+
*
779+
* attrmap is a map of local attributes to remote ones. We can consult this
780+
* map to check whether the local index attribute has a corresponding remote
781+
* attribute.
782+
*
783+
* Note that the limitations of index scans for replica identity full only
784+
* adheres to a subset of the limitations of PK/RI. For example, we support
785+
* columns that are marked as [NULL] or we are not interested in the [NOT
786+
* DEFERRABLE] aspect of constraints here. It works for us because we always
787+
* compare the tuples for non-PK/RI index scans. See
788+
* RelationFindReplTupleByIndex().
789+
*
790+
* The reasons why only Btree and Hash indexes can be considered as usable are:
843791
*
844792
* 1) Other index access methods don't have a fixed strategy for equality
845793
* operation. Refer get_equal_strategy_number_for_am().
@@ -851,16 +799,38 @@ FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
851799
*
852800
* XXX: Note that BRIN and GIN indexes do not implement "amgettuple" which
853801
* will be used later to fetch the tuples. See RelationFindReplTupleByIndex().
802+
*
803+
* XXX: To support partial indexes, the required changes are likely to be larger.
804+
* If none of the tuples satisfy the expression for the index scan, we fall-back
805+
* to sequential execution, which might not be a good idea in some cases.
854806
*/
855807
bool
856-
IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo)
808+
IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo, AttrMap *attrmap)
857809
{
810+
AttrNumber keycol;
811+
858812
/* Ensure that the index access method has a valid equal strategy */
859813
if (get_equal_strategy_number_for_am(indexInfo->ii_Am) == InvalidStrategy)
860814
return false;
815+
816+
/* The index must not be a partial index */
861817
if (indexInfo->ii_Predicate != NIL)
862818
return false;
863-
if (IsIndexOnlyOnExpression(indexInfo))
819+
820+
Assert(indexInfo->ii_NumIndexAttrs >= 1);
821+
822+
/* The leftmost index field must not be an expression */
823+
keycol = indexInfo->ii_IndexAttrNumbers[0];
824+
if (!AttributeNumberIsValid(keycol))
825+
return false;
826+
827+
/*
828+
* And the leftmost index field must reference the remote relation column.
829+
* This is because if it doesn't, the sequential scan is favorable over
830+
* index scan in most cases.
831+
*/
832+
if (attrmap->maplen <= AttrNumberGetAttrOffset(keycol) ||
833+
attrmap->attnums[AttrNumberGetAttrOffset(keycol)] < 0)
864834
return false;
865835

866836
#ifdef USE_ASSERT_CHECKING

src/backend/replication/logical/worker.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
#include <sys/stat.h>
141141
#include <unistd.h>
142142

143+
#include "access/genam.h"
143144
#include "access/table.h"
144145
#include "access/tableam.h"
145146
#include "access/twophase.h"
@@ -410,7 +411,7 @@ static void apply_handle_delete_internal(ApplyExecutionData *edata,
410411
ResultRelInfo *relinfo,
411412
TupleTableSlot *remoteslot,
412413
Oid localindexoid);
413-
static bool FindReplTupleInLocalRel(EState *estate, Relation localrel,
414+
static bool FindReplTupleInLocalRel(ApplyExecutionData *edata, Relation localrel,
414415
LogicalRepRelation *remoterel,
415416
Oid localidxoid,
416417
TupleTableSlot *remoteslot,
@@ -2663,7 +2664,7 @@ apply_handle_update_internal(ApplyExecutionData *edata,
26632664
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1, NIL);
26642665
ExecOpenIndices(relinfo, false);
26652666

2666-
found = FindReplTupleInLocalRel(estate, localrel,
2667+
found = FindReplTupleInLocalRel(edata, localrel,
26672668
&relmapentry->remoterel,
26682669
localindexoid,
26692670
remoteslot, &localslot);
@@ -2816,7 +2817,7 @@ apply_handle_delete_internal(ApplyExecutionData *edata,
28162817
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1, NIL);
28172818
ExecOpenIndices(relinfo, false);
28182819

2819-
found = FindReplTupleInLocalRel(estate, localrel, remoterel, localindexoid,
2820+
found = FindReplTupleInLocalRel(edata, localrel, remoterel, localindexoid,
28202821
remoteslot, &localslot);
28212822

28222823
/* If found delete it. */
@@ -2855,12 +2856,13 @@ apply_handle_delete_internal(ApplyExecutionData *edata,
28552856
* Local tuple, if found, is returned in '*localslot'.
28562857
*/
28572858
static bool
2858-
FindReplTupleInLocalRel(EState *estate, Relation localrel,
2859+
FindReplTupleInLocalRel(ApplyExecutionData *edata, Relation localrel,
28592860
LogicalRepRelation *remoterel,
28602861
Oid localidxoid,
28612862
TupleTableSlot *remoteslot,
28622863
TupleTableSlot **localslot)
28632864
{
2865+
EState *estate = edata->estate;
28642866
bool found;
28652867

28662868
/*
@@ -2875,9 +2877,21 @@ FindReplTupleInLocalRel(EState *estate, Relation localrel,
28752877
(remoterel->replident == REPLICA_IDENTITY_FULL));
28762878

28772879
if (OidIsValid(localidxoid))
2880+
{
2881+
#ifdef USE_ASSERT_CHECKING
2882+
Relation idxrel = index_open(localidxoid, AccessShareLock);
2883+
2884+
/* Index must be PK, RI, or usable for REPLICA IDENTITY FULL tables */
2885+
Assert(GetRelationIdentityOrPK(idxrel) == localidxoid ||
2886+
IsIndexUsableForReplicaIdentityFull(BuildIndexInfo(idxrel),
2887+
edata->targetRel->attrmap));
2888+
index_close(idxrel, AccessShareLock);
2889+
#endif
2890+
28782891
found = RelationFindReplTupleByIndex(localrel, localidxoid,
28792892
LockTupleExclusive,
28802893
remoteslot, *localslot);
2894+
}
28812895
else
28822896
found = RelationFindReplTupleSeq(localrel, LockTupleExclusive,
28832897
remoteslot, *localslot);
@@ -2995,7 +3009,7 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
29953009
bool found;
29963010

29973011
/* Get the matching local tuple from the partition. */
2998-
found = FindReplTupleInLocalRel(estate, partrel,
3012+
found = FindReplTupleInLocalRel(edata, partrel,
29993013
&part_entry->remoterel,
30003014
part_entry->localindexoid,
30013015
remoteslot_part, &localslot);

src/include/replication/logicalrelation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extern LogicalRepRelMapEntry *logicalrep_partition_open(LogicalRepRelMapEntry *r
4848
Relation partrel, AttrMap *map);
4949
extern void logicalrep_rel_close(LogicalRepRelMapEntry *rel,
5050
LOCKMODE lockmode);
51-
extern bool IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo);
51+
extern bool IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo, AttrMap *attrmap);
5252
extern Oid GetRelationIdentityOrPK(Relation rel);
5353

5454
#endif /* LOGICALRELATION_H */

0 commit comments

Comments
 (0)