Skip to content

Commit 35c85c3

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 ad486b0 commit 35c85c3

File tree

4 files changed

+73
-90
lines changed

4 files changed

+73
-90
lines changed

src/backend/executor/execReplication.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
175175
if (!isIdxSafeToSkipDuplicates)
176176
{
177177
if (eq == NULL)
178-
{
179-
#ifdef USE_ASSERT_CHECKING
180-
/* apply assertions only once for the input idxoid */
181-
IndexInfo *indexInfo = BuildIndexInfo(idxrel);
182-
183-
Assert(IsIndexUsableForReplicaIdentityFull(indexInfo));
184-
#endif
185-
186178
eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
187-
}
188179

189180
if (!tuples_equal(outslot, searchslot, eq))
190181
continue;

src/backend/replication/logical/relation.c

Lines changed: 53 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -731,71 +731,9 @@ logicalrep_partition_open(LogicalRepRelMapEntry *root,
731731
return entry;
732732
}
733733

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

819756
idxRel = index_open(idxoid, AccessShareLock);
820757
idxInfo = BuildIndexInfo(idxRel);
821-
isUsableIdx = IsIndexUsableForReplicaIdentityFull(idxInfo);
822-
containsLeftMostCol =
823-
RemoteRelContainsLeftMostColumnOnIdx(idxInfo, attrmap);
758+
isUsableIdx = IsIndexUsableForReplicaIdentityFull(idxInfo, attrmap);
824759
index_close(idxRel, AccessShareLock);
825760

826761
/* Return the first eligible index found */
827-
if (isUsableIdx && containsLeftMostCol)
762+
if (isUsableIdx)
828763
return idxoid;
829764
}
830765

831766
return InvalidOid;
832767
}
833768

834769
/*
835-
* Returns true if the index is usable for replica identity full. For details,
836-
* see FindUsableIndexForReplicaIdentityFull.
770+
* Returns true if the index is usable for replica identity full.
771+
*
772+
* The index must be btree, non-partial, and the leftmost field must be a
773+
* column (not an expression) that references the remote relation column.
774+
* These limitations help to keep the index scan similar to PK/RI index
775+
* scans.
776+
*
777+
* attrmap is a map of local attributes to remote ones. We can consult this
778+
* map to check whether the local index attribute has a corresponding remote
779+
* attribute.
780+
*
781+
* Note that the limitations of index scans for replica identity full only
782+
* adheres to a subset of the limitations of PK/RI. For example, we support
783+
* columns that are marked as [NULL] or we are not interested in the [NOT
784+
* DEFERRABLE] aspect of constraints here. It works for us because we always
785+
* compare the tuples for non-PK/RI index scans. See
786+
* RelationFindReplTupleByIndex().
787+
*
788+
* XXX: There are no fundamental problems for supporting non-btree indexes.
789+
* We mostly need to relax the limitations in RelationFindReplTupleByIndex().
790+
* For partial indexes, the required changes are likely to be larger. If
791+
* none of the tuples satisfy the expression for the index scan, we fall-back
792+
* to sequential execution, which might not be a good idea in some cases.
837793
*/
838794
bool
839-
IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo)
795+
IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo, AttrMap *attrmap)
840796
{
841-
bool is_btree = (indexInfo->ii_Am == BTREE_AM_OID);
842-
bool is_partial = (indexInfo->ii_Predicate != NIL);
843-
bool is_only_on_expression = IsIndexOnlyOnExpression(indexInfo);
797+
AttrNumber keycol;
798+
799+
/* The index must be a Btree index */
800+
if (indexInfo->ii_Am != BTREE_AM_OID)
801+
return false;
802+
803+
/* The index must not be a partial index */
804+
if (indexInfo->ii_Predicate != NIL)
805+
return false;
806+
807+
Assert(indexInfo->ii_NumIndexAttrs >= 1);
844808

845-
return is_btree && !is_partial && !is_only_on_expression;
809+
/* The leftmost index field must not be an expression */
810+
keycol = indexInfo->ii_IndexAttrNumbers[0];
811+
if (!AttributeNumberIsValid(keycol))
812+
return false;
813+
814+
/*
815+
* And the leftmost index field must reference the remote relation column.
816+
* This is because if it doesn't, the sequential scan is favorable over
817+
* index scan in most cases.
818+
*/
819+
if (attrmap->maplen <= AttrNumberGetAttrOffset(keycol) ||
820+
attrmap->attnums[AttrNumberGetAttrOffset(keycol)] < 0)
821+
return false;
822+
823+
return true;
846824
}
847825

848826
/*

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)