@@ -35,20 +35,6 @@ typedef struct pendingPosition
3535} pendingPosition ;
3636
3737
38- /*
39- * Place predicate lock on GIN page if needed.
40- */
41- static void
42- GinPredicateLockPage (Relation index , BlockNumber blkno , Snapshot snapshot )
43- {
44- /*
45- * When fast update is on then no need in locking pages, because we anyway
46- * need to lock the whole index.
47- */
48- if (!GinGetUseFastUpdate (index ))
49- PredicateLockPage (index , blkno , snapshot );
50- }
51-
5238/*
5339 * Goes to the next page if current offset is outside of bounds
5440 */
@@ -68,7 +54,7 @@ moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot
6854 stack -> buffer = ginStepRight (stack -> buffer , btree -> index , GIN_SHARE );
6955 stack -> blkno = BufferGetBlockNumber (stack -> buffer );
7056 stack -> off = FirstOffsetNumber ;
71- GinPredicateLockPage (btree -> index , stack -> blkno , snapshot );
57+ PredicateLockPage (btree -> index , stack -> blkno , snapshot );
7258 }
7359
7460 return true;
@@ -100,11 +86,6 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
10086 */
10187 for (;;)
10288 {
103- /*
104- * Predicate lock each leaf page in posting tree
105- */
106- GinPredicateLockPage (index , BufferGetBlockNumber (buffer ), snapshot );
107-
10889 page = BufferGetPage (buffer );
10990 if ((GinPageGetOpaque (page )-> flags & GIN_DELETED ) == 0 )
11091 {
@@ -158,7 +139,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
158139 * Predicate lock entry leaf page, following pages will be locked by
159140 * moveRightIfItNeeded()
160141 */
161- GinPredicateLockPage (btree -> index , stack -> buffer , snapshot );
142+ PredicateLockPage (btree -> index , stack -> buffer , snapshot );
162143
163144 for (;;)
164145 {
@@ -253,6 +234,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
253234
254235 LockBuffer (stack -> buffer , GIN_UNLOCK );
255236
237+ /*
238+ * Acquire predicate lock on the posting tree. We already hold
239+ * a lock on the entry page, but insertions to the posting tree
240+ * don't check for conflicts on that level.
241+ */
242+ PredicateLockPage (btree -> index , rootPostingTree , snapshot );
243+
256244 /* Collect all the TIDs in this entry's posting tree */
257245 scanPostingTree (btree -> index , scanEntry , rootPostingTree ,
258246 snapshot );
@@ -400,17 +388,20 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
400388 {
401389 IndexTuple itup = (IndexTuple ) PageGetItem (page , PageGetItemId (page , stackEntry -> off ));
402390
403- /* Predicate lock visited entry leaf page */
404- GinPredicateLockPage (ginstate -> index ,
405- BufferGetBlockNumber (stackEntry -> buffer ), snapshot );
406-
407391 if (GinIsPostingTree (itup ))
408392 {
409393 BlockNumber rootPostingTree = GinGetPostingTree (itup );
410394 GinBtreeStack * stack ;
411395 Page page ;
412396 ItemPointerData minItem ;
413397
398+ /*
399+ * This is an equality scan, so lock the root of the posting tree.
400+ * It represents a lock on the exact key value, and covers all the
401+ * items in the posting tree.
402+ */
403+ PredicateLockPage (ginstate -> index , rootPostingTree , snapshot );
404+
414405 /*
415406 * We should unlock entry page before touching posting tree to
416407 * prevent deadlocks with vacuum processes. Because entry is never
@@ -425,12 +416,6 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
425416 rootPostingTree , snapshot );
426417 entry -> buffer = stack -> buffer ;
427418
428- /*
429- * Predicate lock visited posting tree page, following pages will
430- * be locked by moveRightIfItNeeded or entryLoadMoreItems
431- */
432- GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (entry -> buffer ), snapshot );
433-
434419 /*
435420 * We keep buffer pinned because we need to prevent deletion of
436421 * page during scan. See GIN's vacuum implementation. RefCount is
@@ -452,15 +437,38 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
452437 freeGinBtreeStack (stack );
453438 entry -> isFinished = false;
454439 }
455- else if ( GinGetNPosting ( itup ) > 0 )
440+ else
456441 {
457- entry -> list = ginReadTuple (ginstate , entry -> attnum , itup ,
458- & entry -> nlist );
459- entry -> predictNumberResult = entry -> nlist ;
442+ /*
443+ * Lock the entry leaf page. This is more coarse-grained than
444+ * necessary, because it will conflict with any insertions that
445+ * land on the same leaf page, not only the exacty key we searched
446+ * for. But locking an individual tuple would require updating
447+ * that lock whenever it moves because of insertions or vacuums,
448+ * which seems too complicated.
449+ */
450+ PredicateLockPage (ginstate -> index ,
451+ BufferGetBlockNumber (stackEntry -> buffer ),
452+ snapshot );
453+ if (GinGetNPosting (itup ) > 0 )
454+ {
455+ entry -> list = ginReadTuple (ginstate , entry -> attnum , itup ,
456+ & entry -> nlist );
457+ entry -> predictNumberResult = entry -> nlist ;
460458
461- entry -> isFinished = false;
459+ entry -> isFinished = false;
460+ }
462461 }
463462 }
463+ else
464+ {
465+ /*
466+ * No entry found. Predicate lock the leaf page, to lock the place
467+ * where the entry would've been, had there been one.
468+ */
469+ PredicateLockPage (ginstate -> index ,
470+ BufferGetBlockNumber (stackEntry -> buffer ), snapshot );
471+ }
464472
465473 if (needUnlock )
466474 LockBuffer (stackEntry -> buffer , GIN_UNLOCK );
@@ -533,7 +541,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
533541
534542 for (i = 0 ; i < key -> nentries - 1 ; i ++ )
535543 {
536- /* Pass all entries <= i as false , and the rest as MAYBE */
544+ /* Pass all entries <= i as FALSE , and the rest as MAYBE */
537545 for (j = 0 ; j <= i ; j ++ )
538546 key -> entryRes [entryIndexes [j ]] = GIN_FALSE ;
539547 for (j = i + 1 ; j < key -> nentries ; j ++ )
@@ -673,8 +681,6 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
673681 entry -> btree .fullScan = false;
674682 stack = ginFindLeafPage (& entry -> btree , true, snapshot );
675683
676- GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (stack -> buffer ), snapshot );
677-
678684 /* we don't need the stack, just the buffer. */
679685 entry -> buffer = stack -> buffer ;
680686 IncrBufferRefCount (entry -> buffer );
@@ -719,10 +725,6 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
719725 entry -> buffer = ginStepRight (entry -> buffer ,
720726 ginstate -> index ,
721727 GIN_SHARE );
722-
723- GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (entry -> buffer ), snapshot );
724-
725-
726728 page = BufferGetPage (entry -> buffer );
727729 }
728730 stepright = true;
@@ -1084,8 +1086,8 @@ keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key,
10841086 * lossy page even when none of the other entries match.
10851087 *
10861088 * Our strategy is to call the tri-state consistent function, with the
1087- * lossy-page entries set to MAYBE, and all the other entries false . If it
1088- * returns false , none of the lossy items alone are enough for a match, so
1089+ * lossy-page entries set to MAYBE, and all the other entries FALSE . If it
1090+ * returns FALSE , none of the lossy items alone are enough for a match, so
10891091 * we don't need to return a lossy-page pointer. Otherwise, return a
10901092 * lossy-page pointer to indicate that the whole heap page must be
10911093 * checked. (On subsequent calls, we'll do nothing until minItem is past
@@ -1746,8 +1748,7 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
17461748}
17471749
17481750/*
1749- * Collect all matched rows from pending list into bitmap. Also function
1750- * takes PendingLockRelation if it's needed.
1751+ * Collect all matched rows from pending list into bitmap.
17511752 */
17521753static void
17531754scanPendingInsert (IndexScanDesc scan , TIDBitmap * tbm , int64 * ntids )
@@ -1764,6 +1765,12 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
17641765
17651766 * ntids = 0 ;
17661767
1768+ /*
1769+ * Acquire predicate lock on the metapage, to conflict with any
1770+ * fastupdate insertions.
1771+ */
1772+ PredicateLockPage (scan -> indexRelation , GIN_METAPAGE_BLKNO , scan -> xs_snapshot );
1773+
17671774 LockBuffer (metabuffer , GIN_SHARE );
17681775 page = BufferGetPage (metabuffer );
17691776 TestForOldSnapshot (scan -> xs_snapshot , scan -> indexRelation , page );
@@ -1777,24 +1784,9 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
17771784 {
17781785 /* No pending list, so proceed with normal scan */
17791786 UnlockReleaseBuffer (metabuffer );
1780-
1781- /*
1782- * If fast update is enabled, we acquire a predicate lock on the
1783- * entire relation as fast update postpones the insertion of tuples
1784- * into index structure due to which we can't detect rw conflicts.
1785- */
1786- if (GinGetUseFastUpdate (scan -> indexRelation ))
1787- PredicateLockRelation (scan -> indexRelation , scan -> xs_snapshot );
1788-
17891787 return ;
17901788 }
17911789
1792- /*
1793- * Pending list is not empty, we need to lock the index doesn't despite on
1794- * fastupdate state
1795- */
1796- PredicateLockRelation (scan -> indexRelation , scan -> xs_snapshot );
1797-
17981790 pos .pendingBuffer = ReadBuffer (scan -> indexRelation , blkno );
17991791 LockBuffer (pos .pendingBuffer , GIN_SHARE );
18001792 pos .firstOffset = FirstOffsetNumber ;
0 commit comments