Age Owner Branch data TLA 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 *
3352 andres@anarazel.de 40 :UBC 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 *
7824 tgl@sss.pgh.pa.us 51 :CBC 14586 : MultiExecBitmapIndexScan(BitmapIndexScanState *node)
52 : : {
53 : : TIDBitmap *tbm;
54 : : IndexScanDesc scandesc;
55 : 14586 : double nTuples = 0;
56 : : bool doscan;
57 : :
58 : : /* must provide our own instrumentation support */
59 [ + + ]: 14586 : if (node->ss.ps.instrument)
60 : 290 : InstrStartNode(node->ss.ps.instrument);
61 : :
62 : : /*
63 : : * extract necessary information from index scan node
64 : : */
7808 65 : 14586 : 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 : : */
7604 73 [ + + ]: 14586 : if (!node->biss_RuntimeKeysReady &&
74 [ + + + + ]: 11744 : (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
75 : : {
5914 76 : 246 : ExecReScan((PlanState *) node);
7604 77 : 246 : doscan = node->biss_RuntimeKeysReady;
78 : : }
79 : : else
80 : 14340 : 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 : : */
7823 88 [ + + ]: 14586 : if (node->biss_result)
89 : : {
90 : 332 : tbm = node->biss_result;
3378 91 : 332 : node->biss_result = NULL; /* reset for next time */
92 : : }
93 : : else
94 : : {
95 : : /* XXX should we use less than work_mem for this? */
597 96 : 14254 : tbm = tbm_create(work_mem * (Size) 1024,
3483 rhaas@postgresql.org 97 [ + + ]: 14254 : ((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 : : */
7604 tgl@sss.pgh.pa.us 104 [ + + ]: 29189 : while (doscan)
105 : : {
6737 106 : 14603 : nTuples += (double) index_getbitmap(scandesc, tbm);
107 : :
7824 108 [ - + ]: 14603 : CHECK_FOR_INTERRUPTS();
109 : :
6737 110 : 14603 : doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
111 : : node->biss_NumArrayKeys);
6310 bruce@momjian.us 112 [ + + ]: 14603 : if (doscan) /* reset index scan */
5771 tgl@sss.pgh.pa.us 113 : 17 : index_rescan(node->biss_ScanDesc,
114 : : node->biss_ScanKeys, node->biss_NumScanKeys,
115 : : NULL, 0);
116 : : }
117 : :
118 : : /* must provide our own instrumentation support */
7824 119 [ + + ]: 14586 : if (node->ss.ps.instrument)
7418 bruce@momjian.us 120 : 290 : InstrStopNode(node->ss.ps.instrument, nTuples);
121 : :
7824 tgl@sss.pgh.pa.us 122 : 14586 : 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
5914 133 : 3096 : ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
134 : : {
135 : 3096 : 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 : : */
7824 142 [ + + ]: 3096 : if (econtext)
143 : 2820 : 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 : : */
7604 153 [ + + ]: 3096 : if (node->biss_NumRuntimeKeys != 0)
7818 154 : 2803 : ExecIndexEvalRuntimeKeys(econtext,
155 : : node->biss_RuntimeKeys,
156 : : node->biss_NumRuntimeKeys);
7604 157 [ + + ]: 3096 : if (node->biss_NumArrayKeys != 0)
158 : 17 : node->biss_RuntimeKeysReady =
159 : 17 : ExecIndexEvalArrayKeys(econtext,
160 : : node->biss_ArrayKeys,
161 : : node->biss_NumArrayKeys);
162 : : else
7824 163 : 3079 : node->biss_RuntimeKeysReady = true;
164 : :
165 : : /* reset index scan */
7604 166 [ + - ]: 3096 : if (node->biss_RuntimeKeysReady)
5771 167 : 3096 : index_rescan(node->biss_ScanDesc,
168 : : node->biss_ScanKeys, node->biss_NumScanKeys,
169 : : NULL, 0);
7824 170 : 3096 : }
171 : :
172 : : /* ----------------------------------------------------------------
173 : : * ExecEndBitmapIndexScan
174 : : * ----------------------------------------------------------------
175 : : */
176 : : void
177 : 15697 : ExecEndBitmapIndexScan(BitmapIndexScanState *node)
178 : : {
179 : : Relation indexRelationDesc;
180 : : IndexScanDesc indexScanDesc;
181 : :
182 : : /*
183 : : * extract information from the node
184 : : */
7808 185 : 15697 : indexRelationDesc = node->biss_RelationDesc;
186 : 15697 : 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 : : */
558 pg@bowt.ie 193 [ - + - - ]: 15697 : if (node->biss_SharedInfo != NULL && IsParallelWorker())
194 : : {
195 : : IndexScanInstrumentation *winstrument;
196 : :
190 tomas.vondra@postgre 197 [ # # ]:UBC 0 : Assert(ParallelWorkerNumber < node->biss_SharedInfo->num_workers);
558 pg@bowt.ie 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 : : */
182 206 : 0 : winstrument->nsearches += node->biss_Instrument->nsearches;
5 pg@bowt.ie 207 [ # # ]:UNC 0 : Assert(node->biss_Instrument->ntabletuplefetches == 0);
208 : : }
209 : :
210 : : /*
211 : : * close the index relation (no-op if we didn't open it)
212 : : */
7058 tgl@sss.pgh.pa.us 213 [ + + ]:CBC 15697 : if (indexScanDesc)
214 : 13153 : index_endscan(indexScanDesc);
215 [ + + ]: 15697 : if (indexRelationDesc)
216 : 13153 : index_close(indexRelationDesc, NoLock);
7824 217 : 15697 : }
218 : :
219 : : /* ----------------------------------------------------------------
220 : : * ExecInitBitmapIndexScan
221 : : *
222 : : * Initializes the index scan's state information.
223 : : * ----------------------------------------------------------------
224 : : */
225 : : BitmapIndexScanState *
7509 226 : 15778 : ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
227 : : {
228 : : BitmapIndexScanState *indexstate;
229 : : LOCKMODE lockmode;
230 : :
231 : : /* check for unsupported flags */
232 [ - + ]: 15778 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
233 : :
234 : : /*
235 : : * create state structure
236 : : */
7824 237 : 15778 : indexstate = makeNode(BitmapIndexScanState);
238 : 15778 : indexstate->ss.ps.plan = (Plan *) node;
239 : 15778 : indexstate->ss.ps.state = estate;
3352 andres@anarazel.de 240 : 15778 : indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
241 : :
242 : : /* normally we don't make the result bitmap till runtime */
7823 tgl@sss.pgh.pa.us 243 : 15778 : 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 : :
3138 andres@anarazel.de 251 : 15778 : indexstate->ss.ss_currentRelation = NULL;
252 : 15778 : 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 : : */
7058 tgl@sss.pgh.pa.us 275 [ + + ]: 15778 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
276 : 2544 : return indexstate;
277 : :
278 : : /* Set up instrumentation of bitmap index scans if requested */
182 pg@bowt.ie 279 [ + + ]: 13234 : if (estate->es_instrument)
280 : 338 : indexstate->biss_Instrument = palloc0_object(IndexScanInstrumentation);
281 : :
282 : : /* Open the index relation. */
2726 tgl@sss.pgh.pa.us 283 : 13234 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
284 : 13234 : indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
285 : :
286 : : /*
287 : : * Initialize index-specific scan state
288 : : */
7824 289 : 13234 : indexstate->biss_RuntimeKeysReady = false;
5771 290 : 13234 : indexstate->biss_RuntimeKeys = NULL;
291 : 13234 : indexstate->biss_NumRuntimeKeys = 0;
292 : :
293 : : /*
294 : : * build the index scan keys from the index qualification
295 : : */
7604 296 : 13234 : ExecIndexBuildScanKeys((PlanState *) indexstate,
297 : : indexstate->biss_RelationDesc,
298 : : node->indexqual,
299 : : false,
300 : 13234 : &indexstate->biss_ScanKeys,
301 : : &indexstate->biss_NumScanKeys,
302 : : &indexstate->biss_RuntimeKeys,
303 : : &indexstate->biss_NumRuntimeKeys,
304 : : &indexstate->biss_ArrayKeys,
305 : : &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 [ + + ]: 13234 : if (indexstate->biss_NumRuntimeKeys != 0 ||
314 [ + + ]: 12314 : indexstate->biss_NumArrayKeys != 0)
7824 315 : 937 : {
316 : 937 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
317 : :
318 : 937 : ExecAssignExprContext(estate, &indexstate->ss.ps);
319 : 937 : indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
320 : 937 : indexstate->ss.ps.ps_ExprContext = stdecontext;
321 : : }
322 : : else
323 : : {
324 : 12297 : indexstate->biss_RuntimeContext = NULL;
325 : : }
326 : :
327 : : /*
328 : : * Initialize scan descriptor.
329 : : */
7808 330 : 13234 : indexstate->biss_ScanDesc =
6737 331 : 13234 : index_beginscan_bitmap(indexstate->biss_RelationDesc,
332 : : estate->es_snapshot,
333 : : indexstate->biss_Instrument,
334 : : 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 : : */
5771 340 [ + + ]: 13234 : if (indexstate->biss_NumRuntimeKeys == 0 &&
341 [ + + ]: 12314 : indexstate->biss_NumArrayKeys == 0)
342 : 12297 : index_rescan(indexstate->biss_ScanDesc,
343 : : indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
344 : : NULL, 0);
345 : :
346 : : /*
347 : : * all done.
348 : : */
7824 349 : 13234 : return indexstate;
350 : : }
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
558 pg@bowt.ie 360 : 13 : ExecBitmapIndexScanEstimate(BitmapIndexScanState *node, ParallelContext *pcxt)
361 : : {
362 : : 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 [ - + - - ]: 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
369 : 13 : return;
370 : :
558 pg@bowt.ie 371 :UBC 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 : : }
376 : :
377 : : /* ----------------------------------------------------------------
378 : : * ExecBitmapIndexScanInitializeDSM
379 : : *
380 : : * Set up bitmap index scan shared instrumentation.
381 : : * ----------------------------------------------------------------
382 : : */
383 : : void
558 pg@bowt.ie 384 :CBC 13 : ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node,
385 : : ParallelContext *pcxt)
386 : : {
387 : : Size size;
388 : :
389 : : /* don't need this if not instrumenting or no workers */
390 [ - + - - ]: 13 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
391 : 13 : return;
392 : :
558 pg@bowt.ie 393 :UBC 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 : : size);
167 melanieplageman@gmai 398 : 0 : shm_toc_insert(pcxt->toc,
399 : 0 : node->ss.ps.plan->plan_node_id +
400 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
558 pg@bowt.ie 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 : : }
407 : :
408 : : /* ----------------------------------------------------------------
409 : : * ExecBitmapIndexScanInitializeWorker
410 : : *
411 : : * Copy relevant information from TOC into planstate.
412 : : * ----------------------------------------------------------------
413 : : */
414 : : void
558 pg@bowt.ie 415 :CBC 180 : ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
416 : : ParallelWorkerContext *pwcxt)
417 : : {
418 : : /* don't need this if not instrumenting */
419 [ + - ]: 180 : if (!node->ss.ps.instrument)
420 : 180 : return;
421 : :
558 pg@bowt.ie 422 :UBC 0 : node->biss_SharedInfo = (SharedIndexScanInstrumentation *)
167 melanieplageman@gmai 423 : 0 : shm_toc_lookup(pwcxt->toc,
424 : 0 : node->ss.ps.plan->plan_node_id +
425 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
426 : : false);
427 : : }
428 : :
429 : : /* ----------------------------------------------------------------
430 : : * ExecBitmapIndexScanRetrieveInstrumentation
431 : : *
432 : : * Transfer bitmap index scan statistics from DSM to private memory.
433 : : * ----------------------------------------------------------------
434 : : */
435 : : void
558 pg@bowt.ie 436 : 0 : ExecBitmapIndexScanRetrieveInstrumentation(BitmapIndexScanState *node)
437 : : {
438 : 0 : SharedIndexScanInstrumentation *SharedInfo = node->biss_SharedInfo;
439 : : 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 : : }
|