LCOV - code coverage report
Current view: top level - src/backend/executor - nodeBitmapIndexscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.0 % 113 87
Test Date: 2026-03-01 00:15:48 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/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 *
      38            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 *
      49        13218 : MultiExecBitmapIndexScan(BitmapIndexScanState *node)
      50              : {
      51              :     TIDBitmap  *tbm;
      52              :     IndexScanDesc scandesc;
      53        13218 :     double      nTuples = 0;
      54              :     bool        doscan;
      55              : 
      56              :     /* must provide our own instrumentation support */
      57        13218 :     if (node->ss.ps.instrument)
      58          218 :         InstrStartNode(node->ss.ps.instrument);
      59              : 
      60              :     /*
      61              :      * extract necessary information from index scan node
      62              :      */
      63        13218 :     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              :      */
      71        13218 :     if (!node->biss_RuntimeKeysReady &&
      72        10582 :         (node->biss_NumRuntimeKeys != 0 || node->biss_NumArrayKeys != 0))
      73              :     {
      74          214 :         ExecReScan((PlanState *) node);
      75          214 :         doscan = node->biss_RuntimeKeysReady;
      76              :     }
      77              :     else
      78        13004 :         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              :      */
      86        13218 :     if (node->biss_result)
      87              :     {
      88          313 :         tbm = node->biss_result;
      89          313 :         node->biss_result = NULL;    /* reset for next time */
      90              :     }
      91              :     else
      92              :     {
      93              :         /* XXX should we use less than work_mem for this? */
      94        12905 :         tbm = tbm_create(work_mem * (Size) 1024,
      95        12905 :                          ((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              :      */
     102        26449 :     while (doscan)
     103              :     {
     104        13231 :         nTuples += (double) index_getbitmap(scandesc, tbm);
     105              : 
     106        13231 :         CHECK_FOR_INTERRUPTS();
     107              : 
     108        13231 :         doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
     109              :                                            node->biss_NumArrayKeys);
     110        13231 :         if (doscan)             /* reset index scan */
     111           13 :             index_rescan(node->biss_ScanDesc,
     112              :                          node->biss_ScanKeys, node->biss_NumScanKeys,
     113              :                          NULL, 0);
     114              :     }
     115              : 
     116              :     /* must provide our own instrumentation support */
     117        13218 :     if (node->ss.ps.instrument)
     118          218 :         InstrStopNode(node->ss.ps.instrument, nTuples);
     119              : 
     120        13218 :     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
     131         2856 : ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
     132              : {
     133         2856 :     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              :      */
     140         2856 :     if (econtext)
     141         2593 :         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              :      */
     151         2856 :     if (node->biss_NumRuntimeKeys != 0)
     152         2580 :         ExecIndexEvalRuntimeKeys(econtext,
     153              :                                  node->biss_RuntimeKeys,
     154              :                                  node->biss_NumRuntimeKeys);
     155         2856 :     if (node->biss_NumArrayKeys != 0)
     156           13 :         node->biss_RuntimeKeysReady =
     157           13 :             ExecIndexEvalArrayKeys(econtext,
     158              :                                    node->biss_ArrayKeys,
     159              :                                    node->biss_NumArrayKeys);
     160              :     else
     161         2843 :         node->biss_RuntimeKeysReady = true;
     162              : 
     163              :     /* reset index scan */
     164         2856 :     if (node->biss_RuntimeKeysReady)
     165         2856 :         index_rescan(node->biss_ScanDesc,
     166              :                      node->biss_ScanKeys, node->biss_NumScanKeys,
     167              :                      NULL, 0);
     168         2856 : }
     169              : 
     170              : /* ----------------------------------------------------------------
     171              :  *      ExecEndBitmapIndexScan
     172              :  * ----------------------------------------------------------------
     173              :  */
     174              : void
     175        13612 : ExecEndBitmapIndexScan(BitmapIndexScanState *node)
     176              : {
     177              :     Relation    indexRelationDesc;
     178              :     IndexScanDesc indexScanDesc;
     179              : 
     180              :     /*
     181              :      * extract information from the node
     182              :      */
     183        13612 :     indexRelationDesc = node->biss_RelationDesc;
     184        13612 :     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              :      */
     191        13612 :     if (node->biss_SharedInfo != NULL && IsParallelWorker())
     192              :     {
     193              :         IndexScanInstrumentation *winstrument;
     194              : 
     195              :         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              :      */
     210        13612 :     if (indexScanDesc)
     211        11684 :         index_endscan(indexScanDesc);
     212        13612 :     if (indexRelationDesc)
     213        11684 :         index_close(indexRelationDesc, NoLock);
     214        13612 : }
     215              : 
     216              : /* ----------------------------------------------------------------
     217              :  *      ExecInitBitmapIndexScan
     218              :  *
     219              :  *      Initializes the index scan's state information.
     220              :  * ----------------------------------------------------------------
     221              :  */
     222              : BitmapIndexScanState *
     223        13669 : ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
     224              : {
     225              :     BitmapIndexScanState *indexstate;
     226              :     LOCKMODE    lockmode;
     227              : 
     228              :     /* check for unsupported flags */
     229              :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
     230              : 
     231              :     /*
     232              :      * create state structure
     233              :      */
     234        13669 :     indexstate = makeNode(BitmapIndexScanState);
     235        13669 :     indexstate->ss.ps.plan = (Plan *) node;
     236        13669 :     indexstate->ss.ps.state = estate;
     237        13669 :     indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;
     238              : 
     239              :     /* normally we don't make the result bitmap till runtime */
     240        13669 :     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              : 
     248        13669 :     indexstate->ss.ss_currentRelation = NULL;
     249        13669 :     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              :      */
     272        13669 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     273         1928 :         return indexstate;
     274              : 
     275              :     /* Open the index relation. */
     276        11741 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     277        11741 :     indexstate->biss_RelationDesc = index_open(node->indexid, lockmode);
     278              : 
     279              :     /*
     280              :      * Initialize index-specific scan state
     281              :      */
     282        11741 :     indexstate->biss_RuntimeKeysReady = false;
     283        11741 :     indexstate->biss_RuntimeKeys = NULL;
     284        11741 :     indexstate->biss_NumRuntimeKeys = 0;
     285              : 
     286              :     /*
     287              :      * build the index scan keys from the index qualification
     288              :      */
     289        11741 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     290              :                            indexstate->biss_RelationDesc,
     291              :                            node->indexqual,
     292              :                            false,
     293        11741 :                            &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        11741 :     if (indexstate->biss_NumRuntimeKeys != 0 ||
     307        11047 :         indexstate->biss_NumArrayKeys != 0)
     308          707 :     {
     309          707 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
     310              : 
     311          707 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
     312          707 :         indexstate->biss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
     313          707 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
     314              :     }
     315              :     else
     316              :     {
     317        11034 :         indexstate->biss_RuntimeContext = NULL;
     318              :     }
     319              : 
     320              :     /*
     321              :      * Initialize scan descriptor.
     322              :      */
     323        11741 :     indexstate->biss_ScanDesc =
     324        11741 :         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              :      */
     333        11741 :     if (indexstate->biss_NumRuntimeKeys == 0 &&
     334        11047 :         indexstate->biss_NumArrayKeys == 0)
     335        11034 :         index_rescan(indexstate->biss_ScanDesc,
     336              :                      indexstate->biss_ScanKeys, indexstate->biss_NumScanKeys,
     337              :                      NULL, 0);
     338              : 
     339              :     /*
     340              :      * all done.
     341              :      */
     342        11741 :     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
     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              : 
     364            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
     377           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              : 
     386            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
     406          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              : 
     413            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              : }
        

Generated by: LCOV version 2.0-1