Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * blscan.c
4 : * Bloom index scan functions.
5 : *
6 : * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * contrib/bloom/blscan.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "access/relscan.h"
16 : #include "bloom.h"
17 : #include "executor/instrument_node.h"
18 : #include "miscadmin.h"
19 : #include "pgstat.h"
20 : #include "storage/bufmgr.h"
21 : #include "storage/read_stream.h"
22 :
23 : /*
24 : * Begin scan of bloom index.
25 : */
26 : IndexScanDesc
27 387 : blbeginscan(Relation r, int nkeys, int norderbys)
28 : {
29 : IndexScanDesc scan;
30 : BloomScanOpaque so;
31 :
32 387 : scan = RelationGetIndexScan(r, nkeys, norderbys);
33 :
34 387 : so = (BloomScanOpaque) palloc_object(BloomScanOpaqueData);
35 387 : initBloomState(&so->state, scan->indexRelation);
36 387 : so->sign = NULL;
37 :
38 387 : scan->opaque = so;
39 :
40 387 : return scan;
41 : }
42 :
43 : /*
44 : * Rescan a bloom index.
45 : */
46 : void
47 387 : blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
48 : ScanKey orderbys, int norderbys)
49 : {
50 387 : BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
51 :
52 387 : if (so->sign)
53 0 : pfree(so->sign);
54 387 : so->sign = NULL;
55 :
56 387 : if (scankey && scan->numberOfKeys > 0)
57 387 : memcpy(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData));
58 387 : }
59 :
60 : /*
61 : * End scan of bloom index.
62 : */
63 : void
64 387 : blendscan(IndexScanDesc scan)
65 : {
66 387 : BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
67 :
68 387 : if (so->sign)
69 387 : pfree(so->sign);
70 387 : so->sign = NULL;
71 387 : }
72 :
73 : /*
74 : * Insert all matching tuples into a bitmap.
75 : */
76 : int64
77 387 : blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
78 : {
79 387 : int64 ntids = 0;
80 : BlockNumber blkno,
81 : npages;
82 : int i;
83 : BufferAccessStrategy bas;
84 387 : BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
85 : BlockRangeReadStreamPrivate p;
86 : ReadStream *stream;
87 :
88 387 : if (so->sign == NULL)
89 : {
90 : /* New search: have to calculate search signature */
91 387 : ScanKey skey = scan->keyData;
92 :
93 387 : so->sign = palloc0_array(BloomSignatureWord, so->state.opts.bloomLength);
94 :
95 903 : for (i = 0; i < scan->numberOfKeys; i++)
96 : {
97 : /*
98 : * Assume bloom-indexable operators to be strict, so nothing could
99 : * be found for NULL key.
100 : */
101 516 : if (skey->sk_flags & SK_ISNULL)
102 : {
103 0 : pfree(so->sign);
104 0 : so->sign = NULL;
105 0 : return 0;
106 : }
107 :
108 : /* Add next value to the signature */
109 516 : signValue(&so->state, so->sign, skey->sk_argument,
110 516 : skey->sk_attno - 1);
111 :
112 516 : skey++;
113 : }
114 : }
115 :
116 : /*
117 : * We're going to read the whole index. This is why we use appropriate
118 : * buffer access strategy.
119 : */
120 387 : bas = GetAccessStrategy(BAS_BULKREAD);
121 387 : npages = RelationGetNumberOfBlocks(scan->indexRelation);
122 387 : pgstat_count_index_scan(scan->indexRelation);
123 387 : if (scan->instrument)
124 0 : scan->instrument->nsearches++;
125 :
126 : /* Scan all blocks except the metapage using streaming reads */
127 387 : p.current_blocknum = BLOOM_HEAD_BLKNO;
128 387 : p.last_exclusive = npages;
129 :
130 : /*
131 : * It is safe to use batchmode as block_range_read_stream_cb takes no
132 : * locks.
133 : */
134 387 : stream = read_stream_begin_relation(READ_STREAM_FULL |
135 : READ_STREAM_USE_BATCHING,
136 : bas,
137 : scan->indexRelation,
138 : MAIN_FORKNUM,
139 : block_range_read_stream_cb,
140 : &p,
141 : 0);
142 :
143 29334 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
144 : {
145 : Buffer buffer;
146 : Page page;
147 :
148 28947 : buffer = read_stream_next_buffer(stream, NULL);
149 28947 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
150 28947 : page = BufferGetPage(buffer);
151 :
152 28947 : if (!PageIsNew(page) && !BloomPageIsDeleted(page))
153 : {
154 : OffsetNumber offset,
155 28926 : maxOffset = BloomPageGetMaxOffset(page);
156 :
157 14161176 : for (offset = 1; offset <= maxOffset; offset++)
158 : {
159 14132250 : BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
160 14132250 : bool res = true;
161 :
162 : /* Check index signature with scan signature */
163 35371848 : for (i = 0; i < so->state.opts.bloomLength; i++)
164 : {
165 34486186 : if ((itup->sign[i] & so->sign[i]) != so->sign[i])
166 : {
167 13246588 : res = false;
168 13246588 : break;
169 : }
170 : }
171 :
172 : /* Add matching tuples to bitmap */
173 14132250 : if (res)
174 : {
175 885662 : tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
176 885662 : ntids++;
177 : }
178 : }
179 : }
180 :
181 28947 : UnlockReleaseBuffer(buffer);
182 28947 : CHECK_FOR_INTERRUPTS();
183 : }
184 :
185 : Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
186 387 : read_stream_end(stream);
187 387 : FreeAccessStrategy(bas);
188 :
189 387 : return ntids;
190 : }
|