LCOV - code coverage report
Current view: top level - src/backend/executor - nodeBitmapIndexscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 76.1 % 117 89
Test Date: 2026-04-16 20:16:43 Functions: 77.8 % 9 7
Legend: Lines:     hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1