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