From e3c1beb4d2739fb3b1cb7e068a7ef91b0da61fc6 Mon Sep 17 00:00:00 2001
From: nkey <nkey@toloka.ai>
Date: Mon, 12 Aug 2024 13:07:15 +0200
Subject: [PATCH v2 2/2] additional test spec to reproduce dirty snapshot scan
 issue

---
 src/backend/access/index/indexam.c            |  8 ++++
 src/backend/executor/execIndexing.c           |  1 +
 src/test/modules/injection_points/Makefile    |  2 +-
 .../expected/dirty_index_scan.out             | 39 +++++++++++++++++
 src/test/modules/injection_points/meson.build |  1 +
 .../specs/dirty_index_scan.spec               | 43 +++++++++++++++++++
 6 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 src/test/modules/injection_points/expected/dirty_index_scan.out
 create mode 100644 src/test/modules/injection_points/specs/dirty_index_scan.spec

diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dcd04b813d..78b3a58b3b 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -57,6 +57,7 @@
 #include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
+#include "utils/injection_point.h"
 
 
 /* ----------------------------------------------------------------
@@ -694,6 +695,13 @@ index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *
 		 * the index.
 		 */
 		Assert(ItemPointerIsValid(&scan->xs_heaptid));
+#ifdef USE_INJECTION_POINTS
+		if (!IsCatalogRelationOid(scan->indexRelation->rd_id))
+		{
+			INJECTION_POINT("index_getnext_slot_before_fetch");
+		}
+#endif
+
 		if (index_fetch_heap(scan, slot))
 			return true;
 	}
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 45767b4e20..479f145d99 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -775,6 +775,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
 	 * May have to restart scan from this point if a potential conflict is
 	 * found.
 	 */
+	INJECTION_POINT("check_exclusion_or_unique_constraint_before_index_scan");
 retry:
 	conflict = false;
 	found_self = false;
diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile
index 2ffd2f77ed..981dff4e6f 100644
--- a/src/test/modules/injection_points/Makefile
+++ b/src/test/modules/injection_points/Makefile
@@ -9,7 +9,7 @@ PGFILEDESC = "injection_points - facility for injection points"
 REGRESS = injection_points
 REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
 
-ISOLATION = inplace
+ISOLATION = inplace dirty_index_scan
 
 # The injection points are cluster-wide, so disable installcheck
 NO_INSTALLCHECK = 1
diff --git a/src/test/modules/injection_points/expected/dirty_index_scan.out b/src/test/modules/injection_points/expected/dirty_index_scan.out
new file mode 100644
index 0000000000..249e69cb72
--- /dev/null
+++ b/src/test/modules/injection_points/expected/dirty_index_scan.out
@@ -0,0 +1,39 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1_s1 s3_s1 s2_s1 s3_s2
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+step s1_s1: INSERT INTO test.tbl VALUES(42, 1) on conflict(i) do update set n = EXCLUDED.n + 1; <waiting ...>
+step s3_s1: 
+	SELECT injection_points_wakeup('check_exclusion_or_unique_constraint_before_index_scan');
+	SELECT injection_points_detach('check_exclusion_or_unique_constraint_before_index_scan');
+
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
+step s2_s1: UPDATE test.tbl SET n = n + 1 WHERE i = 42;
+step s3_s2: 
+	SELECT injection_points_wakeup('index_getnext_slot_before_fetch');
+	SELECT injection_points_detach('index_getnext_slot_before_fetch');
+
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
+step s1_s1: <... completed>
diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build
index 3c23c14d81..4969952797 100644
--- a/src/test/modules/injection_points/meson.build
+++ b/src/test/modules/injection_points/meson.build
@@ -40,6 +40,7 @@ tests += {
   'isolation': {
     'specs': [
       'inplace',
+      'dirty_index_scan',
     ],
   },
 }
diff --git a/src/test/modules/injection_points/specs/dirty_index_scan.spec b/src/test/modules/injection_points/specs/dirty_index_scan.spec
new file mode 100644
index 0000000000..17c5ec37e6
--- /dev/null
+++ b/src/test/modules/injection_points/specs/dirty_index_scan.spec
@@ -0,0 +1,43 @@
+setup
+{
+	CREATE EXTENSION injection_points;
+	CREATE SCHEMA test;
+	CREATE UNLOGGED TABLE test.tbl(i int primary key, n int);
+	CREATE INDEX tbl_n_idx ON test.tbl(n);
+	INSERT INTO test.tbl VALUES(42,1);
+}
+
+teardown
+{
+	DROP SCHEMA test CASCADE;
+	DROP EXTENSION injection_points;
+}
+
+session s1
+setup	{
+	SELECT injection_points_set_local();
+	SELECT injection_points_attach('check_exclusion_or_unique_constraint_no_conflict', 'error');
+	SELECT injection_points_attach('check_exclusion_or_unique_constraint_before_index_scan', 'wait');
+	SELECT injection_points_attach('index_getnext_slot_before_fetch', 'wait');
+}
+
+step s1_s1	{ INSERT INTO test.tbl VALUES(42, 1) on conflict(i) do update set n = EXCLUDED.n + 1; }
+
+session s2
+step s2_s1	{ UPDATE test.tbl SET n = n + 1 WHERE i = 42; }
+
+session s3
+step s3_s1		{
+	SELECT injection_points_wakeup('check_exclusion_or_unique_constraint_before_index_scan');
+	SELECT injection_points_detach('check_exclusion_or_unique_constraint_before_index_scan');
+}
+step s3_s2		{
+	SELECT injection_points_wakeup('index_getnext_slot_before_fetch');
+	SELECT injection_points_detach('index_getnext_slot_before_fetch');
+}
+
+permutation
+	s1_s1
+	s3_s1
+	s2_s1
+	s3_s2
\ No newline at end of file
-- 
2.34.1

