17
17
#include "access/gin_private.h"
18
18
#include "access/relscan.h"
19
19
#include "miscadmin.h"
20
+ #include "storage/predicate.h"
20
21
#include "utils/datum.h"
21
22
#include "utils/memutils.h"
23
+ #include "utils/rel.h"
22
24
23
25
/* GUC parameter */
24
26
int GinFuzzySearchLimit = 0 ;
@@ -33,11 +35,25 @@ typedef struct pendingPosition
33
35
} pendingPosition ;
34
36
35
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
46
+ * anyway need to lock the whole index.
47
+ */
48
+ if (!GinGetUseFastUpdate (index ))
49
+ PredicateLockPage (index , blkno , snapshot );
50
+ }
51
+
36
52
/*
37
53
* Goes to the next page if current offset is outside of bounds
38
54
*/
39
55
static bool
40
- moveRightIfItNeeded (GinBtreeData * btree , GinBtreeStack * stack )
56
+ moveRightIfItNeeded (GinBtreeData * btree , GinBtreeStack * stack , Snapshot snapshot )
41
57
{
42
58
Page page = BufferGetPage (stack -> buffer );
43
59
@@ -52,6 +68,7 @@ moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack)
52
68
stack -> buffer = ginStepRight (stack -> buffer , btree -> index , GIN_SHARE );
53
69
stack -> blkno = BufferGetBlockNumber (stack -> buffer );
54
70
stack -> off = FirstOffsetNumber ;
71
+ GinPredicateLockPage (btree -> index , stack -> blkno , snapshot );
55
72
}
56
73
57
74
return true;
@@ -73,6 +90,7 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
73
90
/* Descend to the leftmost leaf page */
74
91
stack = ginScanBeginPostingTree (& btree , index , rootPostingTree , snapshot );
75
92
buffer = stack -> buffer ;
93
+
76
94
IncrBufferRefCount (buffer ); /* prevent unpin in freeGinBtreeStack */
77
95
78
96
freeGinBtreeStack (stack );
@@ -82,6 +100,11 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
82
100
*/
83
101
for (;;)
84
102
{
103
+ /*
104
+ * Predicate lock each leaf page in posting tree
105
+ */
106
+ GinPredicateLockPage (index , BufferGetBlockNumber (buffer ), snapshot );
107
+
85
108
page = BufferGetPage (buffer );
86
109
if ((GinPageGetOpaque (page )-> flags & GIN_DELETED ) == 0 )
87
110
{
@@ -131,6 +154,12 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
131
154
attnum = scanEntry -> attnum ;
132
155
attr = TupleDescAttr (btree -> ginstate -> origTupdesc , attnum - 1 );
133
156
157
+ /*
158
+ * Predicate lock entry leaf page, following pages will be locked by
159
+ * moveRightIfItNeeded()
160
+ */
161
+ GinPredicateLockPage (btree -> index , stack -> buffer , snapshot );
162
+
134
163
for (;;)
135
164
{
136
165
Page page ;
@@ -141,7 +170,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
141
170
/*
142
171
* stack->off points to the interested entry, buffer is already locked
143
172
*/
144
- if (moveRightIfItNeeded (btree , stack ) == false)
173
+ if (moveRightIfItNeeded (btree , stack , snapshot ) == false)
145
174
return true;
146
175
147
176
page = BufferGetPage (stack -> buffer );
@@ -250,7 +279,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
250
279
Datum newDatum ;
251
280
GinNullCategory newCategory ;
252
281
253
- if (moveRightIfItNeeded (btree , stack ) == false)
282
+ if (moveRightIfItNeeded (btree , stack , snapshot ) == false)
254
283
elog (ERROR , "lost saved point in index" ); /* must not happen !!! */
255
284
256
285
page = BufferGetPage (stack -> buffer );
@@ -323,6 +352,7 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
323
352
ginstate );
324
353
stackEntry = ginFindLeafPage (& btreeEntry , true, snapshot );
325
354
page = BufferGetPage (stackEntry -> buffer );
355
+
326
356
/* ginFindLeafPage() will have already checked snapshot age. */
327
357
needUnlock = true;
328
358
@@ -370,6 +400,10 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
370
400
{
371
401
IndexTuple itup = (IndexTuple ) PageGetItem (page , PageGetItemId (page , stackEntry -> off ));
372
402
403
+ /* Predicate lock visited entry leaf page */
404
+ GinPredicateLockPage (ginstate -> index ,
405
+ BufferGetBlockNumber (stackEntry -> buffer ), snapshot );
406
+
373
407
if (GinIsPostingTree (itup ))
374
408
{
375
409
BlockNumber rootPostingTree = GinGetPostingTree (itup );
@@ -391,6 +425,12 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
391
425
rootPostingTree , snapshot );
392
426
entry -> buffer = stack -> buffer ;
393
427
428
+ /*
429
+ * Predicate lock visited posting tree page, following pages
430
+ * will be locked by moveRightIfItNeeded or entryLoadMoreItems
431
+ */
432
+ GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (entry -> buffer ), snapshot );
433
+
394
434
/*
395
435
* We keep buffer pinned because we need to prevent deletion of
396
436
* page during scan. See GIN's vacuum implementation. RefCount is
@@ -493,7 +533,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
493
533
494
534
for (i = 0 ; i < key -> nentries - 1 ; i ++ )
495
535
{
496
- /* Pass all entries <= i as FALSE , and the rest as MAYBE */
536
+ /* Pass all entries <= i as false , and the rest as MAYBE */
497
537
for (j = 0 ; j <= i ; j ++ )
498
538
key -> entryRes [entryIndexes [j ]] = GIN_FALSE ;
499
539
for (j = i + 1 ; j < key -> nentries ; j ++ )
@@ -633,6 +673,8 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
633
673
entry -> btree .fullScan = false;
634
674
stack = ginFindLeafPage (& entry -> btree , true, snapshot );
635
675
676
+ GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (stack -> buffer ), snapshot );
677
+
636
678
/* we don't need the stack, just the buffer. */
637
679
entry -> buffer = stack -> buffer ;
638
680
IncrBufferRefCount (entry -> buffer );
@@ -677,6 +719,10 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
677
719
entry -> buffer = ginStepRight (entry -> buffer ,
678
720
ginstate -> index ,
679
721
GIN_SHARE );
722
+
723
+ GinPredicateLockPage (ginstate -> index , BufferGetBlockNumber (entry -> buffer ), snapshot );
724
+
725
+
680
726
page = BufferGetPage (entry -> buffer );
681
727
}
682
728
stepright = true;
@@ -1038,8 +1084,8 @@ keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key,
1038
1084
* lossy page even when none of the other entries match.
1039
1085
*
1040
1086
* Our strategy is to call the tri-state consistent function, with the
1041
- * lossy-page entries set to MAYBE, and all the other entries FALSE . If it
1042
- * returns FALSE , none of the lossy items alone are enough for a match, so
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
1043
1089
* we don't need to return a lossy-page pointer. Otherwise, return a
1044
1090
* lossy-page pointer to indicate that the whole heap page must be
1045
1091
* checked. (On subsequent calls, we'll do nothing until minItem is past
@@ -1700,7 +1746,8 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
1700
1746
}
1701
1747
1702
1748
/*
1703
- * Collect all matched rows from pending list into bitmap
1749
+ * Collect all matched rows from pending list into bitmap. Also function
1750
+ * takes PendingLockRelation if it's needed.
1704
1751
*/
1705
1752
static void
1706
1753
scanPendingInsert (IndexScanDesc scan , TIDBitmap * tbm , int64 * ntids )
@@ -1730,9 +1777,24 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
1730
1777
{
1731
1778
/* No pending list, so proceed with normal scan */
1732
1779
UnlockReleaseBuffer (metabuffer );
1780
+
1781
+ /*
1782
+ * If fast update is enabled, we acquire a predicate lock on the entire
1783
+ * relation as fast update postpones the insertion of tuples into index
1784
+ * structure due to which we can't detect rw conflicts.
1785
+ */
1786
+ if (GinGetUseFastUpdate (scan -> indexRelation ))
1787
+ PredicateLockRelation (scan -> indexRelation , scan -> xs_snapshot );
1788
+
1733
1789
return ;
1734
1790
}
1735
1791
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
+
1736
1798
pos .pendingBuffer = ReadBuffer (scan -> indexRelation , blkno );
1737
1799
LockBuffer (pos .pendingBuffer , GIN_SHARE );
1738
1800
pos .firstOffset = FirstOffsetNumber ;
0 commit comments