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