GSoC 2017 : Patch for predicate locking in Gist index

Started by Shubham Baraiover 8 years ago31 messages
#1Shubham Barai
shubhambaraiss@gmail.com
1 attachment(s)

Hi, hackers!

I have created my first patch for predicate locking in gist index. It
includes a test for verification of serialization failures and a test to
check false positives.
I am submitting my patch little late because there were some issues with
"make check" that I was trying to solve. Now, the patch passes all existing
tests.

Regards,
Shubham

<https://mailtrack.io/&gt; Sent with Mailtrack
<https://mailtrack.io/install?source=signature&amp;lang=en&amp;referral=shubhambaraiss@gmail.com&amp;idSignature=22&gt;

Attachments:

0001-Predicate-Locking-in-Gist-index.patchapplication/octet-stream; name=0001-Predicate-Locking-in-Gist-index.patchDownload
From f28ca824929dab3ffdef4502576259bcb457261c Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Fri, 16 Jun 2017 03:01:24 +0530
Subject: [PATCH] Predicate Locking in Gist index

---
 src/backend/access/gist/gist.c                   |  13 +-
 src/backend/access/gist/gistget.c                |   3 +
 src/backend/storage/lmgr/README-SSI              |   5 +-
 src/test/isolation/expected/predicate-gist-2.out | 321 +++++++++++++++++++++
 src/test/isolation/expected/predicate-gist.out   | 339 +++++++++++++++++++++++
 src/test/isolation/isolation_schedule            |   2 +
 src/test/isolation/specs/predicate-gist-2.spec   |  44 +++
 src/test/isolation/specs/predicate-gist.spec     |  44 +++
 8 files changed, 768 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist-2.out
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 mode change 100644 => 100755 src/test/isolation/isolation_schedule
 create mode 100644 src/test/isolation/specs/predicate-gist-2.spec
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 6593771..960c9dd
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			for (ptr = dist->next; ptr; ptr = ptr->next)
 				UnlockReleaseBuffer(ptr->buffer);
 		}
+
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
+
 	}
 	else
 	{
@@ -723,6 +732,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				{
 					LockBuffer(stack->buffer, GIST_UNLOCK);
 					LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+					CheckForSerializableConflictIn(r, NULL, stack->buffer);
 					xlocked = true;
 					stack->page = (Page) BufferGetPage(stack->buffer);
 
@@ -787,6 +797,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 			{
 				LockBuffer(stack->buffer, GIST_UNLOCK);
 				LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
 				xlocked = true;
 				stack->page = (Page) BufferGetPage(stack->buffer);
 				stack->lsn = PageGetLSN(stack->page);
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 122dc38..710fdf5
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist-2.out b/src/test/isolation/expected/predicate-gist-2.out
new file mode 100644
index 0000000..8f54137
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist-2.out
@@ -0,0 +1,321 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..56bdf3b
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,339 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+26500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3500           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
old mode 100644
new mode 100755
index 2606a27..174d325
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -57,3 +57,5 @@ test: alter-table-3
 test: create-trigger
 test: async-notify
 test: timeouts
+test: predicate-gist
+test: predicate-gist-2
diff --git a/src/test/isolation/specs/predicate-gist-2.spec b/src/test/isolation/specs/predicate-gist-2.spec
new file mode 100644
index 0000000..1d67203
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist-2.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to check false positives.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the different part(sub-tree) of the index.
+
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+session "s2"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..400a1cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the same part(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 100) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(250, 250); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(750,750); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g; }
+step "c2"	{ COMMIT; }
-- 
1.9.1

#2Shubham Barai
shubhambaraiss@gmail.com
In reply to: Shubham Barai (#1)
1 attachment(s)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi,

Please find the updated patch here.

Regards,
Shubham

On 16 June 2017 at 15:54, Shubham Barai <shubhambaraiss@gmail.com> wrote:

Show quoted text

Hi, hackers!

I have created my first patch for predicate locking in gist index. It
includes a test for verification of serialization failures and a test to
check false positives.
I am submitting my patch little late because there were some issues with
"make check" that I was trying to solve. Now, the patch passes all existing
tests.

Regards,
Shubham

<https://mailtrack.io/&gt; Sent with Mailtrack
<https://mailtrack.io/install?source=signature&amp;lang=en&amp;referral=shubhambaraiss@gmail.com&amp;idSignature=22&gt;

Attachments:

Predicate-Locking-in-Gist-index_2.patchapplication/octet-stream; name=Predicate-Locking-in-Gist-index_2.patchDownload
From 8f3a17d3b0785c3c0fd98f8aa19c9f42462ccd44 Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Fri, 16 Jun 2017 03:01:24 +0530
Subject: [PATCH] Predicate Locking in Gist index

---
 src/backend/access/gist/gist.c                   |  11 +-
 src/backend/access/gist/gistget.c                |   3 +
 src/backend/storage/lmgr/README-SSI              |   5 +-
 src/test/isolation/expected/predicate-gist-2.out | 321 +++++++++++++++++++++
 src/test/isolation/expected/predicate-gist.out   | 339 +++++++++++++++++++++++
 src/test/isolation/isolation_schedule            |   2 +
 src/test/isolation/specs/predicate-gist-2.spec   |  44 +++
 src/test/isolation/specs/predicate-gist.spec     |  44 +++
 8 files changed, 766 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist-2.out
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 mode change 100644 => 100755 src/test/isolation/isolation_schedule
 create mode 100644 src/test/isolation/specs/predicate-gist-2.spec
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 6593771..dd8d9a9
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -723,6 +730,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				{
 					LockBuffer(stack->buffer, GIST_UNLOCK);
 					LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+					CheckForSerializableConflictIn(r, NULL, stack->buffer);
 					xlocked = true;
 					stack->page = (Page) BufferGetPage(stack->buffer);
 
@@ -787,6 +795,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 			{
 				LockBuffer(stack->buffer, GIST_UNLOCK);
 				LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
 				xlocked = true;
 				stack->page = (Page) BufferGetPage(stack->buffer);
 				stack->lsn = PageGetLSN(stack->page);
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 122dc38..710fdf5
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist-2.out b/src/test/isolation/expected/predicate-gist-2.out
new file mode 100644
index 0000000..8f54137
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist-2.out
@@ -0,0 +1,321 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..56bdf3b
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,339 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+26500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3500           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
old mode 100644
new mode 100755
index 2606a27..174d325
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -57,3 +57,5 @@ test: alter-table-3
 test: create-trigger
 test: async-notify
 test: timeouts
+test: predicate-gist
+test: predicate-gist-2
diff --git a/src/test/isolation/specs/predicate-gist-2.spec b/src/test/isolation/specs/predicate-gist-2.spec
new file mode 100644
index 0000000..1d67203
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist-2.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to check false positives.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the different part(sub-tree) of the index.
+
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+session "s2"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..400a1cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the same part(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 100) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(250, 250); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(750,750); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g; }
+step "c2"	{ COMMIT; }
-- 
1.9.1

#3Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Shubham Barai (#1)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On 06/16/2017 01:24 PM, Shubham Barai wrote:

@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
for (ptr = dist->next; ptr; ptr = ptr->next)
UnlockReleaseBuffer(ptr->buffer);
}
+
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
+

I think this new code needs to go before the UnlockReleaseBuffer() calls
above. Calling BufferGetBlockNumber() on an already-released buffer is
not cool.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Heikki Linnakangas (#3)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On 06/21/2017 10:41 AM, Heikki Linnakangas wrote:

On 06/16/2017 01:24 PM, Shubham Barai wrote:

@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
for (ptr = dist->next; ptr; ptr = ptr->next)
UnlockReleaseBuffer(ptr->buffer);
}
+
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
+

I think this new code needs to go before the UnlockReleaseBuffer() calls
above. Calling BufferGetBlockNumber() on an already-released buffer is
not cool.

.. and that's exactly what you fixed in your updated patch. Sorry for
the noise :-)

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Shubham Barai
shubhambaraiss@gmail.com
In reply to: Heikki Linnakangas (#3)
1 attachment(s)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi,

On 21 June 2017 at 13:11, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/16/2017 01:24 PM, Shubham Barai wrote:

@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace,
GISTSTATE *giststate,
for (ptr = dist->next; ptr; ptr = ptr->next)
UnlockReleaseBuffer(ptr->buffer);
}
+
+               for (ptr = dist; ptr; ptr = ptr->next)
+                       PredicateLockPageSplit(rel,
+
BufferGetBlockNumber(buffer),
+
BufferGetBlockNumber(ptr->buffer));
+
+

I think this new code needs to go before the UnlockReleaseBuffer() calls
above. Calling BufferGetBlockNumber() on an already-released buffer is not
cool.

- Heikki

I know that. This is the old version of the patch. I had sent updated

patch later. Please have a look at updated patch.

Regards,
Shubham

<https://mailtrack.io/&gt; Sent with Mailtrack
<https://mailtrack.io/install?source=signature&amp;lang=en&amp;referral=shubhambaraiss@gmail.com&amp;idSignature=22&gt;

Attachments:

Predicate-Locking-in-Gist-index_2.patchapplication/octet-stream; name=Predicate-Locking-in-Gist-index_2.patchDownload
From 8f3a17d3b0785c3c0fd98f8aa19c9f42462ccd44 Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Fri, 16 Jun 2017 03:01:24 +0530
Subject: [PATCH] Predicate Locking in Gist index

---
 src/backend/access/gist/gist.c                   |  11 +-
 src/backend/access/gist/gistget.c                |   3 +
 src/backend/storage/lmgr/README-SSI              |   5 +-
 src/test/isolation/expected/predicate-gist-2.out | 321 +++++++++++++++++++++
 src/test/isolation/expected/predicate-gist.out   | 339 +++++++++++++++++++++++
 src/test/isolation/isolation_schedule            |   2 +
 src/test/isolation/specs/predicate-gist-2.spec   |  44 +++
 src/test/isolation/specs/predicate-gist.spec     |  44 +++
 8 files changed, 766 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist-2.out
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 mode change 100644 => 100755 src/test/isolation/isolation_schedule
 create mode 100644 src/test/isolation/specs/predicate-gist-2.spec
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 6593771..dd8d9a9
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -723,6 +730,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				{
 					LockBuffer(stack->buffer, GIST_UNLOCK);
 					LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+					CheckForSerializableConflictIn(r, NULL, stack->buffer);
 					xlocked = true;
 					stack->page = (Page) BufferGetPage(stack->buffer);
 
@@ -787,6 +795,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 			{
 				LockBuffer(stack->buffer, GIST_UNLOCK);
 				LockBuffer(stack->buffer, GIST_EXCLUSIVE);
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
 				xlocked = true;
 				stack->page = (Page) BufferGetPage(stack->buffer);
 				stack->lsn = PageGetLSN(stack->page);
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 122dc38..710fdf5
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist-2.out b/src/test/isolation/expected/predicate-gist-2.out
new file mode 100644
index 0000000..8f54137
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist-2.out
@@ -0,0 +1,321 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..56bdf3b
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,339 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+26500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3500           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
old mode 100644
new mode 100755
index 2606a27..174d325
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -57,3 +57,5 @@ test: alter-table-3
 test: create-trigger
 test: async-notify
 test: timeouts
+test: predicate-gist
+test: predicate-gist-2
diff --git a/src/test/isolation/specs/predicate-gist-2.spec b/src/test/isolation/specs/predicate-gist-2.spec
new file mode 100644
index 0000000..1d67203
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist-2.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to check false positives.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the different part(sub-tree) of the index.
+
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+session "s2"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..400a1cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the same part(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 100) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(250, 250); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(750,750); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g; }
+step "c2"	{ COMMIT; }
-- 
1.9.1

#6Andrey Borodin
x4mmm@yandex-team.ru
In reply to: Shubham Barai (#5)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi, hackers!

21 июня 2017 г., в 12:52, Shubham Barai <shubhambaraiss@gmail.com> написал(а):

Hi,
...
I know that. This is the old version of the patch. I had sent updated patch later. Please have a look at updated patch.

Regards,
Shubham

Here is some information for reviewers. This applies to patches for GiST, Hash, GIN and SP-GiST.

As a mentor of the Shubham's GSoC project for every patch regarding predicate locking I have checked:
0. Correctness of implementation (places of predicate lock function calls, but, anyway, this point deserves special attention)
1. Patch applies and installcheck-world passes
2. Patch contains new isolation tests
3. These tests fail if indexes do not implement predicate locking
4. Patch updates documentation

Shubham also had checked that patches work (install check-world) on 1Kb, 8Kb and 32Kb pages.

Best regards, Andrey Borodin, Yandex.

#7Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Shubham Barai (#5)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi!

On Wed, Jun 21, 2017 at 10:52 AM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

Hi,

On 21 June 2017 at 13:11, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/16/2017 01:24 PM, Shubham Barai wrote:

@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace,
GISTSTATE *giststate,
for (ptr = dist->next; ptr; ptr = ptr->next)
UnlockReleaseBuffer(ptr->buffer);
}
+
+               for (ptr = dist; ptr; ptr = ptr->next)
+                       PredicateLockPageSplit(rel,
+
BufferGetBlockNumber(buffer),
+
BufferGetBlockNumber(ptr->buffer));
+
+

I think this new code needs to go before the UnlockReleaseBuffer() calls
above. Calling BufferGetBlockNumber() on an already-released buffer is not
cool.

- Heikki

I know that. This is the old version of the patch. I had sent updated

patch later. Please have a look at updated patch.

I took a look on this patch.

In gistdoinsert() you do CheckForSerializableConflictIn() only if page
wasn't exclusively locked before (xlocked is false).

if (!xlocked)

{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
CheckForSerializableConflictIn(r, NULL, stack->buffer);
xlocked = true;

However, page might be exclusively locked before. And in this case
CheckForSerializableConflictIn() would be skipped. That happens very
rarely (someone fixes incomplete split before we did), but nevertheless.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#8Andrew Borodin
amborodin86@gmail.com
In reply to: Alexander Korotkov (#7)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi, Alexander!

Thanks for looking into the patch!

On Thu, Sep 28, 2017 at 3:59 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

In gistdoinsert() you do CheckForSerializableConflictIn() only if page
wasn't exclusively locked before (xlocked is false).

if (!xlocked)

{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
CheckForSerializableConflictIn(r, NULL, stack->buffer);
xlocked = true;

However, page might be exclusively locked before. And in this case
CheckForSerializableConflictIn() would be skipped. That happens very
rarely (someone fixes incomplete split before we did), but nevertheless.

if xlocked = true, page was already checked for conflict after setting
exclusive lock on it's buffer. I still do not see any problem here...

Best regards, Andrey Borodin.

#9Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#7)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On Sep 28, 2017 4:30 PM, "Alexander Korotkov" <a.korotkov@postgrespro.ru>
wrote:

Hi!

On Wed, Jun 21, 2017 at 10:52 AM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

Hi,

On 21 June 2017 at 13:11, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/16/2017 01:24 PM, Shubham Barai wrote:

@@ -497,6 +499,13 @@ gistplacetopage(Relation rel, Size freespace,
GISTSTATE *giststate,
for (ptr = dist->next; ptr; ptr = ptr->next)
UnlockReleaseBuffer(ptr->buffer);
}
+
+               for (ptr = dist; ptr; ptr = ptr->next)
+                       PredicateLockPageSplit(rel,
+
BufferGetBlockNumber(buffer),
+
BufferGetBlockNumber(ptr->buffer));
+
+

I think this new code needs to go before the UnlockReleaseBuffer() calls
above. Calling BufferGetBlockNumber() on an already-released buffer is not
cool.

- Heikki

I know that. This is the old version of the patch. I had sent updated

patch later. Please have a look at updated patch.

I took a look on this patch.

In gistdoinsert() you do CheckForSerializableConflictIn() only if page
wasn't exclusively locked before (xlocked is false).

if (!xlocked)

{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
CheckForSerializableConflictIn(r, NULL, stack->buffer);
xlocked = true;

However, page might be exclusively locked before. And in this case
CheckForSerializableConflictIn() would be skipped. That happens very
rarely (someone fixes incomplete split before we did), but nevertheless.

I agree with Andrey Borodin's view on locks. I am quoting his message
"if xlocked = true, page was already checked for conflict after setting
exclusive lock on it's buffer. I still do not see any problem here..."

Regards,
Shubham

#10Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Andrew Borodin (#8)
Re: GSoC 2017 : Patch for predicate locking in Gist index

Hi, Andrew!

On Mon, Oct 2, 2017 at 1:40 PM, Andrew Borodin <amborodin86@gmail.com>
wrote:

Thanks for looking into the patch!

On Thu, Sep 28, 2017 at 3:59 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

In gistdoinsert() you do CheckForSerializableConflictIn() only if page
wasn't exclusively locked before (xlocked is false).

if (!xlocked)

{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
CheckForSerializableConflictIn(r, NULL, stack->buffer);
xlocked = true;

However, page might be exclusively locked before. And in this case
CheckForSerializableConflictIn() would be skipped. That happens very
rarely (someone fixes incomplete split before we did), but nevertheless.

if xlocked = true, page was already checked for conflict after setting
exclusive lock on it's buffer. I still do not see any problem here...

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))

{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without
calling CheckForSerializableConflictIn(). This is very rare codepath, but
still...
I think it would be rather safe and easy for understanding to
more CheckForSerializableConflictIn() directly before gistinserttuple().

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#11Andrew Borodin
amborodin86@gmail.com
In reply to: Alexander Korotkov (#10)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On Mon, Oct 2, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))
{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without calling
CheckForSerializableConflictIn().

Indeed! I've overlooked it. I'm remembering this issue, we were
considering not fixing splits if in Serializable isolation, but
dropped the idea.
CheckForSerializableConflictIn() must be after every exclusive lock.

I think it would be rather safe and easy for understanding to more
CheckForSerializableConflictIn() directly before gistinserttuple().

The difference is that after lock we have conditions to change page,
and before gistinserttuple() we have actual intent to change page.

From the point of future development first version is better (if some
new calls occasionally spawn in), but it has 3 calls while your
proposal have 2 calls.
It seems to me that CheckForSerializableConflictIn() before
gistinserttuple() is better as for now.

Best regards, Andrey Borodin.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Andrew Borodin (#11)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On Mon, Oct 2, 2017 at 9:11 PM, Andrew Borodin <amborodin86@gmail.com>
wrote:

On Mon, Oct 2, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))
{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without calling
CheckForSerializableConflictIn().

Indeed! I've overlooked it. I'm remembering this issue, we were
considering not fixing splits if in Serializable isolation, but
dropped the idea.

Yeah, current insert algorithm assumes that split must be fixed before we
can correctly traverse the tree downwards.

CheckForSerializableConflictIn() must be after every exclusive lock.

I'm not sure, that fixing split is the case to necessary call
CheckForSerializableConflictIn(). This lock on leaf page is not taken to
do modification of the page. It's just taken to ensure that nobody else is
fixing this split the same this. After fixing the split, it might appear
that insert would go to another page.

I think it would be rather safe and easy for understanding to more

CheckForSerializableConflictIn() directly before gistinserttuple().

The difference is that after lock we have conditions to change page,
and before gistinserttuple() we have actual intent to change page.

From the point of future development first version is better (if some
new calls occasionally spawn in), but it has 3 calls while your
proposal have 2 calls.
It seems to me that CheckForSerializableConflictIn() before
gistinserttuple() is better as for now.

Agree.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#13Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#12)
1 attachment(s)
Re: GSoC 2017 : Patch for predicate locking in Gist index

<https://mailtrack.io/&gt; Sent with Mailtrack
<https://chrome.google.com/webstore/detail/mailtrack-for-gmail-inbox/ndnaehgpjlnokgebbaldlmgkapkpjkkb?utm_source=gmail&amp;utm_medium=signature&amp;utm_campaign=signaturevirality&gt;
<#>

On 3 October 2017 at 00:32, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Mon, Oct 2, 2017 at 9:11 PM, Andrew Borodin <amborodin86@gmail.com>
wrote:

On Mon, Oct 2, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))
{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without calling
CheckForSerializableConflictIn().

Indeed! I've overlooked it. I'm remembering this issue, we were
considering not fixing splits if in Serializable isolation, but
dropped the idea.

Yeah, current insert algorithm assumes that split must be fixed before we
can correctly traverse the tree downwards.

CheckForSerializableConflictIn() must be after every exclusive lock.

I'm not sure, that fixing split is the case to necessary call
CheckForSerializableConflictIn(). This lock on leaf page is not taken to
do modification of the page. It's just taken to ensure that nobody else is
fixing this split the same this. After fixing the split, it might appear
that insert would go to another page.

I think it would be rather safe and easy for understanding to more

CheckForSerializableConflictIn() directly before gistinserttuple().

The difference is that after lock we have conditions to change page,
and before gistinserttuple() we have actual intent to change page.

From the point of future development first version is better (if some
new calls occasionally spawn in), but it has 3 calls while your
proposal have 2 calls.
It seems to me that CheckForSerializableConflictIn() before
gistinserttuple() is better as for now.

Agree.

I have updated the location of CheckForSerializableConflictIn() and
changed the status of the patch to "needs review".

Regards,
Shubham

Attachments:

Predicate-locking-in-gist-index_3.patchapplication/octet-stream; name=Predicate-locking-in-gist-index_3.patchDownload
From 3b8acd46982bc2ca212dda2acb89c3e0a55d875b Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Sun, 1 Oct 2017 23:42:41 +0530
Subject: [PATCH] Predicate locking in gist index

---
 src/backend/access/gist/gist.c                   |  12 +-
 src/backend/access/gist/gistget.c                |   3 +
 src/backend/storage/lmgr/README-SSI              |   5 +-
 src/test/isolation/expected/predicate-gist-2.out | 321 +++++++++++++++++++++
 src/test/isolation/expected/predicate-gist.out   | 339 +++++++++++++++++++++++
 src/test/isolation/isolation_schedule            |   2 +
 src/test/isolation/specs/predicate-gist-2.spec   |  44 +++
 src/test/isolation/specs/predicate-gist.spec     |  44 +++
 8 files changed, 767 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist-2.out
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist-2.spec
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 565525b..a64b833
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -733,6 +740,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 					}
 				}
 
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
 				/*
 				 * Update the tuple.
 				 *
@@ -827,6 +835,8 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 760ea0c..4fe5be2
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist-2.out b/src/test/isolation/expected/predicate-gist-2.out
new file mode 100644
index 0000000..8f54137
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist-2.out
@@ -0,0 +1,321 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx1: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..56bdf3b
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,339 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+26500          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3000           
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(750,750);
+sum            
+
+22000          
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(250, 250);
+sum            
+
+3500           
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 32c965b..e95036c 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -62,3 +62,5 @@ test: sequence-ddl
 test: async-notify
 test: vacuum-reltuples
 test: timeouts
+test: predicate-gist
+test: predicate-gist-2
diff --git a/src/test/isolation/specs/predicate-gist-2.spec b/src/test/isolation/specs/predicate-gist-2.spec
new file mode 100644
index 0000000..1d67203
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist-2.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to check false positives.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the different part(sub-tree) of the index.
+
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+session "s2"
+setup		{ 
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..400a1cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,44 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures.
+#
+# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the same part(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 100) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(250, 250); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(15, 20) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(750,750); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 5) g; }
+step "c2"	{ COMMIT; }
-- 
1.9.1

#14Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Shubham Barai (#13)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On Thu, Oct 5, 2017 at 9:48 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 3 October 2017 at 00:32, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Mon, Oct 2, 2017 at 9:11 PM, Andrew Borodin <amborodin86@gmail.com>
wrote:

On Mon, Oct 2, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))
{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without calling
CheckForSerializableConflictIn().

Indeed! I've overlooked it. I'm remembering this issue, we were
considering not fixing splits if in Serializable isolation, but
dropped the idea.

Yeah, current insert algorithm assumes that split must be fixed before we
can correctly traverse the tree downwards.

CheckForSerializableConflictIn() must be after every exclusive lock.

I'm not sure, that fixing split is the case to necessary call
CheckForSerializableConflictIn(). This lock on leaf page is not taken
to do modification of the page. It's just taken to ensure that nobody else
is fixing this split the same this. After fixing the split, it might
appear that insert would go to another page.

I think it would be rather safe and easy for understanding to more

CheckForSerializableConflictIn() directly before gistinserttuple().

The difference is that after lock we have conditions to change page,
and before gistinserttuple() we have actual intent to change page.

From the point of future development first version is better (if some
new calls occasionally spawn in), but it has 3 calls while your
proposal have 2 calls.
It seems to me that CheckForSerializableConflictIn() before
gistinserttuple() is better as for now.

Agree.

I have updated the location of CheckForSerializableConflictIn() and
changed the status of the patch to "needs review".

Now, ITSM that predicate locks and conflict checks are placed right for now.
However, it would be good to add couple comments to gistdoinsert() whose
would state why do we call CheckForSerializableConflictIn() in these
particular places.

I also take a look at isolation tests. You made two separate test specs:
one to verify that serialization failures do fire, and another to check
there are no false positives.
I wonder if we could merge this two test specs into one, but use more
variety of statements with different keys for both inserts and selects.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#15Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#14)
1 attachment(s)
Re: GSoC 2017 : Patch for predicate locking in Gist index

On 9 October 2017 at 18:57, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Thu, Oct 5, 2017 at 9:48 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 3 October 2017 at 00:32, Alexander Korotkov <a.korotkov@postgrespro.ru

wrote:

On Mon, Oct 2, 2017 at 9:11 PM, Andrew Borodin <amborodin86@gmail.com>
wrote:

On Mon, Oct 2, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What happen if exactly this "continue" fires?

if (GistFollowRight(stack->page))
{
if (!xlocked)
{
LockBuffer(stack->buffer, GIST_UNLOCK);
LockBuffer(stack->buffer, GIST_EXCLUSIVE);
xlocked = true;
/* someone might've completed the split when we unlocked */
if (!GistFollowRight(stack->page))
continue;

In this case we might get xlocked == true without calling
CheckForSerializableConflictIn().

Indeed! I've overlooked it. I'm remembering this issue, we were
considering not fixing splits if in Serializable isolation, but
dropped the idea.

Yeah, current insert algorithm assumes that split must be fixed before
we can correctly traverse the tree downwards.

CheckForSerializableConflictIn() must be after every exclusive lock.

I'm not sure, that fixing split is the case to necessary call
CheckForSerializableConflictIn(). This lock on leaf page is not taken
to do modification of the page. It's just taken to ensure that nobody else
is fixing this split the same this. After fixing the split, it might
appear that insert would go to another page.

I think it would be rather safe and easy for understanding to more

CheckForSerializableConflictIn() directly before gistinserttuple().

The difference is that after lock we have conditions to change page,
and before gistinserttuple() we have actual intent to change page.

From the point of future development first version is better (if some
new calls occasionally spawn in), but it has 3 calls while your
proposal have 2 calls.
It seems to me that CheckForSerializableConflictIn() before
gistinserttuple() is better as for now.

Agree.

I have updated the location of CheckForSerializableConflictIn() and
changed the status of the patch to "needs review".

Now, ITSM that predicate locks and conflict checks are placed right for
now.
However, it would be good to add couple comments to gistdoinsert() whose
would state why do we call CheckForSerializableConflictIn() in these
particular places.

I also take a look at isolation tests. You made two separate test specs:
one to verify that serialization failures do fire, and another to check
there are no false positives.
I wonder if we could merge this two test specs into one, but use more
variety of statements with different keys for both inserts and selects.

Please find the updated version of patch here. I have made suggested
changes.

Regards,
Shubham

Attachments:

Predicate-locking-in-gist-index_4.patchapplication/octet-stream; name=Predicate-locking-in-gist-index_4.patchDownload
From 4489079068ed9789ea70dbb016eb341d38ca163f Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Sun, 1 Oct 2017 23:42:41 +0530
Subject: [PATCH] Predicate locking in gist index

---
 src/backend/access/gist/gist.c                 |  20 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   2 +
 src/test/isolation/specs/predicate-gist.spec   | 106 ++++
 6 files changed, 792 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 565525b..849bbb7
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -734,6 +741,11 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 *Check for any r-w conflicts (in serialisation isolation level)
+				 *just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -827,6 +839,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 *Check for any r-w conflicts (in serialisation isolation level)
+			 *just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 760ea0c..4fe5be2
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..ca11510
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 32c965b..4fb9500 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -62,3 +62,5 @@ test: sequence-ddl
 test: async-notify
 test: vacuum-reltuples
 test: timeouts
+test: predicate-gist
+
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..62fd31d
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,106 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access the same part(sub-tree) of the index.
+#
+# To check reduced false positives, queries and permutations are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access different parts(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup	
+		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
+
+# An index scan(from one transaction) and an index insert(from another transaction) try to access the same part of the index but one transaction commits before other transaction begins so no r-w comflict.
+#
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction) try to access different parts of the index and also one transaction commits before other transaction begins, so no r-w comflict.
+#
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan(from one transaction) and an index insert(from another transaction) try to access the same part of the index and one transaction begins before other transaction commits so there is a r-w comflict.
+#
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction) try to access different parts of the index so no r-w comflict.
+#
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#16Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Shubham Barai (#15)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Hi, Shubham!

On Wed, Nov 1, 2017 at 12:10 AM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 9 October 2017 at 18:57, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

Now, ITSM that predicate locks and conflict checks are placed right for
now.
However, it would be good to add couple comments to gistdoinsert() whose
would state why do we call CheckForSerializableConflictIn() in these
particular places.

I also take a look at isolation tests. You made two separate test specs:
one to verify that serialization failures do fire, and another to check
there are no false positives.
I wonder if we could merge this two test specs into one, but use more
variety of statements with different keys for both inserts and selects.

Please find the updated version of patch here. I have made suggested
changes.

In general, patch looks good for me now. I just see some cosmetic issues.

/*

+ *Check for any r-w conflicts (in serialisation isolation level)
+ *just before we intend to modify the page
+ */
+ CheckForSerializableConflictIn(r, NULL, stack->buffer);
+ /*

Formatting doesn't look good here. You've missed space after star sign in
the comment. You also missed newline after
CheckForSerializableConflictIn() call.

Also, you've long comment lines in predicate-gist.spec. Please, break long
comments into multiple lines.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#17Michael Paquier
michael.paquier@gmail.com
In reply to: Alexander Korotkov (#16)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On Mon, Nov 27, 2017 at 4:47 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

Also, you've long comment lines in predicate-gist.spec. Please, break long
comments into multiple lines.

Two days is to short to reply. I am moving this patch to next CF.
--
Michael

#18Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#16)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On 27 November 2017 at 13:17, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

Hi, Shubham!

On Wed, Nov 1, 2017 at 12:10 AM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 9 October 2017 at 18:57, Alexander Korotkov <a.korotkov@postgrespro.ru

wrote:

Now, ITSM that predicate locks and conflict checks are placed right for
now.
However, it would be good to add couple comments to gistdoinsert() whose
would state why do we call CheckForSerializableConflictIn() in these
particular places.

I also take a look at isolation tests. You made two separate test
specs: one to verify that serialization failures do fire, and another to
check there are no false positives.
I wonder if we could merge this two test specs into one, but use more
variety of statements with different keys for both inserts and selects.

Please find the updated version of patch here. I have made suggested
changes.

In general, patch looks good for me now. I just see some cosmetic issues.

/*

+ *Check for any r-w conflicts (in serialisation isolation level)
+ *just before we intend to modify the page
+ */
+ CheckForSerializableConflictIn(r, NULL, stack->buffer);
+ /*

Formatting doesn't look good here. You've missed space after star sign in
the comment. You also missed newline after CheckForSerializableConflictIn()
call.

Also, you've long comment lines in predicate-gist.spec. Please, break
long comments into multiple lines.

I have fixed formatting style. Please take a look at updated patch.

Regards,
Shubham

Show quoted text

Attachments:

Predicate-locking-in-gist-index_5.patchtext/x-patch; charset=US-ASCII; name=Predicate-locking-in-gist-index_5.patchDownload
From d7780debdcce60340aebcef06bb03f12419dbbeb Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Sun, 1 Oct 2017 23:42:41 +0530
Subject: [PATCH] Predicate locking in gist index

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   2 +
 src/test/isolation/specs/predicate-gist.spec   | 117 +++++
 6 files changed, 804 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 565525b..74e8c7c
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -734,6 +741,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -827,6 +840,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 760ea0c..4fe5be2
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..ca11510
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 32c965b..4fb9500 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -62,3 +62,5 @@ test: sequence-ddl
 test: async-notify
 test: vacuum-reltuples
 test: timeouts
+test: predicate-gist
+
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..ce9973c
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,117 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan(from one transaction) and an index insert(from another
+# transaction) will try to access the same part(sub-tree) of the index.
+#
+# To check reduced false positives, queries and permutations are written in such
+# a way that an index scan(from one transaction) and an index insert(from another
+# transaction) will try to access different parts(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup	
+		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access the same part of the index but one transaction commits before other
+# transaction begins so no r-w comflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access different parts of the index and also one transaction commits before
+# other transaction begins, so no r-w comflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access the same part of the index and one transaction begins before other
+# transaction commits so there is a r-w comflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access different parts of the index so no r-w comflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#19Andrey Borodin
x4mmm@yandex-team.ru
In reply to: Shubham Barai (#18)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Hello everyone!

29 нояб. 2017 г., в 22:50, Shubham Barai <shubhambaraiss@gmail.com> написал(а):

I have fixed formatting style. Please take a look at updated patch.

Here's rebased patch. Every issue has been addressed, so I'm marking this patch as ready for committer.

Best regards, Andrey Borodin.

Attachments:

0001-Predicate-locking-in-GiST-index-v6.patchapplication/octet-stream; name=0001-Predicate-locking-in-GiST-index-v6.patch; x-unix-mode=0644Download
From 4017f787b854bd6f028e4dfe215a5cde19dfe110 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Thu, 4 Jan 2018 21:03:44 +0500
Subject: [PATCH] Predicate locking in GiST index v6

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   1 +
 src/test/isolation/specs/predicate-gist.spec   | 117 +++++
 6 files changed, 803 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index aff969ead4..c0b5c89d97
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -733,6 +740,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 					}
 				}
 
+				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 				/*
 				 * Update the tuple.
 				 *
@@ -827,6 +840,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index ca21cf7047..4368b7ae69
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f237..e221241f96
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000000..ca11510a74
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index befe676816..febe338ae1 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -65,3 +65,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-gist
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000000..ba80873fe2
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,117 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan(from one transaction) and an index insert(from another
+# transaction) will try to access the same part(sub-tree) of the index.
+#
+# To check reduced false positives, queries and permutations are written in such
+# a way that an index scan(from one transaction) and an index insert(from another
+# transaction) will try to access different parts(sub-tree) of the index.
+
+setup
+{
+ create table gist_point_tbl(id int4, p point);
+ create index gist_pointidx on gist_point_tbl using gist(p);
+ insert into gist_point_tbl (id, p)
+ select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+ DROP TABLE gist_point_tbl;
+}
+
+session "s1"
+setup
+		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+                  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  BEGIN ISOLATION LEVEL SERIALIZABLE;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access the same part of the index but one transaction commits before other
+# transaction begins so no r-w comflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access different parts of the index and also one transaction commits before
+# other transaction begins, so no r-w comflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access the same part of the index and one transaction begins before other
+# transaction commits so there is a r-w comflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan(from one transaction) and an index insert(from another transaction)
+# try to access different parts of the index so no r-w comflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
2.14.3 (Apple Git-98)

#20Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Andrey Borodin (#19)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On Thu, Jan 4, 2018 at 7:07 PM, Andrey Borodin <x4mmm@yandex-team.ru> wrote:

29 нояб. 2017 г., в 22:50, Shubham Barai <shubhambaraiss@gmail.com>
написал(а):

I have fixed formatting style. Please take a look at updated patch.

Here's rebased patch. Every issue has been addressed, so I'm marking this
patch as ready for committer.

I'm sorry for concentrating on boring things, but formatting of
predicate-gist.spec still doesn't look good for me.

# To verify serialization failures, queries and permutations are written in

such
# a way that an index scan(from one transaction) and an index insert(from
another
# transaction) will try to access the same part(sub-tree) of the index.
#
# To check reduced false positives, queries and permutations are written
in such
# a way that an index scan(from one transaction) and an index insert(from
another
# transaction) will try to access different parts(sub-tree) of the index.

No space before open bracket (I think it should be when there are multiple
words brackets).
Also, we're trying to fit our lines to 80 characters (if it's not
objectively difficult).
And these are two almost same paragraphs. I think it should be simplified.

setup

{
create table gist_point_tbl(id int4, p point);
create index gist_pointidx on gist_point_tbl using gist(p);
insert into gist_point_tbl (id, p)
select g, point(g*10, g*10) from generate_series(1, 1000) g;
}
setup
{
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}
setup {
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}

I didn't get idea of using various indentation styles for same purpose.

step "wx3" { insert into gist_point_tbl (id, p)

select g, point(g*500, g*500) from generate_series(12,
18) g; }

Indented using spaces here...

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#21Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#20)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On 5 January 2018 at 03:18, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Thu, Jan 4, 2018 at 7:07 PM, Andrey Borodin <x4mmm@yandex-team.ru>
wrote:

29 нояб. 2017 г., в 22:50, Shubham Barai <shubhambaraiss@gmail.com>
написал(а):

I have fixed formatting style. Please take a look at updated patch.

Here's rebased patch. Every issue has been addressed, so I'm marking this
patch as ready for committer.

I'm sorry for concentrating on boring things, but formatting of
predicate-gist.spec still doesn't look good for me.

# To verify serialization failures, queries and permutations are written

in such
# a way that an index scan(from one transaction) and an index insert(from
another
# transaction) will try to access the same part(sub-tree) of the index.
#
# To check reduced false positives, queries and permutations are written
in such
# a way that an index scan(from one transaction) and an index insert(from
another
# transaction) will try to access different parts(sub-tree) of the index.

No space before open bracket (I think it should be when there are multiple
words brackets).
Also, we're trying to fit our lines to 80 characters (if it's not
objectively difficult).
And these are two almost same paragraphs. I think it should be simplified.

setup

{
create table gist_point_tbl(id int4, p point);
create index gist_pointidx on gist_point_tbl using gist(p);
insert into gist_point_tbl (id, p)
select g, point(g*10, g*10) from generate_series(1, 1000) g;
}
setup
{
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}
setup {
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}

I didn't get idea of using various indentation styles for same purpose.

step "wx3" { insert into gist_point_tbl (id, p)

select g, point(g*500, g*500) from generate_series(12,
18) g; }

Indented using spaces here...

I have fixed formatting issues. Please have a look at updated patch.

Regards,
Shubham

Attachments:

Predicate-locking-in-gist-index_7.patchapplication/octet-stream; name=Predicate-locking-in-gist-index_7.patchDownload
From 9776ccad44fbfcbe868925d03fce2b36683b976c Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Sun, 1 Oct 2017 23:42:41 +0530
Subject: [PATCH] Predicate locking in gist index

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   2 +
 src/test/isolation/specs/predicate-gist.spec   | 110 +++++
 6 files changed, 797 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 565525b..74e8c7c
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -734,6 +741,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -827,6 +840,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 760ea0c..4fe5be2
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..302079b
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: COMMIT;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: COMMIT;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
+step c2: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: COMMIT;
+step c1: COMMIT;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: COMMIT;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 32c965b..4fb9500 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -62,3 +62,5 @@ test: sequence-ddl
 test: async-notify
 test: vacuum-reltuples
 test: timeouts
+test: predicate-gist
+
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..35a31e4
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,110 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup		{
+ 		  create table gist_point_tbl(id int4, p point);
+ 		  create index gist_pointidx on gist_point_tbl using gist(p);
+ 		  insert into gist_point_tbl (id, p)
+ 		  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+		}
+
+teardown	{ drop table gist_point_tbl; }
+
+session "s1"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ COMMIT; }
+
+
+session "s2"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ COMMIT; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access the same part of the index and one transaction 
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#22Shubham Barai
shubhambaraiss@gmail.com
In reply to: Shubham Barai (#21)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On 8 January 2018 at 22:44, Shubham Barai <shubhambaraiss@gmail.com> wrote:

On 5 January 2018 at 03:18, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Thu, Jan 4, 2018 at 7:07 PM, Andrey Borodin <x4mmm@yandex-team.ru>
wrote:

29 нояб. 2017 г., в 22:50, Shubham Barai <shubhambaraiss@gmail.com>
написал(а):

I have fixed formatting style. Please take a look at updated patch.

Here's rebased patch. Every issue has been addressed, so I'm marking
this patch as ready for committer.

I'm sorry for concentrating on boring things, but formatting of
predicate-gist.spec still doesn't look good for me.

# To verify serialization failures, queries and permutations are written

in such
# a way that an index scan(from one transaction) and an index
insert(from another
# transaction) will try to access the same part(sub-tree) of the index.
#
# To check reduced false positives, queries and permutations are written
in such
# a way that an index scan(from one transaction) and an index
insert(from another
# transaction) will try to access different parts(sub-tree) of the index.

No space before open bracket (I think it should be when there are
multiple words brackets).
Also, we're trying to fit our lines to 80 characters (if it's not
objectively difficult).
And these are two almost same paragraphs. I think it should be
simplified.

setup

{
create table gist_point_tbl(id int4, p point);
create index gist_pointidx on gist_point_tbl using gist(p);
insert into gist_point_tbl (id, p)
select g, point(g*10, g*10) from generate_series(1, 1000) g;
}
setup
{
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}
setup {
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}

I didn't get idea of using various indentation styles for same purpose.

step "wx3" { insert into gist_point_tbl (id, p)

select g, point(g*500, g*500) from generate_series(12,
18) g; }

Indented using spaces here...

I have fixed formatting issues. Please have a look at updated patch.

Regards,
Shubham

Attachments:

Predicate-locking-in-gist_index_v7.patchapplication/octet-stream; name=Predicate-locking-in-gist_index_v7.patchDownload
From 6ab7382c5766010a93d36008a1fdfd18c3e73202 Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Sun, 1 Oct 2017 23:42:41 +0530
Subject: [PATCH] Predicate locking in gist index

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   2 +
 src/test/isolation/specs/predicate-gist.spec   | 110 +++++
 6 files changed, 797 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 565525b..74e8c7c
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -734,6 +741,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -827,6 +840,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index 760ea0c..4fe5be2
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..8965ccc
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 32c965b..4fb9500 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -62,3 +62,5 @@ test: sequence-ddl
 test: async-notify
 test: vacuum-reltuples
 test: timeouts
+test: predicate-gist
+
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..9db53cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,110 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup		{
+ 		  create table gist_point_tbl(id int4, p point);
+ 		  create index gist_pointidx on gist_point_tbl using gist(p);
+ 		  insert into gist_point_tbl (id, p)
+ 		  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+		}
+
+teardown	{ drop table gist_point_tbl; }
+
+session "s1"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ commit; }
+
+
+session "s2"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ commit; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access the same part of the index and one transaction 
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#23Shubham Barai
shubhambaraiss@gmail.com
In reply to: Shubham Barai (#22)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On 8 January 2018 at 23:13, Shubham Barai <shubhambaraiss@gmail.com> wrote:

On 8 January 2018 at 22:44, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 5 January 2018 at 03:18, Alexander Korotkov <a.korotkov@postgrespro.ru

wrote:

On Thu, Jan 4, 2018 at 7:07 PM, Andrey Borodin <x4mmm@yandex-team.ru>
wrote:

29 нояб. 2017 г., в 22:50, Shubham Barai <shubhambaraiss@gmail.com>
написал(а):

I have fixed formatting style. Please take a look at updated patch.

Here's rebased patch. Every issue has been addressed, so I'm marking
this patch as ready for committer.

I'm sorry for concentrating on boring things, but formatting of
predicate-gist.spec still doesn't look good for me.

# To verify serialization failures, queries and permutations are written

in such
# a way that an index scan(from one transaction) and an index
insert(from another
# transaction) will try to access the same part(sub-tree) of the index.
#
# To check reduced false positives, queries and permutations are
written in such
# a way that an index scan(from one transaction) and an index
insert(from another
# transaction) will try to access different parts(sub-tree) of the
index.

No space before open bracket (I think it should be when there are
multiple words brackets).
Also, we're trying to fit our lines to 80 characters (if it's not
objectively difficult).
And these are two almost same paragraphs. I think it should be
simplified.

setup

{
create table gist_point_tbl(id int4, p point);
create index gist_pointidx on gist_point_tbl using gist(p);
insert into gist_point_tbl (id, p)
select g, point(g*10, g*10) from generate_series(1, 1000) g;
}
setup
{
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}
setup {
BEGIN ISOLATION LEVEL SERIALIZABLE;
set enable_seqscan=off;
set enable_bitmapscan=off;
set enable_indexonlyscan=on;
}

I didn't get idea of using various indentation styles for same purpose.

step "wx3" { insert into gist_point_tbl (id, p)

select g, point(g*500, g*500) from
generate_series(12, 18) g; }

Indented using spaces here...

I have fixed formatting issues. Please have a look at updated patch.

The previous patch couldn't be applied cleanly because there were some
modifications to isolation_schedule. I have updated the patch now.

Regards,
Shubham

Attachments:

Predicate-Locking-in-gist-index_v8.patchapplication/octet-stream; name=Predicate-Locking-in-gist-index_v8.patchDownload
From 352832d21626f4ffc42468455449540f33879e00 Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Wed, 10 Jan 2018 23:42:04 +0530
Subject: [PATCH] Predicate Locking in gist index

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   1 +
 src/test/isolation/specs/predicate-gist.spec   | 110 +++++
 6 files changed, 796 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 51c32e4..19b6d5f
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -735,6 +742,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -828,6 +841,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index b30b931..c4e8a3b
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..8965ccc
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index befe676..febe338 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -65,3 +65,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-gist
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..9db53cf
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,110 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup		{
+ 		  create table gist_point_tbl(id int4, p point);
+ 		  create index gist_pointidx on gist_point_tbl using gist(p);
+ 		  insert into gist_point_tbl (id, p)
+ 		  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+		}
+
+teardown	{ drop table gist_point_tbl; }
+
+session "s1"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ commit; }
+
+
+session "s2"
+setup		{
+		  begin isolation level serializable;
+		  set enable_seqscan=off;
+		  set enable_bitmapscan=off;
+		  set enable_indexonlyscan=on;
+		}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+		  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ commit; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access the same part of the index and one transaction 
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#24Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Shubham Barai (#23)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On Wed, Jan 10, 2018 at 9:55 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

The previous patch couldn't be applied cleanly because there were some
modifications to isolation_schedule. I have updated the patch now.

In the attached patch indentation is still looking strange.
I've contacted Shubham using Telegram, and we realized that
it's because he used tab width 8 in his editor.
Shumham seems to have updated version of this patch, but didn't
post it yet. Thus, I'm marking this "Waiting on author" until
the updated patch is posted.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#25Shubham Barai
shubhambaraiss@gmail.com
In reply to: Alexander Korotkov (#24)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On 25 January 2018 at 18:40, Alexander Korotkov <a.korotkov@postgrespro.ru>
wrote:

On Wed, Jan 10, 2018 at 9:55 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

The previous patch couldn't be applied cleanly because there were some
modifications to isolation_schedule. I have updated the patch now.

In the attached patch indentation is still looking strange.
I've contacted Shubham using Telegram, and we realized that
it's because he used tab width 8 in his editor.
Shumham seems to have updated version of this patch, but didn't
post it yet. Thus, I'm marking this "Waiting on author" until
the updated patch is posted.

I have fixed formatting issues. Please take a look at updated patch.

Regards,
Shubham

Attachments:

Predicate-Locking-in-gist-index_v9.patchapplication/octet-stream; name=Predicate-Locking-in-gist-index_v9.patchDownload
From adb1b96396e92f123a7aee3618c3c587f1c3496e Mon Sep 17 00:00:00 2001
From: shubhambaraiss <you@example.com>
Date: Wed, 10 Jan 2018 23:42:04 +0530
Subject: [PATCH] Predicate Locking in gist index

---
 src/backend/access/gist/gist.c                 |  21 +-
 src/backend/access/gist/gistget.c              |   3 +
 src/backend/storage/lmgr/README-SSI            |   5 +-
 src/test/isolation/expected/predicate-gist.out | 659 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule          |   1 +
 src/test/isolation/specs/predicate-gist.spec   | 117 +++++
 6 files changed, 803 insertions(+), 3 deletions(-)
 mode change 100644 => 100755 src/backend/access/gist/gist.c
 mode change 100644 => 100755 src/backend/access/gist/gistget.c
 mode change 100644 => 100755 src/backend/storage/lmgr/README-SSI
 create mode 100644 src/test/isolation/expected/predicate-gist.out
 create mode 100644 src/test/isolation/specs/predicate-gist.spec

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
old mode 100644
new mode 100755
index 51c32e4..19b6d5f
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -446,6 +448,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
@@ -735,6 +742,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 
 				/*
+				 * Check for any r-w conflicts (in serialisation isolation level)
+				 * just before we intend to modify the page
+				 */
+				CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
+				/*
 				 * Update the tuple.
 				 *
 				 * We still hold the lock after gistinserttuple(), but it
@@ -828,6 +841,12 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
 				}
 			}
 
+			/*
+			 * Check for any r-w conflicts (in serialisation isolation level)
+			 * just before we intend to modify the page
+			 */
+			CheckForSerializableConflictIn(r, NULL, stack->buffer);
+
 			/* now state.stack->(page, buffer and blkno) points to leaf page */
 
 			gistinserttuple(&state, stack, giststate, itup,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
old mode 100644
new mode 100755
index b30b931..c4e8a3b
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
old mode 100644
new mode 100755
index a9dc01f..e221241
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000..77a2795
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index befe676..febe338 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -65,3 +65,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-gist
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000..a5d3f64
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,117 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup
+{
+  create table gist_point_tbl(id int4, p point);
+  create index gist_pointidx on gist_point_tbl using gist(p);
+  insert into gist_point_tbl (id, p)
+  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+  drop table gist_point_tbl;
+}
+
+session "s1"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ commit; }
+
+
+session "s2"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ commit; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access the same part of the index and one transaction 
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
-- 
1.9.1

#26Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Shubham Barai (#25)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

On Thu, Jan 25, 2018 at 5:13 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

On 25 January 2018 at 18:40, Alexander Korotkov <a.korotkov@postgrespro.ru

wrote:

On Wed, Jan 10, 2018 at 9:55 PM, Shubham Barai <shubhambaraiss@gmail.com>
wrote:

The previous patch couldn't be applied cleanly because there were some
modifications to isolation_schedule. I have updated the patch now.

In the attached patch indentation is still looking strange.
I've contacted Shubham using Telegram, and we realized that
it's because he used tab width 8 in his editor.
Shumham seems to have updated version of this patch, but didn't
post it yet. Thus, I'm marking this "Waiting on author" until
the updated patch is posted.

I have fixed formatting issues. Please take a look at updated patch.

Now it looks good for me. I'm marking it "Ready for committer".

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#27Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#26)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Hi!

Now it looks good for me.О©╫ I'm marking it "Ready for committer".

I have a question: why do not CheckForSerializableConflictIn() move into
begining of gistplacetopage()? Seems, it is the single function which actually
changes page and all predicate locking stuff will be placed in single function...

See attached version of patch.
--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

Attachments:

Predicate-Locking-in-gist-index_v10.patchtext/x-patch; name=Predicate-Locking-in-gist-index_v10.patchDownload
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 51c32e4afe..5ee1e5e4e1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -224,6 +226,12 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 	int			i;
 	bool		is_split;
 
+	/*
+	 * Check for any rw conflicts (in serialisation isolation level)
+	 * just before we intend to modify the page
+	 */
+	CheckForSerializableConflictIn(rel, NULL, buffer);
+
 	/*
 	 * Refuse to modify a page that's incompletely split. This should not
 	 * happen because we finish any incomplete splits while we walk down the
@@ -446,6 +454,11 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GistPageSetNSN(ptr->page, oldnsn);
 		}
 
+		for (ptr = dist; ptr; ptr = ptr->next)
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
+
 		/*
 		 * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
 		 * insertion for that. NB: The number of pages and data segments
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index b30b931c3b..c4e8a3b913 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
index a9dc01f237..e221241f96 100644
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000000..77a27958af
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 74d7d59546..53e1f192b0 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -66,3 +66,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-gist
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000000..a5d3f6456f
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,117 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup
+{
+  create table gist_point_tbl(id int4, p point);
+  create index gist_pointidx on gist_point_tbl using gist(p);
+  insert into gist_point_tbl (id, p)
+  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+  drop table gist_point_tbl;
+}
+
+session "s1"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ commit; }
+
+
+session "s2"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ commit; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access the same part of the index and one transaction 
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
#28Andrey Borodin
x4mmm@yandex-team.ru
In reply to: Teodor Sigaev (#27)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Hi1

27 марта 2018 г., в 12:53, Teodor Sigaev <teodor@sigaev.ru> написал(а):

I have a question: why do not CheckForSerializableConflictIn() move into begining of gistplacetopage()? Seems, it is the single function which actually changes page and all predicate locking stuff will be placed in single function...

gistplacetopage() is called from
1. Buffered build - probably harmless
2. Finish split - i'm not sure about this. It seems to me that it is necessary... then your version is correct.

Best regards, Andrey Borodin.

#29Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Andrey Borodin (#28)
1 attachment(s)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Hi!

On Tue, Mar 27, 2018 at 11:16 AM, Andrey Borodin <x4mmm@yandex-team.ru>
wrote:

27 марта 2018 г., в 12:53, Teodor Sigaev <teodor@sigaev.ru> написал(а):

I have a question: why do not CheckForSerializableConflictIn() move

into begining of gistplacetopage()? Seems, it is the single function which
actually changes page and all predicate locking stuff will be placed in
single function...

gistplacetopage() is called from
1. Buffered build - probably harmless

Yes, harmless, but useless.

2. Finish split - i'm not sure about this. It seems to me that it is
necessary... then your version is correct.

Yes, it's necessary, because GiST scan can end up on non-leaf page. So,
scan and modify of same non-leaf page should conflict.

Checking for serializable conflicts from buffering build seems useless
overhead. gistplacetopage()
is called from only two places: gistinserttuples()
and gistbufferinginserttuples(). In order to evade
useless overhead for buffering build, I've moved
CheckForSerializableConflictIn() into gistinserttuples().

Also, I find that we call PredicateLockPageSplit() for every page produced
by split including
original. That also seems to cause extra overhead. This is why I've moved
PredicateLockPageSplit() into loop where we do assign new buffers.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

Predicate-Locking-in-gist-index_v11.patchapplication/octet-stream; name=Predicate-Locking-in-gist-index_v11.patchDownload
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 51c32e4afe..52c83b9cbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,8 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "nodes/execnodes.h"
 #include "utils/builtins.h"
 #include "utils/index_selfuncs.h"
@@ -70,7 +72,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amsearchnulls = true;
 	amroutine->amstorage = true;
 	amroutine->amclusterable = true;
-	amroutine->ampredlocks = false;
+	amroutine->ampredlocks = true;
 	amroutine->amcanparallel = false;
 	amroutine->amkeytype = InvalidOid;
 
@@ -337,6 +339,9 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 			GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
 			ptr->page = BufferGetPage(ptr->buffer);
 			ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
+			PredicateLockPageSplit(rel,
+						BufferGetBlockNumber(buffer),
+						BufferGetBlockNumber(ptr->buffer));
 		}
 
 		/*
@@ -1213,6 +1218,12 @@ gistinserttuples(GISTInsertState *state, GISTInsertStack *stack,
 	List	   *splitinfo;
 	bool		is_split;
 
+	/*
+	 * Check for any rw conflicts (in serialisation isolation level)
+	 * just before we intend to modify the page
+	 */
+	CheckForSerializableConflictIn(state->r, NULL, stack->buffer);
+
 	/* Insert the tuple(s) to the page, splitting the page if necessary */
 	is_split = gistplacetopage(state->r, state->freespace, giststate,
 							   stack->buffer,
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index b30b931c3b..c4e8a3b913 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -18,6 +18,8 @@
 #include "access/relscan.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
 #include "pgstat.h"
 #include "lib/pairingheap.h"
 #include "utils/builtins.h"
@@ -336,6 +338,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
+	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
 	gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI
index a9dc01f237..e221241f96 100644
--- a/src/backend/storage/lmgr/README-SSI
+++ b/src/backend/storage/lmgr/README-SSI
@@ -374,10 +374,11 @@ however, a search discovers that no root page has yet been created, a
 predicate lock on the index relation is required.
 
     * GiST searches can determine that there are no matches at any
-level of the index, so there must be a predicate lock at each index
+level of the index, so we acquire predicate lock at each index
 level during a GiST search. An index insert at the leaf level can
 then be trusted to ripple up to all levels and locations where
-conflicting predicate locks may exist.
+conflicting predicate locks may exist. In case there is a page split,
+we need to copy predicate lock from an original page to all new pages.
 
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
diff --git a/src/test/isolation/expected/predicate-gist.out b/src/test/isolation/expected/predicate-gist.out
new file mode 100644
index 0000000000..77a27958af
--- /dev/null
+++ b/src/test/isolation/expected/predicate-gist.out
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2233750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+316250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p[0]) from gist_point_tbl where p >> point(7500,7500);
+sum            
+
+2188750        
+step wy2: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g;
+step rxy1: select sum(p[0]) from gist_point_tbl where p << point(2500, 2500);
+sum            
+
+311250         
+step c2: commit;
+step wx1: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p[0]) from gist_point_tbl where p << point(1000,1000);
+sum            
+
+49500          
+step wy4: insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g;
+step rxy3: select sum(p[0]) from gist_point_tbl where p >> point(6000,6000);
+sum            
+
+3202000        
+step c2: commit;
+step wx3: insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g;
+step c1: commit;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 74d7d59546..53e1f192b0 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -66,3 +66,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-gist
diff --git a/src/test/isolation/specs/predicate-gist.spec b/src/test/isolation/specs/predicate-gist.spec
new file mode 100644
index 0000000000..6d6021f5e4
--- /dev/null
+++ b/src/test/isolation/specs/predicate-gist.spec
@@ -0,0 +1,117 @@
+# Test for page level predicate locking in gist
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same part (sub-tree) of the index
+# whereas to check reduced false positives, they will try to access different
+# parts (sub-tree) of the index.
+
+setup
+{
+  create table gist_point_tbl(id int4, p point);
+  create index gist_pointidx on gist_point_tbl using gist(p);
+  insert into gist_point_tbl (id, p)
+  select g, point(g*10, g*10) from generate_series(1, 1000) g;
+}
+
+teardown
+{
+  drop table gist_point_tbl;
+}
+
+session "s1"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy1"	{ select sum(p[0]) from gist_point_tbl where p << point(2500, 2500); }
+step "wx1"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(15, 20) g; }
+step "rxy3"	{ select sum(p[0]) from gist_point_tbl where p >> point(6000,6000); }
+step "wx3"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(12, 18) g; }
+step "c1"	{ commit; }
+
+
+session "s2"
+setup
+{
+  begin isolation level serializable;
+  set enable_seqscan=off;
+  set enable_bitmapscan=off;
+  set enable_indexonlyscan=on;
+}
+
+step "rxy2"	{ select sum(p[0]) from gist_point_tbl where p >> point(7500,7500); }
+step "wy2"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*500, g*500) from generate_series(1, 5) g; }
+step "rxy4"	{ select sum(p[0]) from gist_point_tbl where p << point(1000,1000); }
+step "wy4"	{ insert into gist_point_tbl (id, p)
+			  select g, point(g*50, g*50) from generate_series(1, 20) g; }
+step "c2"	{ commit; }
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same part of the index and one transaction
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different parts of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"
#30Andrey Borodin
x4mmm@yandex-team.ru
In reply to: Alexander Korotkov (#29)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

27 марта 2018 г., в 13:45, Alexander Korotkov <a.korotkov@postgrespro.ru> написал(а):

On Tue, Mar 27, 2018 at 11:16 AM, Andrey Borodin <x4mmm@yandex-team.ru <mailto:x4mmm@yandex-team.ru>> wrote:

27 марта 2018 г., в 12:53, Teodor Sigaev <teodor@sigaev.ru <mailto:teodor@sigaev.ru>> написал(а):

I have a question: why do not CheckForSerializableConflictIn() move into begining of gistplacetopage()? Seems, it is the single function which actually changes page and all predicate locking stuff will be placed in single function...

gistplacetopage() is called from
1. Buffered build - probably harmless

Yes, harmless, but useless.

2. Finish split - i'm not sure about this. It seems to me that it is necessary... then your version is correct.

Yes, it's necessary, because GiST scan can end up on non-leaf page. So, scan and modify of same non-leaf page should conflict.

Checking for serializable conflicts from buffering build seems useless overhead. gistplacetopage()
is called from only two places: gistinserttuples() and gistbufferinginserttuples(). In order to evade
useless overhead for buffering build, I've moved CheckForSerializableConflictIn() into gistinserttuples().

+1
Also, both gistdoinsert() and gistplacetopage() are mind blowing in complexity. gistinserttuples() looks like a cosy place if we need to add some more.

Best regards, Andrey Borodin.

#31Teodor Sigaev
teodor@sigaev.ru
In reply to: Andrey Borodin (#30)
Re: [HACKERS] GSoC 2017 : Patch for predicate locking in Gist index

Thanks to everyone, pushed

Andrey Borodin wrote:

27 О©╫О©╫О©╫О©╫О©╫ 2018 О©╫., О©╫ 13:45, Alexander Korotkov <a.korotkov@postgrespro.ru
<mailto:a.korotkov@postgrespro.ru>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫(О©╫):

On Tue, Mar 27, 2018 at 11:16 AM, Andrey Borodin <x4mmm@yandex-team.ru
<mailto:x4mmm@yandex-team.ru>> wrote:

27 О©╫О©╫О©╫О©╫О©╫ 2018 О©╫., О©╫ 12:53, Teodor Sigaev <teodor@sigaev.ru

<mailto:teodor@sigaev.ru>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫(О©╫):

I have a question: why do not CheckForSerializableConflictIn() moveО©╫ into begining of gistplacetopage()? Seems, it is the single

function which actually changes page and all predicate locking stuff will
be placed in single function...

gistplacetopage() is called from
1. Buffered build - probably harmless

Yes, harmless, but useless.

2. Finish split - i'm not sure about this. It seems to me that it is
necessary... then your version is correct.

Yes, it's necessary, because GiST scan can end up on non-leaf page.О©╫ So, scan
and modify of same non-leaf page should conflict.

Checking for serializable conflicts from buffering build seems useless
overhead.О©╫ gistplacetopage()
is called from only two places:О©╫gistinserttuples()
andО©╫gistbufferinginserttuples().О©╫ In order to evade
useless overhead for buffering build, I've moved
CheckForSerializableConflictIn() intoО©╫gistinserttuples().

+1
Also, both gistdoinsert() and gistplacetopage() are mind blowing in complexity.
gistinserttuples() looks like a cosy place if we need to add some more.

Best regards, Andrey Borodin.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/