@@ -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 )
@@ -265,7 +270,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack)
265
270
ptr -> parent = root ;
266
271
ptr -> off = InvalidOffsetNumber ;
267
272
268
- ginFinishSplit (btree ,ptr ,false, NULL );
273
+ ginFinishOldSplit (btree ,ptr ,NULL , GIN_EXCLUSIVE );
269
274
}
270
275
271
276
leftmostBlkno = btree -> getLeftMostChild (btree ,page );
@@ -294,7 +299,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack)
294
299
ptr -> parent = root ;
295
300
ptr -> off = InvalidOffsetNumber ;
296
301
297
- ginFinishSplit (btree ,ptr ,false, NULL );
302
+ ginFinishOldSplit (btree ,ptr ,NULL , GIN_EXCLUSIVE );
298
303
}
299
304
}
300
305
@@ -676,15 +681,6 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
676
681
bool done ;
677
682
bool first = true;
678
683
679
- /*
680
- * freestack == false when we encounter an incompletely split page during
681
- * a scan, while freestack == true is used in the normal scenario that a
682
- * split is finished right after the initial insert.
683
- */
684
- if (!freestack )
685
- elog (DEBUG1 ,"finishing incomplete split of block %u in gin index \"%s\"" ,
686
- stack -> blkno ,RelationGetRelationName (btree -> index ));
687
-
688
684
/* this loop crawls up the stack until the insertion is complete */
689
685
do
690
686
{
@@ -705,7 +701,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
705
701
* would fail.
706
702
*/
707
703
if (GinPageIsIncompleteSplit (BufferGetPage (parent -> buffer )))
708
- ginFinishSplit (btree ,parent ,false, buildStats );
704
+ ginFinishOldSplit (btree ,parent ,buildStats , GIN_EXCLUSIVE );
709
705
710
706
/* move right if it's needed */
711
707
page = BufferGetPage (parent -> buffer );
@@ -729,7 +725,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
729
725
page = BufferGetPage (parent -> buffer );
730
726
731
727
if (GinPageIsIncompleteSplit (BufferGetPage (parent -> buffer )))
732
- ginFinishSplit (btree ,parent ,false, buildStats );
728
+ ginFinishOldSplit (btree ,parent ,buildStats , GIN_EXCLUSIVE );
733
729
}
734
730
735
731
/* insert the downlink */
@@ -765,6 +761,42 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
765
761
freeGinBtreeStack (stack );
766
762
}
767
763
764
+ /*
765
+ * An entry point to ginFinishSplit() that is used when we stumble upon an
766
+ * existing incompletely split page in the tree, as opposed to completing a
767
+ * split that we just made outselves. The difference is that stack->buffer may
768
+ * be merely share-locked on entry, and will be upgraded to exclusive mode.
769
+ *
770
+ * Note: Upgrading the lock momentarily releases it. Doing that in a scan
771
+ * would not be OK, because a concurrent VACUUM might delete the page while
772
+ * we're not holding the lock. It's OK in an insert, though, because VACUUM
773
+ * has a different mechanism that prevents it from running concurrently with
774
+ * inserts. (Namely, it holds a cleanup lock on the root.)
775
+ */
776
+ static void
777
+ ginFinishOldSplit (GinBtree btree ,GinBtreeStack * stack ,GinStatsData * buildStats ,int access )
778
+ {
779
+ elog (DEBUG1 ,"finishing incomplete split of block %u in gin index \"%s\"" ,
780
+ stack -> blkno ,RelationGetRelationName (btree -> index ));
781
+
782
+ if (access == GIN_SHARE )
783
+ {
784
+ LockBuffer (stack -> buffer ,GIN_UNLOCK );
785
+ LockBuffer (stack -> buffer ,GIN_EXCLUSIVE );
786
+
787
+ if (!GinPageIsIncompleteSplit (BufferGetPage (stack -> buffer )))
788
+ {
789
+ /*
790
+ * Someone else already completed the split while we were not
791
+ * holding the lock.
792
+ */
793
+ return ;
794
+ }
795
+ }
796
+
797
+ ginFinishSplit (btree ,stack , false,buildStats );
798
+ }
799
+
768
800
/*
769
801
* Insert a value to tree described by stack.
770
802
*
@@ -785,7 +817,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, void *insertdata,
785
817
786
818
/* If the leaf page was incompletely split, finish the split first */
787
819
if (GinPageIsIncompleteSplit (BufferGetPage (stack -> buffer )))
788
- ginFinishSplit (btree ,stack ,false, buildStats );
820
+ ginFinishOldSplit (btree ,stack ,buildStats , GIN_EXCLUSIVE );
789
821
790
822
done = ginPlaceToPage (btree ,stack ,
791
823
insertdata ,InvalidBlockNumber ,