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 *
3214 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 *
7686 tgl@sss.pgh.pa.us 51 :CBC 15174 : MultiExecBitmapIndexScan(BitmapIndexScanState *node)
52 : : {
53 : : TIDBitmap *tbm;
54 : : IndexScanDesc scandesc;
55 : 15174 : double nTuples = 0;
56 : : bool doscan;
57 : :
58 : : /* must provide our own instrumentation support */
59 [ + + ]: 15174 : if (node->ss.ps.instrument)
60 : 290 : InstrStartNode(node->ss.ps.instrument);
61 : :
62 : : /*
63 : : * extract necessary information from index scan node
64 : : */
7670 65 : 15174 : 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 : : */
7466 73 [ + + ]: 15174 : if (!node->biss_RuntimeKeysReady &&
74 [ + + + + ]: 12247 : (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
75 : : {
5776 76 : 331 : ExecReScan((PlanState *) node);
7466 77 : 331 : doscan = node->biss_RuntimeKeysReady;
78 : : }
79 : : else
80 : 14843 : 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 : : */
7685 88 [ + + ]: 15174 : if (node->biss_result)
89 : : {
90 : 350 : tbm = node->biss_result;
3240 91 : 350 : node->biss_result = NULL; /* reset for next time */
92 : : }
93 : : else
94 : : {
95 : : /* XXX should we use less than work_mem for this? */
459 96 : 14824 : tbm = tbm_create(work_mem * (Size) 1024,
3345 rhaas@postgresql.org 97 [ + + ]: 14824 : ((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 : : */
7466 tgl@sss.pgh.pa.us 104 [ + + ]: 30385 : while (doscan)
105 : : {
6599 106 : 15211 : nTuples += (double) index_getbitmap(scandesc, tbm);
107 : :
7686 108 [ - + ]: 15211 : CHECK_FOR_INTERRUPTS();
109 : :
6599 110 : 15211 : doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
111 : : node->biss_NumArrayKeys);
6172 bruce@momjian.us 112 [ + + ]: 15211 : if (doscan) /* reset index scan */
5633 tgl@sss.pgh.pa.us 113 : 37 : index_rescan(node->biss_ScanDesc,
5633 tgl@sss.pgh.pa.us 114 :ECB (13) : node->biss_ScanKeys, node->biss_NumScanKeys,
115 : : NULL, 0);
116 : : }
117 : :
118 : : /* must provide our own instrumentation support */
7686 tgl@sss.pgh.pa.us 119 [ + + ]:CBC 15174 : if (node->ss.ps.instrument)
7280 bruce@momjian.us 120 : 290 : InstrStopNode(node->ss.ps.instrument, nTuples);
121 : :
7686 tgl@sss.pgh.pa.us 122 : 15174 : 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
5776 133 : 3266 : ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
134 : : {
135 : 3266 : 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 : : */
7686 142 [ + + ]: 3266 : if (econtext)
143 : 2960 : 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 : : */
7466 153 [ + + ]: 3266 : if (node->biss_NumRuntimeKeys != 0)
7680 154 : 2923 : ExecIndexEvalRuntimeKeys(econtext,
155 : : node->biss_RuntimeKeys,
156 : : node->biss_NumRuntimeKeys);
7466 157 [ + + ]: 3266 : if (node->biss_NumArrayKeys != 0)
158 : 37 : node->biss_RuntimeKeysReady =
159 : 37 : ExecIndexEvalArrayKeys(econtext,
160 : : node->biss_ArrayKeys,
161 : : node->biss_NumArrayKeys);
162 : : else
7686 163 : 3229 : node->biss_RuntimeKeysReady = true;
164 : :
165 : : /* reset index scan */
7466 166 [ + - ]: 3266 : if (node->biss_RuntimeKeysReady)
5633 167 : 3266 : index_rescan(node->biss_ScanDesc,
5633 tgl@sss.pgh.pa.us 168 :ECB (2776) : node->biss_ScanKeys, node->biss_NumScanKeys,
169 : : NULL, 0);
7686 tgl@sss.pgh.pa.us 170 :CBC 3266 : }
171 : :
172 : : /* ----------------------------------------------------------------
173 : : * ExecEndBitmapIndexScan
174 : : * ----------------------------------------------------------------
175 : : */
176 : : void
177 : 16100 : ExecEndBitmapIndexScan(BitmapIndexScanState *node)
178 : : {
179 : : Relation indexRelationDesc;
180 : : IndexScanDesc indexScanDesc;
181 : :
182 : : /*
183 : : * extract information from the node
184 : : */
7670 185 : 16100 : indexRelationDesc = node->biss_RelationDesc;
186 : 16100 : 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 : : */
420 pg@bowt.ie 193 [ - + - - ]: 16100 : if (node->biss_SharedInfo != NULL && IsParallelWorker())
194 : : {
195 : : IndexScanInstrumentation *winstrument;
196 : :
52 tomas.vondra@postgre 197 [ # # ]:UBC 0 : Assert(ParallelWorkerNumber < node->biss_SharedInfo->num_workers);
420 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 : : */
44 pg@bowt.ie 206 :UNC 0 : winstrument->nsearches += node->biss_Instrument->nsearches;
207 : : }
208 : :
209 : : /*
210 : : * close the index relation (no-op if we didn't open it)
211 : : */
6920 tgl@sss.pgh.pa.us 212 [ + + ]:CBC 16100 : if (indexScanDesc)
213 : 13547 : index_endscan(indexScanDesc);
214 [ + + ]: 16100 : if (indexRelationDesc)
215 : 13547 : index_close(indexRelationDesc, NoLock);
7686 216 : 16100 : }
217 : :
218 : : /* ----------------------------------------------------------------
219 : : * ExecInitBitmapIndexScan
220 : : *
221 : : * Initializes the index scan's state information.
222 : : * ----------------------------------------------------------------
223 : : */
224 : : BitmapIndexScanState *
7371 225 : 16181 : ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
226 : : {
227 : : BitmapIndexScanState *indexstate;
228 : : LOCKMODE lockmode;
229 : :
230 : : /* check for unsupported flags */
231 [ - + ]: 16181 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
232 : :
233 : : /*
234 : : * create state structure
235 : : */
7686 236 : 16181 : indexstate = makeNode(BitmapIndexScanState);
237 : 16181 : indexstate->ss.ps.plan = (Plan *) node;
238 : 16181 : indexstate->ss.ps.state = estate;
3214 andres@anarazel.de 239 : 16181 : indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
240 : :
241 : : /* normally we don't make the result bitmap till runtime */
7685 tgl@sss.pgh.pa.us 242 : 16181 : 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 : :
3000 andres@anarazel.de 250 : 16181 : indexstate->ss.ss_currentRelation = NULL;
251 : 16181 : 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 : : */
6920 tgl@sss.pgh.pa.us 274 [ + + ]: 16181 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
275 : 2553 : return indexstate;
276 : :
277 : : /* Set up instrumentation of bitmap index scans if requested */
44 pg@bowt.ie 278 [ + + ]:GNC 13628 : if (estate->es_instrument)
279 : 338 : indexstate->biss_Instrument = palloc0_object(IndexScanInstrumentation);
280 : :
281 : : /* Open the index relation. */
2588 tgl@sss.pgh.pa.us 282 :CBC 13628 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
283 : 13628 : indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
284 : :
285 : : /*
286 : : * Initialize index-specific scan state
287 : : */
7686 288 : 13628 : indexstate->biss_RuntimeKeysReady = false;
5633 289 : 13628 : indexstate->biss_RuntimeKeys = NULL;
290 : 13628 : indexstate->biss_NumRuntimeKeys = 0;
291 : :
292 : : /*
293 : : * build the index scan keys from the index qualification
294 : : */
7466 295 : 13628 : ExecIndexBuildScanKeys((PlanState *) indexstate,
296 : : indexstate->biss_RelationDesc,
297 : : node->indexqual,
298 : : false,
299 : 13628 : &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 [ + + ]: 13628 : if (indexstate->biss_NumRuntimeKeys != 0 ||
313 [ + + ]: 12738 : indexstate->biss_NumArrayKeys != 0)
7686 314 : 927 : {
315 : 927 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
316 : :
317 : 927 : ExecAssignExprContext(estate, &indexstate->ss.ps);
318 : 927 : indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
319 : 927 : indexstate->ss.ps.ps_ExprContext = stdecontext;
320 : : }
321 : : else
322 : : {
323 : 12701 : indexstate->biss_RuntimeContext = NULL;
324 : : }
325 : :
326 : : /*
327 : : * Initialize scan descriptor.
328 : : */
7670 329 : 13628 : indexstate->biss_ScanDesc =
6599 330 : 13628 : 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 : : */
5633 339 [ + + ]: 13628 : if (indexstate->biss_NumRuntimeKeys == 0 &&
340 [ + + ]: 12738 : indexstate->biss_NumArrayKeys == 0)
341 : 12701 : index_rescan(indexstate->biss_ScanDesc,
5633 tgl@sss.pgh.pa.us 342 :ECB (7539) : indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
343 : : NULL, 0);
344 : :
345 : : /*
346 : : * all done.
347 : : */
7686 tgl@sss.pgh.pa.us 348 :CBC 13628 : 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
420 pg@bowt.ie 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 : :
420 pg@bowt.ie 370 :UBC 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
420 pg@bowt.ie 383 :CBC 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 : :
420 pg@bowt.ie 392 :UBC 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);
29 melanieplageman@gmai 397 :UNC 0 : shm_toc_insert(pcxt->toc,
398 : 0 : node->ss.ps.plan->plan_node_id +
399 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
420 pg@bowt.ie 400 :UBC 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
420 pg@bowt.ie 414 :CBC 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 : :
420 pg@bowt.ie 421 :UBC 0 : node->biss_SharedInfo = (SharedIndexScanInstrumentation *)
29 melanieplageman@gmai 422 :UNC 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
420 pg@bowt.ie 435 :UBC 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 : : }
|