Line data Source code
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 0 : ExecBitmapIndexScan(PlanState *pstate)
41 : {
42 0 : elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention");
43 : return NULL;
44 : }
45 :
46 : /* ----------------------------------------------------------------
47 : * MultiExecBitmapIndexScan(node)
48 : * ----------------------------------------------------------------
49 : */
50 : Node *
51 16111 : MultiExecBitmapIndexScan(BitmapIndexScanState *node)
52 : {
53 : TIDBitmap *tbm;
54 : IndexScanDesc scandesc;
55 16111 : double nTuples = 0;
56 : bool doscan;
57 :
58 : /* must provide our own instrumentation support */
59 16111 : if (node->ss.ps.instrument)
60 290 : InstrStartNode(node->ss.ps.instrument);
61 :
62 : /*
63 : * extract necessary information from index scan node
64 : */
65 16111 : 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 16111 : if (!node->biss_RuntimeKeysReady &&
74 13018 : (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
75 : {
76 337 : ExecReScan((PlanState *) node);
77 337 : doscan = node->biss_RuntimeKeysReady;
78 : }
79 : else
80 15774 : 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 16111 : if (node->biss_result)
89 : {
90 354 : tbm = node->biss_result;
91 354 : node->biss_result = NULL; /* reset for next time */
92 : }
93 : else
94 : {
95 : /* XXX should we use less than work_mem for this? */
96 15757 : tbm = tbm_create(work_mem * (Size) 1024,
97 15757 : ((BitmapIndexScan *) node->ss.ps.plan)->isshared ?
98 48 : node->ss.ps.state->es_query_dsa : NULL);
99 : }
100 :
101 : /*
102 : * Get TIDs from index and insert into bitmap
103 : */
104 32259 : while (doscan)
105 : {
106 16148 : nTuples += (double) index_getbitmap(scandesc, tbm);
107 :
108 16148 : CHECK_FOR_INTERRUPTS();
109 :
110 16148 : doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
111 : node->biss_NumArrayKeys);
112 16148 : if (doscan) /* reset index scan */
113 37 : index_rescan(node->biss_ScanDesc,
114 : node->biss_ScanKeys, node->biss_NumScanKeys,
115 : NULL, 0);
116 : }
117 :
118 : /* must provide our own instrumentation support */
119 16111 : if (node->ss.ps.instrument)
120 290 : InstrStopNode(node->ss.ps.instrument, nTuples);
121 :
122 16111 : return (Node *) tbm;
123 : }
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 3438 : ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
134 : {
135 3438 : 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 3438 : if (econtext)
143 3128 : 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 3438 : if (node->biss_NumRuntimeKeys != 0)
154 3091 : ExecIndexEvalRuntimeKeys(econtext,
155 : node->biss_RuntimeKeys,
156 : node->biss_NumRuntimeKeys);
157 3438 : if (node->biss_NumArrayKeys != 0)
158 37 : node->biss_RuntimeKeysReady =
159 37 : ExecIndexEvalArrayKeys(econtext,
160 : node->biss_ArrayKeys,
161 : node->biss_NumArrayKeys);
162 : else
163 3401 : node->biss_RuntimeKeysReady = true;
164 :
165 : /* reset index scan */
166 3438 : if (node->biss_RuntimeKeysReady)
167 3438 : index_rescan(node->biss_ScanDesc,
168 : node->biss_ScanKeys, node->biss_NumScanKeys,
169 : NULL, 0);
170 3438 : }
171 :
172 : /* ----------------------------------------------------------------
173 : * ExecEndBitmapIndexScan
174 : * ----------------------------------------------------------------
175 : */
176 : void
177 17176 : ExecEndBitmapIndexScan(BitmapIndexScanState *node)
178 : {
179 : Relation indexRelationDesc;
180 : IndexScanDesc indexScanDesc;
181 :
182 : /*
183 : * extract information from the node
184 : */
185 17176 : indexRelationDesc = node->biss_RelationDesc;
186 17176 : 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 17176 : if (node->biss_SharedInfo != NULL && IsParallelWorker())
194 : {
195 : IndexScanInstrumentation *winstrument;
196 :
197 : 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 : }
208 :
209 : /*
210 : * close the index relation (no-op if we didn't open it)
211 : */
212 17176 : if (indexScanDesc)
213 14623 : index_endscan(indexScanDesc);
214 17176 : if (indexRelationDesc)
215 14623 : index_close(indexRelationDesc, NoLock);
216 17176 : }
217 :
218 : /* ----------------------------------------------------------------
219 : * ExecInitBitmapIndexScan
220 : *
221 : * Initializes the index scan's state information.
222 : * ----------------------------------------------------------------
223 : */
224 : BitmapIndexScanState *
225 17257 : ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
226 : {
227 : BitmapIndexScanState *indexstate;
228 : LOCKMODE lockmode;
229 :
230 : /* check for unsupported flags */
231 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
232 :
233 : /*
234 : * create state structure
235 : */
236 17257 : indexstate = makeNode(BitmapIndexScanState);
237 17257 : indexstate->ss.ps.plan = (Plan *) node;
238 17257 : indexstate->ss.ps.state = estate;
239 17257 : indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
240 :
241 : /* normally we don't make the result bitmap till runtime */
242 17257 : indexstate->biss_result = NULL;
243 :
244 : /*
245 : * We do not open or lock the base relation here. We assume that an
246 : * ancestor BitmapHeapScan node is holding AccessShareLock (or better) on
247 : * the heap relation throughout the execution of the plan tree.
248 : */
249 :
250 17257 : indexstate->ss.ss_currentRelation = NULL;
251 17257 : indexstate->ss.ss_currentScanDesc = NULL;
252 :
253 : /*
254 : * Miscellaneous initialization
255 : *
256 : * We do not need a standard exprcontext for this node, though we may
257 : * decide below to create a runtime-key exprcontext
258 : */
259 :
260 : /*
261 : * initialize child expressions
262 : *
263 : * We don't need to initialize targetlist or qual since neither are used.
264 : *
265 : * Note: we don't initialize all of the indexqual expression, only the
266 : * sub-parts corresponding to runtime keys (see below).
267 : */
268 :
269 : /*
270 : * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
271 : * here. This allows an index-advisor plugin to EXPLAIN a plan containing
272 : * references to nonexistent indexes.
273 : */
274 17257 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
275 2553 : return indexstate;
276 :
277 : /* Set up instrumentation of bitmap index scans if requested */
278 14704 : if (estate->es_instrument)
279 338 : indexstate->biss_Instrument = palloc0_object(IndexScanInstrumentation);
280 :
281 : /* Open the index relation. */
282 14704 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
283 14704 : indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
284 :
285 : /*
286 : * Initialize index-specific scan state
287 : */
288 14704 : indexstate->biss_RuntimeKeysReady = false;
289 14704 : indexstate->biss_RuntimeKeys = NULL;
290 14704 : indexstate->biss_NumRuntimeKeys = 0;
291 :
292 : /*
293 : * build the index scan keys from the index qualification
294 : */
295 14704 : ExecIndexBuildScanKeys((PlanState *) indexstate,
296 : indexstate->biss_RelationDesc,
297 : node->indexqual,
298 : false,
299 14704 : &indexstate->biss_ScanKeys,
300 : &indexstate->biss_NumScanKeys,
301 : &indexstate->biss_RuntimeKeys,
302 : &indexstate->biss_NumRuntimeKeys,
303 : &indexstate->biss_ArrayKeys,
304 : &indexstate->biss_NumArrayKeys);
305 :
306 : /*
307 : * If we have runtime keys or array keys, we need an ExprContext to
308 : * evaluate them. We could just create a "standard" plan node exprcontext,
309 : * but to keep the code looking similar to nodeIndexscan.c, it seems
310 : * better to stick with the approach of using a separate ExprContext.
311 : */
312 14704 : if (indexstate->biss_NumRuntimeKeys != 0 ||
313 13507 : indexstate->biss_NumArrayKeys != 0)
314 1234 : {
315 1234 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
316 :
317 1234 : ExecAssignExprContext(estate, &indexstate->ss.ps);
318 1234 : indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
319 1234 : indexstate->ss.ps.ps_ExprContext = stdecontext;
320 : }
321 : else
322 : {
323 13470 : indexstate->biss_RuntimeContext = NULL;
324 : }
325 :
326 : /*
327 : * Initialize scan descriptor.
328 : */
329 14704 : indexstate->biss_ScanDesc =
330 14704 : index_beginscan_bitmap(indexstate->biss_RelationDesc,
331 : estate->es_snapshot,
332 : indexstate->biss_Instrument,
333 : indexstate->biss_NumScanKeys);
334 :
335 : /*
336 : * If no run-time keys to calculate, go ahead and pass the scankeys to the
337 : * index AM.
338 : */
339 14704 : if (indexstate->biss_NumRuntimeKeys == 0 &&
340 13507 : indexstate->biss_NumArrayKeys == 0)
341 13470 : index_rescan(indexstate->biss_ScanDesc,
342 : indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
343 : NULL, 0);
344 :
345 : /*
346 : * all done.
347 : */
348 14704 : return indexstate;
349 : }
350 :
351 : /* ----------------------------------------------------------------
352 : * ExecBitmapIndexScanEstimate
353 : *
354 : * Compute the amount of space we'll need in the parallel
355 : * query DSM, and inform pcxt->estimator about our needs.
356 : * ----------------------------------------------------------------
357 : */
358 : void
359 13 : ExecBitmapIndexScanEstimate(BitmapIndexScanState *node, ParallelContext *pcxt)
360 : {
361 : Size size;
362 :
363 : /*
364 : * Parallel bitmap index scans are not supported, but we still need to
365 : * store the scan's instrumentation in DSM during parallel query
366 : */
367 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
368 13 : return;
369 :
370 0 : size = offsetof(SharedIndexScanInstrumentation, winstrument) +
371 0 : pcxt->nworkers * sizeof(IndexScanInstrumentation);
372 0 : shm_toc_estimate_chunk(&pcxt->estimator, size);
373 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
374 : }
375 :
376 : /* ----------------------------------------------------------------
377 : * ExecBitmapIndexScanInitializeDSM
378 : *
379 : * Set up bitmap index scan shared instrumentation.
380 : * ----------------------------------------------------------------
381 : */
382 : void
383 13 : ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node,
384 : ParallelContext *pcxt)
385 : {
386 : Size size;
387 :
388 : /* don't need this if not instrumenting or no workers */
389 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
390 13 : return;
391 :
392 0 : size = offsetof(SharedIndexScanInstrumentation, winstrument) +
393 0 : pcxt->nworkers * sizeof(IndexScanInstrumentation);
394 0 : node->biss_SharedInfo =
395 0 : (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc,
396 : size);
397 0 : shm_toc_insert(pcxt->toc,
398 0 : node->ss.ps.plan->plan_node_id +
399 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
400 0 : node->biss_SharedInfo);
401 :
402 : /* Each per-worker area must start out as zeroes */
403 0 : memset(node->biss_SharedInfo, 0, size);
404 0 : node->biss_SharedInfo->num_workers = pcxt->nworkers;
405 : }
406 :
407 : /* ----------------------------------------------------------------
408 : * ExecBitmapIndexScanInitializeWorker
409 : *
410 : * Copy relevant information from TOC into planstate.
411 : * ----------------------------------------------------------------
412 : */
413 : void
414 181 : ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
415 : ParallelWorkerContext *pwcxt)
416 : {
417 : /* don't need this if not instrumenting */
418 181 : if (!node->ss.ps.instrument)
419 181 : return;
420 :
421 0 : node->biss_SharedInfo = (SharedIndexScanInstrumentation *)
422 0 : shm_toc_lookup(pwcxt->toc,
423 0 : node->ss.ps.plan->plan_node_id +
424 : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
425 : false);
426 : }
427 :
428 : /* ----------------------------------------------------------------
429 : * ExecBitmapIndexScanRetrieveInstrumentation
430 : *
431 : * Transfer bitmap index scan statistics from DSM to private memory.
432 : * ----------------------------------------------------------------
433 : */
434 : void
435 0 : ExecBitmapIndexScanRetrieveInstrumentation(BitmapIndexScanState *node)
436 : {
437 0 : SharedIndexScanInstrumentation *SharedInfo = node->biss_SharedInfo;
438 : size_t size;
439 :
440 0 : if (SharedInfo == NULL)
441 0 : return;
442 :
443 : /* Create a copy of SharedInfo in backend-local memory */
444 0 : size = offsetof(SharedIndexScanInstrumentation, winstrument) +
445 0 : SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
446 0 : node->biss_SharedInfo = palloc(size);
447 0 : memcpy(node->biss_SharedInfo, SharedInfo, size);
448 : }
|