Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/nodeBitmapIndexscan.c
Line
Count
Source
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
ExecBitmapIndexScan(PlanState *pstate)
41
0
{
42
0
  elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention");
43
0
  return NULL;
44
0
}
45
46
/* ----------------------------------------------------------------
47
 *    MultiExecBitmapIndexScan(node)
48
 * ----------------------------------------------------------------
49
 */
50
Node *
51
MultiExecBitmapIndexScan(BitmapIndexScanState *node)
52
0
{
53
0
  TIDBitmap  *tbm;
54
0
  IndexScanDesc scandesc;
55
0
  double    nTuples = 0;
56
0
  bool    doscan;
57
58
  /* must provide our own instrumentation support */
59
0
  if (node->ss.ps.instrument)
60
0
    InstrStartNode(node->ss.ps.instrument);
61
62
  /*
63
   * extract necessary information from index scan node
64
   */
65
0
  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
0
  if (!node->biss_RuntimeKeysReady &&
74
0
    (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
75
0
  {
76
0
    ExecReScan((PlanState *) node);
77
0
    doscan = node->biss_RuntimeKeysReady;
78
0
  }
79
0
  else
80
0
    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
0
  if (node->biss_result)
89
0
  {
90
0
    tbm = node->biss_result;
91
0
    node->biss_result = NULL; /* reset for next time */
92
0
  }
93
0
  else
94
0
  {
95
    /* XXX should we use less than work_mem for this? */
96
0
    tbm = tbm_create(work_mem * (Size) 1024,
97
0
             ((BitmapIndexScan *) node->ss.ps.plan)->isshared ?
98
0
             node->ss.ps.state->es_query_dsa : NULL);
99
0
  }
100
101
  /*
102
   * Get TIDs from index and insert into bitmap
103
   */
104
0
  while (doscan)
105
0
  {
106
0
    nTuples += (double) index_getbitmap(scandesc, tbm);
107
108
0
    CHECK_FOR_INTERRUPTS();
109
110
0
    doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
111
0
                       node->biss_NumArrayKeys);
112
0
    if (doscan)       /* reset index scan */
113
0
      index_rescan(node->biss_ScanDesc,
114
0
             node->biss_ScanKeys, node->biss_NumScanKeys,
115
0
             NULL, 0);
116
0
  }
117
118
  /* must provide our own instrumentation support */
119
0
  if (node->ss.ps.instrument)
120
0
    InstrStopNode(node->ss.ps.instrument, nTuples);
121
122
0
  return (Node *) tbm;
123
0
}
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
ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
134
0
{
135
0
  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
0
  if (econtext)
143
0
    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
0
  if (node->biss_NumRuntimeKeys != 0)
154
0
    ExecIndexEvalRuntimeKeys(econtext,
155
0
                 node->biss_RuntimeKeys,
156
0
                 node->biss_NumRuntimeKeys);
157
0
  if (node->biss_NumArrayKeys != 0)
158
0
    node->biss_RuntimeKeysReady =
159
0
      ExecIndexEvalArrayKeys(econtext,
160
0
                   node->biss_ArrayKeys,
161
0
                   node->biss_NumArrayKeys);
162
0
  else
163
0
    node->biss_RuntimeKeysReady = true;
164
165
  /* reset index scan */
166
0
  if (node->biss_RuntimeKeysReady)
167
0
    index_rescan(node->biss_ScanDesc,
168
0
           node->biss_ScanKeys, node->biss_NumScanKeys,
169
0
           NULL, 0);
170
0
}
171
172
/* ----------------------------------------------------------------
173
 *    ExecEndBitmapIndexScan
174
 * ----------------------------------------------------------------
175
 */
176
void
177
ExecEndBitmapIndexScan(BitmapIndexScanState *node)
178
0
{
179
0
  Relation  indexRelationDesc;
180
0
  IndexScanDesc indexScanDesc;
181
182
  /*
183
   * extract information from the node
184
   */
185
0
  indexRelationDesc = node->biss_RelationDesc;
186
0
  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
0
  if (node->biss_SharedInfo != NULL && IsParallelWorker())
194
0
  {
195
0
    IndexScanInstrumentation *winstrument;
196
197
0
    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
0
    Assert(node->biss_Instrument->ntabletuplefetches == 0);
208
0
  }
209
210
  /*
211
   * close the index relation (no-op if we didn't open it)
212
   */
213
0
  if (indexScanDesc)
214
0
    index_endscan(indexScanDesc);
215
0
  if (indexRelationDesc)
216
0
    index_close(indexRelationDesc, NoLock);
217
0
}
218
219
/* ----------------------------------------------------------------
220
 *    ExecInitBitmapIndexScan
221
 *
222
 *    Initializes the index scan's state information.
223
 * ----------------------------------------------------------------
224
 */
225
BitmapIndexScanState *
226
ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
227
0
{
228
0
  BitmapIndexScanState *indexstate;
229
0
  LOCKMODE  lockmode;
230
231
  /* check for unsupported flags */
232
0
  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
233
234
  /*
235
   * create state structure
236
   */
237
0
  indexstate = makeNode(BitmapIndexScanState);
238
0
  indexstate->ss.ps.plan = (Plan *) node;
239
0
  indexstate->ss.ps.state = estate;
240
0
  indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
241
242
  /* normally we don't make the result bitmap till runtime */
243
0
  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
251
0
  indexstate->ss.ss_currentRelation = NULL;
252
0
  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
   */
275
0
  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
276
0
    return indexstate;
277
278
  /* Set up instrumentation of bitmap index scans if requested */
279
0
  if (estate->es_instrument)
280
0
    indexstate->biss_Instrument = palloc0_object(IndexScanInstrumentation);
281
282
  /* Open the index relation. */
283
0
  lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
284
0
  indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
285
286
  /*
287
   * Initialize index-specific scan state
288
   */
289
0
  indexstate->biss_RuntimeKeysReady = false;
290
0
  indexstate->biss_RuntimeKeys = NULL;
291
0
  indexstate->biss_NumRuntimeKeys = 0;
292
293
  /*
294
   * build the index scan keys from the index qualification
295
   */
296
0
  ExecIndexBuildScanKeys((PlanState *) indexstate,
297
0
               indexstate->biss_RelationDesc,
298
0
               node->indexqual,
299
0
               false,
300
0
               &indexstate->biss_ScanKeys,
301
0
               &indexstate->biss_NumScanKeys,
302
0
               &indexstate->biss_RuntimeKeys,
303
0
               &indexstate->biss_NumRuntimeKeys,
304
0
               &indexstate->biss_ArrayKeys,
305
0
               &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
0
  if (indexstate->biss_NumRuntimeKeys != 0 ||
314
0
    indexstate->biss_NumArrayKeys != 0)
315
0
  {
316
0
    ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
317
318
0
    ExecAssignExprContext(estate, &indexstate->ss.ps);
319
0
    indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
320
0
    indexstate->ss.ps.ps_ExprContext = stdecontext;
321
0
  }
322
0
  else
323
0
  {
324
0
    indexstate->biss_RuntimeContext = NULL;
325
0
  }
326
327
  /*
328
   * Initialize scan descriptor.
329
   */
330
0
  indexstate->biss_ScanDesc =
331
0
    index_beginscan_bitmap(indexstate->biss_RelationDesc,
332
0
                 estate->es_snapshot,
333
0
                 indexstate->biss_Instrument,
334
0
                 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
   */
340
0
  if (indexstate->biss_NumRuntimeKeys == 0 &&
341
0
    indexstate->biss_NumArrayKeys == 0)
342
0
    index_rescan(indexstate->biss_ScanDesc,
343
0
           indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
344
0
           NULL, 0);
345
346
  /*
347
   * all done.
348
   */
349
0
  return indexstate;
350
0
}
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
360
ExecBitmapIndexScanEstimate(BitmapIndexScanState *node, ParallelContext *pcxt)
361
0
{
362
0
  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
0
  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
369
0
    return;
370
371
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
0
}
376
377
/* ----------------------------------------------------------------
378
 *    ExecBitmapIndexScanInitializeDSM
379
 *
380
 *    Set up bitmap index scan shared instrumentation.
381
 * ----------------------------------------------------------------
382
 */
383
void
384
ExecBitmapIndexScanInitializeDSM(BitmapIndexScanState *node,
385
                 ParallelContext *pcxt)
386
0
{
387
0
  Size    size;
388
389
  /* don't need this if not instrumenting or no workers */
390
0
  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
391
0
    return;
392
393
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
0
                              size);
398
0
  shm_toc_insert(pcxt->toc,
399
0
           node->ss.ps.plan->plan_node_id +
400
0
           PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
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
0
}
407
408
/* ----------------------------------------------------------------
409
 *    ExecBitmapIndexScanInitializeWorker
410
 *
411
 *    Copy relevant information from TOC into planstate.
412
 * ----------------------------------------------------------------
413
 */
414
void
415
ExecBitmapIndexScanInitializeWorker(BitmapIndexScanState *node,
416
                  ParallelWorkerContext *pwcxt)
417
0
{
418
  /* don't need this if not instrumenting */
419
0
  if (!node->ss.ps.instrument)
420
0
    return;
421
422
0
  node->biss_SharedInfo = (SharedIndexScanInstrumentation *)
423
0
    shm_toc_lookup(pwcxt->toc,
424
0
             node->ss.ps.plan->plan_node_id +
425
0
             PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
426
0
             false);
427
0
}
428
429
/* ----------------------------------------------------------------
430
 * ExecBitmapIndexScanRetrieveInstrumentation
431
 *
432
 *    Transfer bitmap index scan statistics from DSM to private memory.
433
 * ----------------------------------------------------------------
434
 */
435
void
436
ExecBitmapIndexScanRetrieveInstrumentation(BitmapIndexScanState *node)
437
0
{
438
0
  SharedIndexScanInstrumentation *SharedInfo = node->biss_SharedInfo;
439
0
  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
0
}