@@ -35,20 +35,6 @@ typedef struct pendingPosition
35
35
} pendingPosition ;
36
36
37
37
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
-
52
38
/*
53
39
* Goes to the next page if current offset is outside of bounds
54
40
*/
@@ -68,7 +54,7 @@ moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot
68
54
stack -> buffer = ginStepRight (stack -> buffer , btree -> index , GIN_SHARE );
69
55
stack -> blkno = BufferGetBlockNumber (stack -> buffer );
70
56
stack -> off = FirstOffsetNumber ;
71
- GinPredicateLockPage (btree -> index , stack -> blkno , snapshot );
57
+ PredicateLockPage (btree -> index , stack -> blkno , snapshot );
72
58
}
73
59
74
60
return true;
@@ -100,11 +86,6 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
100
86
*/
101
87
for (;;)
102
88
{
103
- /*
104
- * Predicate lock each leaf page in posting tree
105
- */
106
- GinPredicateLockPage (index , BufferGetBlockNumber (buffer ), snapshot );
107
-
108
89
page = BufferGetPage (buffer );
109
90
if ((GinPageGetOpaque (page )-> flags & GIN_DELETED ) == 0 )
110
91
{
@@ -158,7 +139,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
158
139
* Predicate lock entry leaf page, following pages will be locked by
159
140
* moveRightIfItNeeded()
160
141
*/
161
- GinPredicateLockPage (btree -> index , stack -> buffer , snapshot );
142
+ PredicateLockPage (btree -> index , stack -> buffer , snapshot );
162
143
163
144
for (;;)
164
145
{
@@ -253,6 +234,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
253
234
254
235
LockBuffer (stack -> buffer , GIN_UNLOCK );
255
236
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
+
256
244
/* Collect all the TIDs in this entry's posting tree */
257
245
scanPostingTree (btree -> index , scanEntry , rootPostingTree ,
258
246
snapshot );
@@ -400,17 +388,20 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
400
388
{
401
389
IndexTuple itup = (IndexTuple ) PageGetItem (page , PageGetItemId (page , stackEntry -> off ));
402
390
403
- /* Predicate lock visited entry leaf page */
404
- GinPredicateLockPage (ginstate -> index ,
405
- BufferGetBlockNumber (stackEntry -> buffer ), snapshot );
406
-
407
391
if (GinIsPostingTree (itup ))
408
392
{
409
393
BlockNumber rootPostingTree = GinGetPostingTree (itup );
410
394
GinBtreeStack * stack ;
411
395
Page page ;
412
396
ItemPointerData minItem ;
413
397
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
+
414
405
/*
415
406
* We should unlock entry page before touching posting tree to
416
407
* prevent deadlocks with vacuum processes. Because entry is never
@@ -425,12 +416,6 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
425
416
rootPostingTree , snapshot );
426
417
entry -> buffer = stack -> buffer ;
427
418
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
-
434
419
/*
435
420
* We keep buffer pinned because we need to prevent deletion of
436
421
* page during scan. See GIN's vacuum implementation. RefCount is
@@ -452,15 +437,38 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
452
437
freeGinBtreeStack (stack );
453
438
entry -> isFinished = false;
454
439
}
455
- else if ( GinGetNPosting ( itup ) > 0 )
440
+ else
456
441
{
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 ;
460
458
461
- entry -> isFinished = false;
459
+ entry -> isFinished = false;
460
+ }
462
461
}
463
462
}
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
+ }
464
472
465
473
if (needUnlock )
466
474
LockBuffer (stackEntry -> buffer , GIN_UNLOCK );
@@ -533,7 +541,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
533
541
534
542
for (i = 0 ; i < key -> nentries - 1 ; i ++ )
535
543
{
536
- /* Pass all entries <= i as false , and the rest as MAYBE */
544
+ /* Pass all entries <= i as FALSE , and the rest as MAYBE */
537
545
for (j = 0 ; j <= i ; j ++ )
538
546
key -> entryRes [entryIndexes [j ]] = GIN_FALSE ;
539
547
for (j = i + 1 ; j < key -> nentries ; j ++ )
@@ -673,8 +681,6 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
673
681
entry -> btree .fullScan = false;
674
682
stack = ginFindLeafPage (& entry -> btree , true, snapshot );
675
683
676
- GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (stack -> buffer ), snapshot );
677
-
678
684
/* we don't need the stack, just the buffer. */
679
685
entry -> buffer = stack -> buffer ;
680
686
IncrBufferRefCount (entry -> buffer );
@@ -719,10 +725,6 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
719
725
entry -> buffer = ginStepRight (entry -> buffer ,
720
726
ginstate -> index ,
721
727
GIN_SHARE );
722
-
723
- GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (entry -> buffer ), snapshot );
724
-
725
-
726
728
page = BufferGetPage (entry -> buffer );
727
729
}
728
730
stepright = true;
@@ -1084,8 +1086,8 @@ keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key,
1084
1086
* lossy page even when none of the other entries match.
1085
1087
*
1086
1088
* 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
1089
1091
* we don't need to return a lossy-page pointer. Otherwise, return a
1090
1092
* lossy-page pointer to indicate that the whole heap page must be
1091
1093
* checked. (On subsequent calls, we'll do nothing until minItem is past
@@ -1746,8 +1748,7 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
1746
1748
}
1747
1749
1748
1750
/*
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.
1751
1752
*/
1752
1753
static void
1753
1754
scanPendingInsert (IndexScanDesc scan , TIDBitmap * tbm , int64 * ntids )
@@ -1764,6 +1765,12 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1764
1765
1765
1766
* ntids = 0 ;
1766
1767
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
+
1767
1774
LockBuffer (metabuffer , GIN_SHARE );
1768
1775
page = BufferGetPage (metabuffer );
1769
1776
TestForOldSnapshot (scan -> xs_snapshot , scan -> indexRelation , page );
@@ -1777,24 +1784,9 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1777
1784
{
1778
1785
/* No pending list, so proceed with normal scan */
1779
1786
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
-
1789
1787
return ;
1790
1788
}
1791
1789
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
-
1798
1790
pos .pendingBuffer = ReadBuffer (scan -> indexRelation , blkno );
1799
1791
LockBuffer (pos .pendingBuffer , GIN_SHARE );
1800
1792
pos .firstOffset = FirstOffsetNumber ;
0 commit comments