/src/postgres/src/backend/executor/nodeBitmapIndexscan.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * nodeBitmapIndexscan.c |
4 | | * Routines to support bitmapped index scans of relations |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/executor/nodeBitmapIndexscan.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | /* |
16 | | * INTERFACE ROUTINES |
17 | | * MultiExecBitmapIndexScan scans a relation using index. |
18 | | * ExecInitBitmapIndexScan creates and initializes state info. |
19 | | * ExecReScanBitmapIndexScan prepares to rescan the plan. |
20 | | * ExecEndBitmapIndexScan releases all storage. |
21 | | */ |
22 | | #include "postgres.h" |
23 | | |
24 | | #include "access/genam.h" |
25 | | #include "executor/executor.h" |
26 | | #include "executor/instrument.h" |
27 | | #include "executor/nodeBitmapIndexscan.h" |
28 | | #include "executor/nodeIndexscan.h" |
29 | | #include "miscadmin.h" |
30 | | #include "nodes/tidbitmap.h" |
31 | | |
32 | | |
33 | | /* ---------------------------------------------------------------- |
34 | | * ExecBitmapIndexScan |
35 | | * |
36 | | * stub for pro forma compliance |
37 | | * ---------------------------------------------------------------- |
38 | | */ |
39 | | static TupleTableSlot * |
40 | | ExecBitmapIndexScan(PlanState *pstate) |
41 | 0 | { |
42 | 0 | elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention"); |
43 | 0 | return NULL; |
44 | 0 | } |
45 | | |
46 | | /* ---------------------------------------------------------------- |
47 | | * MultiExecBitmapIndexScan(node) |
48 | | * ---------------------------------------------------------------- |
49 | | */ |
50 | | Node * |
51 | | MultiExecBitmapIndexScan(BitmapIndexScanState *node) |
52 | 0 | { |
53 | 0 | TIDBitmap *tbm; |
54 | 0 | IndexScanDesc scandesc; |
55 | 0 | double nTuples = 0; |
56 | 0 | bool doscan; |
57 | | |
58 | | /* must provide our own instrumentation support */ |
59 | 0 | if (node->ss.ps.instrument) |
60 | 0 | InstrStartNode(node->ss.ps.instrument); |
61 | | |
62 | | /* |
63 | | * extract necessary information from index scan node |
64 | | */ |
65 | 0 | scandesc = node->biss_ScanDesc; |
66 | | |
67 | | /* |
68 | | * If we have runtime keys and they've not already been set up, do it now. |
69 | | * Array keys are also treated as runtime keys; note that if ExecReScan |
70 | | * returns with biss_RuntimeKeysReady still false, then there is an empty |
71 | | * array key so we should do nothing. |
72 | | */ |
73 | 0 | if (!node->biss_RuntimeKeysReady && |
74 | 0 | (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0)) |
75 | 0 | { |
76 | 0 | ExecReScan((PlanState *) node); |
77 | 0 | doscan = node->biss_RuntimeKeysReady; |
78 | 0 | } |
79 | 0 | else |
80 | 0 | doscan = true; |
81 | | |
82 | | /* |
83 | | * Prepare the result bitmap. Normally we just create a new one to pass |
84 | | * back; however, our parent node is allowed to store a pre-made one into |
85 | | * node->biss_result, in which case we just OR our tuple IDs into the |
86 | | * existing bitmap. (This saves needing explicit UNION steps.) |
87 | | */ |
88 | 0 | if (node->biss_result) |
89 | 0 | { |
90 | 0 | tbm = node->biss_result; |
91 | 0 | node->biss_result = NULL; /* reset for next time */ |
92 | 0 | } |
93 | 0 | else |
94 | 0 | { |
95 | | /* XXX should we use less than work_mem for this? */ |
96 | 0 | tbm = tbm_create(work_mem * (Size) 1024, |
97 | 0 | ((BitmapIndexScan *) node->ss.ps.plan)->isshared ? |
98 | 0 | node->ss.ps.state->es_query_dsa : NULL); |
99 | 0 | } |
100 | | |
101 | | /* |
102 | | * Get TIDs from index and insert into bitmap |
103 | | */ |
104 | 0 | while (doscan) |
105 | 0 | { |
106 | 0 | nTuples += (double) index_getbitmap(scandesc, tbm); |
107 | |
|
108 | 0 | CHECK_FOR_INTERRUPTS(); |
109 | |
|
110 | 0 | doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys, |
111 | 0 | node->biss_NumArrayKeys); |
112 | 0 | if (doscan) /* reset index scan */ |
113 | 0 | index_rescan(node->biss_ScanDesc, |
114 | 0 | node->biss_ScanKeys, node->biss_NumScanKeys, |
115 | 0 | NULL, 0); |
116 | 0 | } |
117 | | |
118 | | /* must provide our own instrumentation support */ |
119 | 0 | if (node->ss.ps.instrument) |
120 | 0 | InstrStopNode(node->ss.ps.instrument, nTuples); |
121 | |
|
122 | 0 | return (Node *) tbm; |
123 | 0 | } |
124 | | |
125 | | /* ---------------------------------------------------------------- |
126 | | * ExecReScanBitmapIndexScan(node) |
127 | | * |
128 | | * Recalculates the values of any scan keys whose value depends on |
129 | | * information known at runtime, then rescans the indexed relation. |
130 | | * ---------------------------------------------------------------- |
131 | | */ |
132 | | void |
133 | | ExecReScanBitmapIndexScan(BitmapIndexScanState *node) |
134 | 0 | { |
135 | 0 | ExprContext *econtext = node->biss_RuntimeContext; |
136 | | |
137 | | /* |
138 | | * Reset the runtime-key context so we don't leak memory as each outer |
139 | | * tuple is scanned. Note this assumes that we will recalculate *all* |
140 | | * runtime keys on each call. |
141 | | */ |
142 | 0 | if (econtext) |
143 | 0 | ResetExprContext(econtext); |
144 | | |
145 | | /* |
146 | | * If we are doing runtime key calculations (ie, any of the index key |
147 | | * values weren't simple Consts), compute the new key values. |
148 | | * |
149 | | * Array keys are also treated as runtime keys; note that if we return |
150 | | * with biss_RuntimeKeysReady still false, then there is an empty array |
151 | | * key so no index scan is needed. |
152 | | */ |
153 | 0 | if (node->biss_NumRuntimeKeys != 0) |
154 | 0 | ExecIndexEvalRuntimeKeys(econtext, |
155 | 0 | node->biss_RuntimeKeys, |
156 | 0 | node->biss_NumRuntimeKeys); |
157 | 0 | if (node->biss_NumArrayKeys != 0) |
158 | 0 | node->biss_RuntimeKeysReady = |
159 | 0 | ExecIndexEvalArrayKeys(econtext, |
160 | 0 | node->biss_ArrayKeys, |
161 | 0 | node->biss_NumArrayKeys); |
162 | 0 | else |
163 | 0 | node->biss_RuntimeKeysReady = true; |
164 | | |
165 | | /* reset index scan */ |
166 | 0 | if (node->biss_RuntimeKeysReady) |
167 | 0 | index_rescan(node->biss_ScanDesc, |
168 | 0 | node->biss_ScanKeys, node->biss_NumScanKeys, |
169 | 0 | NULL, 0); |
170 | 0 | } |
171 | | |
172 | | /* ---------------------------------------------------------------- |
173 | | * ExecEndBitmapIndexScan |
174 | | * ---------------------------------------------------------------- |
175 | | */ |
176 | | void |
177 | | ExecEndBitmapIndexScan(BitmapIndexScanState *node) |
178 | 0 | { |
179 | 0 | Relation indexRelationDesc; |
180 | 0 | IndexScanDesc indexScanDesc; |
181 | | |
182 | | /* |
183 | | * extract information from the node |
184 | | */ |
185 | 0 | indexRelationDesc = node->biss_RelationDesc; |
186 | 0 | indexScanDesc = node->biss_ScanDesc; |
187 | | |
188 | | /* |
189 | | * When ending a parallel worker, copy the statistics gathered by the |
190 | | * worker back into shared memory so that it can be picked up by the main |
191 | | * process to report in EXPLAIN ANALYZE |
192 | | */ |
193 | 0 | if (node->biss_SharedInfo != NULL && IsParallelWorker()) |
194 | 0 | { |
195 | 0 | IndexScanInstrumentation *winstrument; |
196 | |
|
197 | 0 | Assert(ParallelWorkerNumber < node->biss_SharedInfo->num_workers); |
198 | 0 | winstrument = &node->biss_SharedInfo->winstrument[ParallelWorkerNumber]; |
199 | | |
200 | | /* |
201 | | * We have to accumulate the stats rather than performing a memcpy. |
202 | | * When a Gather/GatherMerge node finishes it will perform planner |
203 | | * shutdown on the workers. On rescan it will spin up new workers |
204 | | * which will have a new BitmapIndexScanState and zeroed stats. |
205 | | */ |
206 | 0 | winstrument->nsearches += node->biss_Instrument->nsearches; |
207 | 0 | Assert(node->biss_Instrument->ntabletuplefetches == 0); |
208 | 0 | } |
209 | | |
210 | | /* |
211 | | * close the index relation (no-op if we didn't open it) |
212 | | */ |
213 | 0 | if (indexScanDesc) |
214 | 0 | index_endscan(indexScanDesc); |
215 | 0 | if (indexRelationDesc) |
216 | 0 | index_close(indexRelationDesc, NoLock); |
217 | 0 | } |
218 | | |
219 | | /* ---------------------------------------------------------------- |
220 | | * ExecInitBitmapIndexScan |
221 | | * |
222 | | * Initializes the index scan's state information. |
223 | | * ---------------------------------------------------------------- |
224 | | */ |
225 | | BitmapIndexScanState * |
226 | | ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) |
227 | 0 | { |
228 | 0 | BitmapIndexScanState *indexstate; |
229 | 0 | LOCKMODE lockmode; |
230 | | |
231 | | /* check for unsupported flags */ |
232 | 0 | Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); |
233 | | |
234 | | /* |
235 | | * create state structure |
236 | | */ |
237 | 0 | indexstate = makeNode(BitmapIndexScanState); |
238 | 0 | indexstate->ss.ps.plan = (Plan *) node; |
239 | 0 | indexstate->ss.ps.state = estate; |
240 | 0 | indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan; |
241 | | |
242 | | /* normally we don't make the result bitmap till runtime */ |
243 | 0 | indexstate->biss_result = NULL; |
244 | | |
245 | | /* |
246 | | * We do not open or lock the base relation here. We assume that an |
247 | | * ancestor BitmapHeapScan node is holding AccessShareLock (or better) on |
248 | | * the heap relation throughout the execution of the plan tree. |
249 | | */ |
250 | |
|
251 | 0 | indexstate->ss.ss_currentRelation = NULL; |
252 | 0 | indexstate->ss.ss_currentScanDesc = NULL; |
253 | | |
254 | | /* |
255 | | * Miscellaneous initialization |
256 | | * |
257 | | * We do not need a standard exprcontext for this node, though we may |
258 | | * decide below to create a runtime-key exprcontext |
259 | | */ |
260 | | |
261 | | /* |
262 | | * initialize child expressions |
263 | | * |
264 | | * We don't need to initialize targetlist or qual since neither are used. |
265 | | * |
266 | | * Note: we don't initialize all of the indexqual expression, only the |
267 | | * sub-parts corresponding to runtime keys (see below). |
268 | | */ |
269 | | |
270 | | /* |
271 | | * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop |
272 | | * here. This allows an index-advisor plugin to EXPLAIN a plan containing |
273 | | * references to nonexistent indexes. |
274 | | */ |
275 | 0 | if (eflags & EXEC_FLAG_EXPLAIN_ONLY) |
276 | 0 | return indexstate; |
277 | | |
278 | | /* Set up instrumentation of bitmap index scans if requested */ |
279 | 0 | if (estate->es_instrument) |
280 | 0 | indexstate->biss_Instrument = palloc0_object(IndexScanInstrumentation); |
281 | | |
282 | | /* Open the index relation. */ |
283 | 0 | lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode; |
284 | 0 | indexstate->biss_RelationDesc = index_open(node->indexid, lockmode); |
285 | | |
286 | | /* |
287 | | * Initialize index-specific scan state |
288 | | */ |
289 | 0 | indexstate->biss_RuntimeKeysReady = false; |
290 | 0 | indexstate->biss_RuntimeKeys = NULL; |
291 | 0 | indexstate->biss_NumRuntimeKeys = 0; |
292 | | |
293 | | /* |
294 | | * build the index scan keys from the index qualification |
295 | | */ |
296 | 0 | ExecIndexBuildScanKeys((PlanState *) indexstate, |
297 | 0 | indexstate->biss_RelationDesc, |
298 | 0 | node->indexqual, |
299 | 0 | false, |
300 | 0 | &indexstate->biss_ScanKeys, |
301 | 0 | &indexstate->biss_NumScanKeys, |
302 | 0 | &indexstate->biss_RuntimeKeys, |
303 | 0 | &indexstate->biss_NumRuntimeKeys, |
304 | 0 | &indexstate->biss_ArrayKeys, |
305 | 0 | &indexstate->biss_NumArrayKeys); |
306 | | |
307 | | /* |
308 | | * If we have runtime keys or array keys, we need an ExprContext to |
309 | | * evaluate them. We could just create a "standard" plan node exprcontext, |
310 | | * but to keep the code looking similar to nodeIndexscan.c, it seems |
311 | | * better to stick with the approach of using a separate ExprContext. |
312 | | */ |
313 | 0 | if (indexstate->biss_NumRuntimeKeys != 0 || |
314 | 0 | indexstate->biss_NumArrayKeys != 0) |
315 | 0 | { |
316 | 0 | ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext; |
317 | |
|
318 | 0 | ExecAssignExprContext(estate, &indexstate->ss.ps); |
319 | 0 | indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext; |
320 | 0 | indexstate->ss.ps.ps_ExprContext = stdecontext; |
321 | 0 | } |
322 | 0 | else |
323 | 0 | { |
324 | 0 | indexstate->biss_RuntimeContext = NULL; |
325 | 0 | } |
326 | | |
327 | | /* |
328 | | * Initialize scan descriptor. |
329 | | */ |
330 | 0 | indexstate->biss_ScanDesc = |
331 | 0 | index_beginscan_bitmap(indexstate->biss_RelationDesc, |
332 | 0 | estate->es_snapshot, |
333 | 0 | indexstate->biss_Instrument, |
334 | 0 | indexstate->biss_NumScanKeys); |
335 | | |
336 | | /* |
337 | | * If no run-time keys to calculate, go ahead and pass the scankeys to the |
338 | | * index AM. |
339 | | */ |
340 | 0 | if (indexstate->biss_NumRuntimeKeys == 0 && |
341 | 0 | indexstate->biss_NumArrayKeys == 0) |
342 | 0 | index_rescan(indexstate->biss_ScanDesc, |
343 | 0 | indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys, |
344 | 0 | NULL, 0); |
345 | | |
346 | | /* |
347 | | * all done. |
348 | | */ |
349 | 0 | return indexstate; |
350 | 0 | } |
351 | | |
352 | | /* ---------------------------------------------------------------- |
353 | | * ExecBitmapIndexScanEstimate |
354 | | * |
355 | | * Compute the amount of space we'll need in the parallel |
356 | | * query DSM, and inform pcxt->estimator about our needs. |
357 | | * ---------------------------------------------------------------- |
358 | | */ |
359 | | void |
360 | | ExecBitmapIndexScanEstimate(BitmapIndexScanState *node, ParallelContext *pcxt) |
361 | 0 | { |
362 | 0 | Size size; |
363 | | |
364 | | /* |
365 | | * Parallel bitmap index scans are not supported, but we still need to |
366 | | * store the scan's instrumentation in DSM during parallel query |
367 | | */ |
368 | 0 | if (!node->ss.ps.instrument || pcxt->nworkers == 0) |
369 | 0 | return; |
370 | | |
371 | 0 | size = offsetof(SharedIndexScanInstrumentation, winstrument) + |
372 | 0 | pcxt->nworkers * sizeof(IndexScanInstrumentation); |
373 | 0 | shm_toc_estimate_chunk(&pcxt->estimator, size); |
374 | 0 | shm_toc_estimate_keys(&pcxt->estimator, 1); |
375 | 0 | } |
376 | | |
377 | | /* ---------------------------------------------------------------- |
378 | | * ExecBitmapIndexScanInitializeDSM |
379 | | * |
380 | | * Set up bitmap index scan shared instrumentation. |
381 | | * ---------------------------------------------------------------- |
382 | | */ |
383 | | void |
384 | | ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node, |
385 | | ParallelContext *pcxt) |
386 | 0 | { |
387 | 0 | Size size; |
388 | | |
389 | | /* don't need this if not instrumenting or no workers */ |
390 | 0 | if (!node->ss.ps.instrument || pcxt->nworkers == 0) |
391 | 0 | return; |
392 | | |
393 | 0 | size = offsetof(SharedIndexScanInstrumentation, winstrument) + |
394 | 0 | pcxt->nworkers * sizeof(IndexScanInstrumentation); |
395 | 0 | node->biss_SharedInfo = |
396 | 0 | (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, |
397 | 0 | size); |
398 | 0 | shm_toc_insert(pcxt->toc, |
399 | 0 | node->ss.ps.plan->plan_node_id + |
400 | 0 | PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET, |
401 | 0 | node->biss_SharedInfo); |
402 | | |
403 | | /* Each per-worker area must start out as zeroes */ |
404 | 0 | memset(node->biss_SharedInfo, 0, size); |
405 | 0 | node->biss_SharedInfo->num_workers = pcxt->nworkers; |
406 | 0 | } |
407 | | |
408 | | /* ---------------------------------------------------------------- |
409 | | * ExecBitmapIndexScanInitializeWorker |
410 | | * |
411 | | * Copy relevant information from TOC into planstate. |
412 | | * ---------------------------------------------------------------- |
413 | | */ |
414 | | void |
415 | | ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node, |
416 | | ParallelWorkerContext *pwcxt) |
417 | 0 | { |
418 | | /* don't need this if not instrumenting */ |
419 | 0 | if (!node->ss.ps.instrument) |
420 | 0 | return; |
421 | | |
422 | 0 | node->biss_SharedInfo = (SharedIndexScanInstrumentation *) |
423 | 0 | shm_toc_lookup(pwcxt->toc, |
424 | 0 | node->ss.ps.plan->plan_node_id + |
425 | 0 | PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET, |
426 | 0 | false); |
427 | 0 | } |
428 | | |
429 | | /* ---------------------------------------------------------------- |
430 | | * ExecBitmapIndexScanRetrieveInstrumentation |
431 | | * |
432 | | * Transfer bitmap index scan statistics from DSM to private memory. |
433 | | * ---------------------------------------------------------------- |
434 | | */ |
435 | | void |
436 | | ExecBitmapIndexScanRetrieveInstrumentation(BitmapIndexScanState *node) |
437 | 0 | { |
438 | 0 | SharedIndexScanInstrumentation *SharedInfo = node->biss_SharedInfo; |
439 | 0 | size_t size; |
440 | |
|
441 | 0 | if (SharedInfo == NULL) |
442 | 0 | return; |
443 | | |
444 | | /* Create a copy of SharedInfo in backend-local memory */ |
445 | 0 | size = offsetof(SharedIndexScanInstrumentation, winstrument) + |
446 | 0 | SharedInfo->num_workers * sizeof(IndexScanInstrumentation); |
447 | 0 | node->biss_SharedInfo = palloc(size); |
448 | 0 | memcpy(node->biss_SharedInfo, SharedInfo, size); |
449 | 0 | } |