From: Teodor Sigaev <teodor@sigaev.ru>
Date: Tue, 27 Mar 2018 12:43:19 +0000 (+0300)
Subject: Add predicate locking for GiST
X-Git-Tag: REL_11_BETA1~471
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3ad55863e9392bff73377911ebbf9760027ed405;p=postgresql

Add predicate locking for GiST

Add page-level predicate locking, due to gist's code organization, patch seems
close to trivial: add check before page changing, add predicate lock before page
scanning.  Although choosing right place to check is not simple: it should not
be called during index build, it should support insertion of new downlink and so
on.

Author: Shubham Barai with editorization by me and Alexander Korotkov
Reviewed by: Alexander Korotkov, Andrey Borodin, me
Discussion: https://www.postgresql.org/message-id/flat/CALxAEPtdcANpw5ePU3LvnTP8HCENFw6wygupQAyNBgD-sG3h0g@mail.gmail.com
---

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"