@@ -28,6 +28,8 @@ static bool ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
28
28
Buffer childbuf ,GinStatsData * buildStats );
29
29
static void ginFinishSplit (GinBtree btree ,GinBtreeStack * stack ,
30
30
bool freestack ,GinStatsData * buildStats );
31
+ static void ginFinishOldSplit (GinBtree btree ,GinBtreeStack * stack ,
32
+ GinStatsData * buildStats ,int access );
31
33
32
34
/*
33
35
* Lock buffer by needed method for search.
@@ -109,7 +111,7 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
109
111
* encounter on the way.
110
112
*/
111
113
if (!searchMode && GinPageIsIncompleteSplit (page ))
112
- ginFinishSplit (btree ,stack ,false, NULL );
114
+ ginFinishOldSplit (btree ,stack ,NULL , access );
113
115
114
116
/*
115
117
* ok, page is correctly locked, we should check to move right ..,
@@ -130,7 +132,7 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
130
132
TestForOldSnapshot (snapshot ,btree -> index ,page );
131
133
132
134
if (!searchMode && GinPageIsIncompleteSplit (page ))
133
- ginFinishSplit (btree ,stack ,false, NULL );
135
+ ginFinishOldSplit (btree ,stack ,NULL , access );
134
136
}
135
137
136
138
if (GinPageIsLeaf (page ))/* we found, return locked page */
@@ -166,8 +168,11 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
166
168
* Step right from current page.
167
169
*
168
170
* The next page is locked first, before releasing the current page. This is
169
- * crucial to protect from concurrent page deletion (see comment in
170
- * ginDeletePage).
171
+ * crucial to prevent concurrent VACUUM from deleting a page that we are about
172
+ * to step to. (The lock-coupling isn't strictly necessary when we are
173
+ * traversing the tree to find an insert location, because page deletion grabs
174
+ * a cleanup lock on the root to prevent any concurrent inserts. See Page
175
+ * deletion section in the README. But there's no harm in doing it always.)
171
176
*/
172
177
Buffer
173
178
ginStepRight (Buffer buffer ,Relation index ,int lockmode )
@@ -264,7 +269,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack)
264
269
ptr -> parent = root ;
265
270
ptr -> off = InvalidOffsetNumber ;
266
271
267
- ginFinishSplit (btree ,ptr ,false, NULL );
272
+ ginFinishOldSplit (btree ,ptr ,NULL , GIN_EXCLUSIVE );
268
273
}
269
274
270
275
leftmostBlkno = btree -> getLeftMostChild (btree ,page );
@@ -293,7 +298,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack)
293
298
ptr -> parent = root ;
294
299
ptr -> off = InvalidOffsetNumber ;
295
300
296
- ginFinishSplit (btree ,ptr ,false, NULL );
301
+ ginFinishOldSplit (btree ,ptr ,NULL , GIN_EXCLUSIVE );
297
302
}
298
303
}
299
304
@@ -675,15 +680,6 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
675
680
bool done ;
676
681
bool first = true;
677
682
678
- /*
679
- * freestack == false when we encounter an incompletely split page during
680
- * a scan, while freestack == true is used in the normal scenario that a
681
- * split is finished right after the initial insert.
682
- */
683
- if (!freestack )
684
- elog (DEBUG1 ,"finishing incomplete split of block %u in gin index \"%s\"" ,
685
- stack -> blkno ,RelationGetRelationName (btree -> index ));
686
-
687
683
/* this loop crawls up the stack until the insertion is complete */
688
684
do
689
685
{
@@ -704,7 +700,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
704
700
* would fail.
705
701
*/
706
702
if (GinPageIsIncompleteSplit (BufferGetPage (parent -> buffer )))
707
- ginFinishSplit (btree ,parent ,false, buildStats );
703
+ ginFinishOldSplit (btree ,parent ,buildStats , GIN_EXCLUSIVE );
708
704
709
705
/* move right if it's needed */
710
706
page = BufferGetPage (parent -> buffer );
@@ -728,7 +724,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
728
724
page = BufferGetPage (parent -> buffer );
729
725
730
726
if (GinPageIsIncompleteSplit (BufferGetPage (parent -> buffer )))
731
- ginFinishSplit (btree ,parent ,false, buildStats );
727
+ ginFinishOldSplit (btree ,parent ,buildStats , GIN_EXCLUSIVE );
732
728
}
733
729
734
730
/* insert the downlink */
@@ -764,6 +760,42 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
764
760
freeGinBtreeStack (stack );
765
761
}
766
762
763
+ /*
764
+ * An entry point to ginFinishSplit() that is used when we stumble upon an
765
+ * existing incompletely split page in the tree, as opposed to completing a
766
+ * split that we just made outselves. The difference is that stack->buffer may
767
+ * be merely share-locked on entry, and will be upgraded to exclusive mode.
768
+ *
769
+ * Note: Upgrading the lock momentarily releases it. Doing that in a scan
770
+ * would not be OK, because a concurrent VACUUM might delete the page while
771
+ * we're not holding the lock. It's OK in an insert, though, because VACUUM
772
+ * has a different mechanism that prevents it from running concurrently with
773
+ * inserts. (Namely, it holds a cleanup lock on the root.)
774
+ */
775
+ static void
776
+ ginFinishOldSplit (GinBtree btree ,GinBtreeStack * stack ,GinStatsData * buildStats ,int access )
777
+ {
778
+ elog (DEBUG1 ,"finishing incomplete split of block %u in gin index \"%s\"" ,
779
+ stack -> blkno ,RelationGetRelationName (btree -> index ));
780
+
781
+ if (access == GIN_SHARE )
782
+ {
783
+ LockBuffer (stack -> buffer ,GIN_UNLOCK );
784
+ LockBuffer (stack -> buffer ,GIN_EXCLUSIVE );
785
+
786
+ if (!GinPageIsIncompleteSplit (BufferGetPage (stack -> buffer )))
787
+ {
788
+ /*
789
+ * Someone else already completed the split while we were not
790
+ * holding the lock.
791
+ */
792
+ return ;
793
+ }
794
+ }
795
+
796
+ ginFinishSplit (btree ,stack , false,buildStats );
797
+ }
798
+
767
799
/*
768
800
* Insert a value to tree described by stack.
769
801
*
@@ -784,7 +816,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, void *insertdata,
784
816
785
817
/* If the leaf page was incompletely split, finish the split first */
786
818
if (GinPageIsIncompleteSplit (BufferGetPage (stack -> buffer )))
787
- ginFinishSplit (btree ,stack ,false, buildStats );
819
+ ginFinishOldSplit (btree ,stack ,buildStats , GIN_EXCLUSIVE );
788
820
789
821
done = ginPlaceToPage (btree ,stack ,
790
822
insertdata ,InvalidBlockNumber ,