wip_optimize_for_non_pkey_non_ri_unique_index.patch

application/octet-stream

Filename: wip_optimize_for_non_pkey_non_ri_unique_index.patch
Type: application/octet-stream
Part: 0
Message: Re: [PATCH] Use indexes on the subscriber when REPLICA IDENTITY is full on the publisher

Patch

Format: format-patch
Series: patch 0002
Subject: WIP: Optimize for non-pkey / non-RI unique indexes
File+
src/backend/executor/execReplication.c 6 7
src/backend/replication/logical/relation.c 76 6
src/backend/replication/logical/worker.c 1 1
src/include/executor/executor.h 2 1
src/include/replication/logicalrelation.h 4 2
From 1e091fbe751a97ba296cc43fd66c057102b34b43 Mon Sep 17 00:00:00 2001
From: Onder Kalaci <onderkalaci@gmail.com>
Date: Thu, 9 Mar 2023 14:38:40 +0300
Subject: [PATCH 2/2] WIP: Optimize for non-pkey / non-RI unique indexes

---
 src/backend/executor/execReplication.c     | 13 ++--
 src/backend/replication/logical/relation.c | 82 ++++++++++++++++++++--
 src/backend/replication/logical/worker.c   |  2 +-
 src/include/executor/executor.h            |  3 +-
 src/include/replication/logicalrelation.h  |  6 +-
 5 files changed, 89 insertions(+), 17 deletions(-)

diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index d1be63d9de..6f9bfdac05 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -137,8 +137,8 @@ build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel,
  * contents, and return true.  Return false otherwise.
  */
 bool
-RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
-							 LockTupleMode lockmode,
+RelationFindReplTupleByIndex(Relation rel, LogicalRepRelation *remoterel,
+							 Oid idxoid, LockTupleMode lockmode,
 							 TupleTableSlot *searchslot,
 							 TupleTableSlot *outslot)
 {
@@ -150,12 +150,12 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
 	Relation	idxrel;
 	bool		found;
 	TypeCacheEntry **eq = NULL;
-	bool		idxIsRelationIdentityOrPK;
+	bool		isIdxSafeToSkipDuplicates;
 
 	/* Open the index. */
 	idxrel = index_open(idxoid, RowExclusiveLock);
 
-	idxIsRelationIdentityOrPK = IdxIsRelationIdentityOrPK(rel, idxoid);
+	isIdxSafeToSkipDuplicates = IsIdxSafeToSkipDuplicates(rel, idxrel, remoterel);
 
 	InitDirtySnapshot(snap);
 
@@ -174,10 +174,9 @@ retry:
 	while (index_getnext_slot(scan, ForwardScanDirection, outslot))
 	{
 		/*
-		 * Avoid expensive equality check if the index is primary key or
-		 * replica identity index.
+		 * Avoid expensive equality check if the index is unique.
 		 */
-		if (!idxIsRelationIdentityOrPK)
+		if (!isIdxSafeToSkipDuplicates)
 		{
 			if (eq == NULL)
 			{
diff --git a/src/backend/replication/logical/relation.c b/src/backend/replication/logical/relation.c
index 1616bd5712..a7cc38bfab 100644
--- a/src/backend/replication/logical/relation.c
+++ b/src/backend/replication/logical/relation.c
@@ -27,6 +27,7 @@
 #include "replication/logicalrelation.h"
 #include "replication/worker_internal.h"
 #include "utils/inval.h"
+#include "utils/syscache.h"
 
 
 static MemoryContext LogicalRepRelMapContext = NULL;
@@ -771,6 +772,76 @@ IndexContainsAnyRemoteColumn(IndexInfo  *indexInfo,
 	return false;
 }
 
+/*
+ * WIP
+ */
+bool
+AllIndexColumnsProvided(IndexInfo  *indexInfo, LogicalRepRelation  *remoterel)
+{
+	int indexColumnMatchCount = 0;
+	for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+	{
+		int			keycol = indexInfo->ii_IndexAttrNumbers[i];
+
+		if (!AttributeNumberIsValid(keycol))
+				return false;
+
+		if (bms_is_member( keycol- 1, remoterel->attkeys))
+			indexColumnMatchCount++;
+	}
+
+	if (indexColumnMatchCount != indexInfo->ii_NumIndexAttrs)
+		return false;
+
+	return true;
+}
+
+bool
+IndexHasPkeyCharacterstics(IndexInfo  *indexInfo, Relation localrel)
+{
+
+	if (!indexInfo->ii_Unique)
+			return false;
+
+	if (!indexInfo->ii_NullsNotDistinct)
+		return false;
+
+	/*
+	 * Check that all of the attributes in a primary key are marked as not
+	 * null.  (We don't really expect to see that; it'd mean the parser messed
+	 * up.  But it seems wise to check anyway.)
+	 */
+	for (int i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+	{
+		AttrNumber	attnum = indexInfo->ii_IndexAttrNumbers[i];
+		HeapTuple	atttuple;
+		Form_pg_attribute attform;
+
+		if (attnum == 0)
+			return false;
+
+		/* System attributes are never null, so no need to check */
+		if (attnum < 0)
+			return false;
+
+		atttuple = SearchSysCache2(ATTNUM,
+								   ObjectIdGetDatum(RelationGetRelid(localrel)),
+								   Int16GetDatum(attnum));
+		if (!HeapTupleIsValid(atttuple))
+			return false;
+
+		attform = (Form_pg_attribute) GETSTRUCT(atttuple);
+
+		if (!attform->attnotnull)
+			return false;
+
+		ReleaseSysCache(atttuple);
+	}
+
+	return true;
+
+}
+
 /*
  * Returns the oid of an index that can be used by the apply worker to scan
  * the relation. The index must be btree, non-partial, and have at least
@@ -859,18 +930,17 @@ GetRelationIdentityOrPK(Relation rel)
 }
 
 /*
- * Given a relation and OID of an index, returns true if the index is relation's
- * replica identity index or relation's primary key's index.
+ * Given index relation, returns true if the index is unique.
  *
  * Returns false otherwise.
  */
 bool
-IdxIsRelationIdentityOrPK(Relation rel, Oid idxoid)
+IsIdxSafeToSkipDuplicates(Relation localrel, Relation idxrel, LogicalRepRelation *remoterel)
 {
-	Assert(OidIsValid(idxoid));
+	IndexInfo  *indexInfo = BuildIndexInfo(idxrel);
 
-	return RelationGetReplicaIndex(rel) == idxoid ||
-		RelationGetPrimaryKeyIndex(rel) == idxoid;
+	return	AllIndexColumnsProvided(indexInfo, remoterel) &&
+			IndexHasPkeyCharacterstics(indexInfo, localrel);
 }
 
 /*
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 10f9711972..73faf7b39d 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -2846,7 +2846,7 @@ FindReplTupleInLocalRel(EState *estate, Relation localrel,
 		   (remoterel->replident == REPLICA_IDENTITY_FULL));
 
 	if (OidIsValid(localidxoid))
-		found = RelationFindReplTupleByIndex(localrel, localidxoid,
+		found = RelationFindReplTupleByIndex(localrel, remoterel, localidxoid,
 											 LockTupleExclusive,
 											 remoteslot, *localslot);
 	else
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 946abc0051..4a636d7ef3 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -634,7 +634,8 @@ extern void check_exclusion_constraint(Relation heap, Relation index,
 /*
  * prototypes from functions in execReplication.c
  */
-extern bool RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
+#include "replication/logicalproto.h"
+extern bool RelationFindReplTupleByIndex(Relation rel, LogicalRepRelation *remoterel, Oid idxoid,
 										 LockTupleMode lockmode,
 										 TupleTableSlot *searchslot,
 										 TupleTableSlot *outslot);
diff --git a/src/include/replication/logicalrelation.h b/src/include/replication/logicalrelation.h
index 723fb6d707..5066f513f6 100644
--- a/src/include/replication/logicalrelation.h
+++ b/src/include/replication/logicalrelation.h
@@ -49,7 +49,9 @@ extern LogicalRepRelMapEntry *logicalrep_partition_open(LogicalRepRelMapEntry *r
 extern void logicalrep_rel_close(LogicalRepRelMapEntry *rel,
 								 LOCKMODE lockmode);
 extern bool IsIndexUsableForReplicaIdentityFull(IndexInfo *indexInfo);
+extern bool AllIndexColumnsProvided(IndexInfo  *indexInfo, LogicalRepRelation *remoterel);
 extern Oid GetRelationIdentityOrPK(Relation rel);
-extern bool IdxIsRelationIdentityOrPK(Relation rel, Oid idxoid);
-
+extern bool IsIdxSafeToSkipDuplicates(Relation localrel, Relation idxrel,
+									  LogicalRepRelation *remoterel);
+extern bool IndexHasPkeyCharacterstics(IndexInfo  *indexInfo, Relation localrel);
 #endif							/* LOGICALRELATION_H */
-- 
2.34.1