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-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 *
2973 andres@anarazel.de 38 :UBC 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 *
7445 tgl@sss.pgh.pa.us 49 :CBC 10151 : MultiExecBitmapIndexScan(BitmapIndexScanState *node)
50 : : {
51 : : TIDBitmap *tbm;
52 : : IndexScanDesc scandesc;
53 : 10151 : double nTuples = 0;
54 : : bool doscan;
55 : :
56 : : /* must provide our own instrumentation support */
57 [ + + ]: 10151 : if (node->ss.ps.instrument)
58 : 218 : InstrStartNode(node->ss.ps.instrument);
59 : :
60 : : /*
61 : : * extract necessary information from index scan node
62 : : */
7429 63 : 10151 : 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 : : */
7225 71 [ + + ]: 10151 : if (!node->biss_RuntimeKeysReady &&
72 [ + + + + ]: 7442 : (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
73 : : {
5535 74 : 220 : ExecReScan((PlanState *) node);
7225 75 : 220 : doscan = node->biss_RuntimeKeysReady;
76 : : }
77 : : else
78 : 9931 : 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 : : */
7444 86 [ + + ]: 10151 : if (node->biss_result)
87 : : {
88 : 301 : tbm = node->biss_result;
2999 89 : 301 : node->biss_result = NULL; /* reset for next time */
90 : : }
91 : : else
92 : : {
93 : : /* XXX should we use less than work_mem for this? */
218 94 : 9850 : tbm = tbm_create(work_mem * (Size) 1024,
3104 rhaas@postgresql.org 95 [ + + ]: 9850 : ((BitmapIndexScan *) node->ss.ps.plan)->isshared ?
96 : 36 : node->ss.ps.state->es_query_dsa : NULL);
97 : : }
98 : :
99 : : /*
100 : : * Get TIDs from index and insert into bitmap
101 : : */
7225 tgl@sss.pgh.pa.us 102 [ + + ]: 20315 : while (doscan)
103 : : {
6358 104 : 10164 : nTuples += (double) index_getbitmap(scandesc, tbm);
105 : :
7445 106 [ - + ]: 10164 : CHECK_FOR_INTERRUPTS();
107 : :
6358 108 : 10164 : doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
109 : : node->biss_NumArrayKeys);
5931 bruce@momjian.us 110 [ + + ]: 10164 : if (doscan) /* reset index scan */
5392 tgl@sss.pgh.pa.us 111 : 13 : index_rescan(node->biss_ScanDesc,
112 : 13 : node->biss_ScanKeys, node->biss_NumScanKeys,
113 : : NULL, 0);
114 : : }
115 : :
116 : : /* must provide our own instrumentation support */
7445 117 [ + + ]: 10151 : if (node->ss.ps.instrument)
7039 bruce@momjian.us 118 : 218 : InstrStopNode(node->ss.ps.instrument, nTuples);
119 : :
7445 tgl@sss.pgh.pa.us 120 : 10151 : 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
5535 131 : 2935 : ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
132 : : {
133 : 2935 : 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 : : */
7445 140 [ + + ]: 2935 : if (econtext)
141 : 2596 : 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 : : */
7225 151 [ + + ]: 2935 : if (node->biss_NumRuntimeKeys != 0)
7439 152 : 2583 : ExecIndexEvalRuntimeKeys(econtext,
153 : : node->biss_RuntimeKeys,
154 : : node->biss_NumRuntimeKeys);
7225 155 [ + + ]: 2935 : if (node->biss_NumArrayKeys != 0)
156 : 13 : node->biss_RuntimeKeysReady =
157 : 13 : ExecIndexEvalArrayKeys(econtext,
158 : : node->biss_ArrayKeys,
159 : : node->biss_NumArrayKeys);
160 : : else
7445 161 : 2922 : node->biss_RuntimeKeysReady = true;
162 : :
163 : : /* reset index scan */
7225 164 [ + - ]: 2935 : if (node->biss_RuntimeKeysReady)
5392 165 : 2935 : index_rescan(node->biss_ScanDesc,
166 : 2935 : node->biss_ScanKeys, node->biss_NumScanKeys,
167 : : NULL, 0);
7445 168 : 2935 : }
169 : :
170 : : /* ----------------------------------------------------------------
171 : : * ExecEndBitmapIndexScan
172 : : * ----------------------------------------------------------------
173 : : */
174 : : void
175 : 10592 : ExecEndBitmapIndexScan(BitmapIndexScanState *node)
176 : : {
177 : : Relation indexRelationDesc;
178 : : IndexScanDesc indexScanDesc;
179 : :
180 : : /*
181 : : * extract information from the node
182 : : */
7429 183 : 10592 : indexRelationDesc = node->biss_RelationDesc;
184 : 10592 : 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 : : */
179 pg@bowt.ie 191 [ - + - - ]: 10592 : if (node->biss_SharedInfo != NULL && IsParallelWorker())
192 : : {
193 : : IndexScanInstrumentation *winstrument;
194 : :
179 pg@bowt.ie 195 [ # # ]:UBC 0 : 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 : : */
6679 tgl@sss.pgh.pa.us 210 [ + + ]:CBC 10592 : if (indexScanDesc)
211 : 8682 : index_endscan(indexScanDesc);
212 [ + + ]: 10592 : if (indexRelationDesc)
213 : 8682 : index_close(indexRelationDesc, NoLock);
7445 214 : 10592 : }
215 : :
216 : : /* ----------------------------------------------------------------
217 : : * ExecInitBitmapIndexScan
218 : : *
219 : : * Initializes the index scan's state information.
220 : : * ----------------------------------------------------------------
221 : : */
222 : : BitmapIndexScanState *
7130 223 : 10642 : ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
224 : : {
225 : : BitmapIndexScanState *indexstate;
226 : : LOCKMODE lockmode;
227 : :
228 : : /* check for unsupported flags */
229 [ - + ]: 10642 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
230 : :
231 : : /*
232 : : * create state structure
233 : : */
7445 234 : 10642 : indexstate = makeNode(BitmapIndexScanState);
235 : 10642 : indexstate->ss.ps.plan = (Plan *) node;
236 : 10642 : indexstate->ss.ps.state = estate;
2973 andres@anarazel.de 237 : 10642 : indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
238 : :
239 : : /* normally we don't make the result bitmap till runtime */
7444 tgl@sss.pgh.pa.us 240 : 10642 : 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 : :
2759 andres@anarazel.de 248 : 10642 : indexstate->ss.ss_currentRelation = NULL;
249 : 10642 : 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 : : */
6679 tgl@sss.pgh.pa.us 272 [ + + ]: 10642 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
273 : 1910 : return indexstate;
274 : :
275 : : /* Open the index relation. */
2347 276 : 8732 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
277 : 8732 : indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
278 : :
279 : : /*
280 : : * Initialize index-specific scan state
281 : : */
7445 282 : 8732 : indexstate->biss_RuntimeKeysReady = false;
5392 283 : 8732 : indexstate->biss_RuntimeKeys = NULL;
284 : 8732 : indexstate->biss_NumRuntimeKeys = 0;
285 : :
286 : : /*
287 : : * build the index scan keys from the index qualification
288 : : */
7225 289 : 8732 : ExecIndexBuildScanKeys((PlanState *) indexstate,
290 : : indexstate->biss_RelationDesc,
291 : : node->indexqual,
292 : : false,
293 : 8732 : &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 [ + + ]: 8732 : if (indexstate->biss_NumRuntimeKeys != 0 ||
307 [ + + ]: 7942 : indexstate->biss_NumArrayKeys != 0)
7445 308 : 803 : {
309 : 803 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
310 : :
311 : 803 : ExecAssignExprContext(estate, &indexstate->ss.ps);
312 : 803 : indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
313 : 803 : indexstate->ss.ps.ps_ExprContext = stdecontext;
314 : : }
315 : : else
316 : : {
317 : 7929 : indexstate->biss_RuntimeContext = NULL;
318 : : }
319 : :
320 : : /*
321 : : * Initialize scan descriptor.
322 : : */
7429 323 : 8732 : indexstate->biss_ScanDesc =
6358 324 : 8732 : 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 : : */
5392 333 [ + + ]: 8732 : if (indexstate->biss_NumRuntimeKeys == 0 &&
334 [ + + ]: 7942 : indexstate->biss_NumArrayKeys == 0)
335 : 7929 : index_rescan(indexstate->biss_ScanDesc,
336 : 7929 : indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
337 : : NULL, 0);
338 : :
339 : : /*
340 : : * all done.
341 : : */
7445 342 : 8732 : 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
179 pg@bowt.ie 353 : 10 : 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 [ - + - - ]: 10 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
362 : 10 : return;
363 : :
179 pg@bowt.ie 364 :UBC 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
179 pg@bowt.ie 377 :CBC 10 : ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node,
378 : : ParallelContext *pcxt)
379 : : {
380 : : Size size;
381 : :
382 : : /* don't need this if not instrumenting or no workers */
383 [ - + - - ]: 10 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
384 : 10 : return;
385 : :
179 pg@bowt.ie 386 :UBC 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
179 pg@bowt.ie 406 :CBC 136 : ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
407 : : ParallelWorkerContext *pwcxt)
408 : : {
409 : : /* don't need this if not instrumenting */
410 [ + - ]: 136 : if (!node->ss.ps.instrument)
411 : 136 : return;
412 : :
179 pg@bowt.ie 413 :UBC 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 : : }
|