LCOV - code coverage report
Current view: top level - src/backend/executor - nodeSeqscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 100.0 % 89 89
Test Date: 2026-02-28 23:15:01 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeSeqscan.c
       4              :  *    Support routines for sequential 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/nodeSeqscan.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : /*
      16              :  * INTERFACE ROUTINES
      17              :  *      ExecSeqScan             sequentially scans a relation.
      18              :  *      ExecSeqNext             retrieve next tuple in sequential order.
      19              :  *      ExecInitSeqScan         creates and initializes a seqscan node.
      20              :  *      ExecEndSeqScan          releases any storage allocated.
      21              :  *      ExecReScanSeqScan       rescans the relation
      22              :  *
      23              :  *      ExecSeqScanEstimate     estimates DSM space needed for parallel scan
      24              :  *      ExecSeqScanInitializeDSM initialize DSM for parallel scan
      25              :  *      ExecSeqScanReInitializeDSM reinitialize DSM for fresh parallel scan
      26              :  *      ExecSeqScanInitializeWorker attach to DSM info in parallel worker
      27              :  */
      28              : #include "postgres.h"
      29              : 
      30              : #include "access/relscan.h"
      31              : #include "access/tableam.h"
      32              : #include "executor/execScan.h"
      33              : #include "executor/executor.h"
      34              : #include "executor/nodeSeqscan.h"
      35              : #include "utils/rel.h"
      36              : 
      37              : static TupleTableSlot *SeqNext(SeqScanState *node);
      38              : 
      39              : /* ----------------------------------------------------------------
      40              :  *                      Scan Support
      41              :  * ----------------------------------------------------------------
      42              :  */
      43              : 
      44              : /* ----------------------------------------------------------------
      45              :  *      SeqNext
      46              :  *
      47              :  *      This is a workhorse for ExecSeqScan
      48              :  * ----------------------------------------------------------------
      49              :  */
      50              : static pg_attribute_always_inline TupleTableSlot *
      51     45144375 : SeqNext(SeqScanState *node)
      52              : {
      53              :     TableScanDesc scandesc;
      54              :     EState     *estate;
      55              :     ScanDirection direction;
      56              :     TupleTableSlot *slot;
      57              : 
      58              :     /*
      59              :      * get information from the estate and scan state
      60              :      */
      61     45144375 :     scandesc = node->ss.ss_currentScanDesc;
      62     45144375 :     estate = node->ss.ps.state;
      63     45144375 :     direction = estate->es_direction;
      64     45144375 :     slot = node->ss.ss_ScanTupleSlot;
      65              : 
      66     45144375 :     if (scandesc == NULL)
      67              :     {
      68              :         /*
      69              :          * We reach here if the scan is not parallel, or if we're serially
      70              :          * executing a scan that was planned to be parallel.
      71              :          */
      72       101633 :         scandesc = table_beginscan(node->ss.ss_currentRelation,
      73              :                                    estate->es_snapshot,
      74              :                                    0, NULL);
      75       101633 :         node->ss.ss_currentScanDesc = scandesc;
      76              :     }
      77              : 
      78              :     /*
      79              :      * get the next tuple from the table
      80              :      */
      81     45144375 :     if (table_scan_getnextslot(scandesc, direction, slot))
      82     44228563 :         return slot;
      83       915787 :     return NULL;
      84              : }
      85              : 
      86              : /*
      87              :  * SeqRecheck -- access method routine to recheck a tuple in EvalPlanQual
      88              :  */
      89              : static pg_attribute_always_inline bool
      90          132 : SeqRecheck(SeqScanState *node, TupleTableSlot *slot)
      91              : {
      92              :     /*
      93              :      * Note that unlike IndexScan, SeqScan never use keys in heap_beginscan
      94              :      * (and this is very bad) - so, here we do not check are keys ok or not.
      95              :      */
      96          132 :     return true;
      97              : }
      98              : 
      99              : /* ----------------------------------------------------------------
     100              :  *      ExecSeqScan(node)
     101              :  *
     102              :  *      Scans the relation sequentially and returns the next qualifying
     103              :  *      tuple. This variant is used when there is no es_epq_active, no qual
     104              :  *      and no projection.  Passing const-NULLs for these to ExecScanExtended
     105              :  *      allows the compiler to eliminate the additional code that would
     106              :  *      ordinarily be required for the evaluation of these.
     107              :  * ----------------------------------------------------------------
     108              :  */
     109              : static TupleTableSlot *
     110      7044657 : ExecSeqScan(PlanState *pstate)
     111              : {
     112      7044657 :     SeqScanState *node = castNode(SeqScanState, pstate);
     113              : 
     114              :     Assert(pstate->state->es_epq_active == NULL);
     115              :     Assert(pstate->qual == NULL);
     116              :     Assert(pstate->ps_ProjInfo == NULL);
     117              : 
     118      7044657 :     return ExecScanExtended(&node->ss,
     119              :                             (ExecScanAccessMtd) SeqNext,
     120              :                             (ExecScanRecheckMtd) SeqRecheck,
     121              :                             NULL,
     122              :                             NULL,
     123              :                             NULL);
     124              : }
     125              : 
     126              : /*
     127              :  * Variant of ExecSeqScan() but when qual evaluation is required.
     128              :  */
     129              : static TupleTableSlot *
     130      3472927 : ExecSeqScanWithQual(PlanState *pstate)
     131              : {
     132      3472927 :     SeqScanState *node = castNode(SeqScanState, pstate);
     133              : 
     134              :     /*
     135              :      * Use pg_assume() for != NULL tests to make the compiler realize no
     136              :      * runtime check for the field is needed in ExecScanExtended().
     137              :      */
     138              :     Assert(pstate->state->es_epq_active == NULL);
     139      3472927 :     pg_assume(pstate->qual != NULL);
     140              :     Assert(pstate->ps_ProjInfo == NULL);
     141              : 
     142      3472927 :     return ExecScanExtended(&node->ss,
     143              :                             (ExecScanAccessMtd) SeqNext,
     144              :                             (ExecScanRecheckMtd) SeqRecheck,
     145              :                             NULL,
     146              :                             pstate->qual,
     147              :                             NULL);
     148              : }
     149              : 
     150              : /*
     151              :  * Variant of ExecSeqScan() but when projection is required.
     152              :  */
     153              : static TupleTableSlot *
     154     12633856 : ExecSeqScanWithProject(PlanState *pstate)
     155              : {
     156     12633856 :     SeqScanState *node = castNode(SeqScanState, pstate);
     157              : 
     158              :     Assert(pstate->state->es_epq_active == NULL);
     159              :     Assert(pstate->qual == NULL);
     160     12633856 :     pg_assume(pstate->ps_ProjInfo != NULL);
     161              : 
     162     12633856 :     return ExecScanExtended(&node->ss,
     163              :                             (ExecScanAccessMtd) SeqNext,
     164              :                             (ExecScanRecheckMtd) SeqRecheck,
     165              :                             NULL,
     166              :                             NULL,
     167              :                             pstate->ps_ProjInfo);
     168              : }
     169              : 
     170              : /*
     171              :  * Variant of ExecSeqScan() but when qual evaluation and projection are
     172              :  * required.
     173              :  */
     174              : static TupleTableSlot *
     175      4248492 : ExecSeqScanWithQualProject(PlanState *pstate)
     176              : {
     177      4248492 :     SeqScanState *node = castNode(SeqScanState, pstate);
     178              : 
     179              :     Assert(pstate->state->es_epq_active == NULL);
     180      4248492 :     pg_assume(pstate->qual != NULL);
     181      4248492 :     pg_assume(pstate->ps_ProjInfo != NULL);
     182              : 
     183      4248492 :     return ExecScanExtended(&node->ss,
     184              :                             (ExecScanAccessMtd) SeqNext,
     185              :                             (ExecScanRecheckMtd) SeqRecheck,
     186              :                             NULL,
     187              :                             pstate->qual,
     188              :                             pstate->ps_ProjInfo);
     189              : }
     190              : 
     191              : /*
     192              :  * Variant of ExecSeqScan for when EPQ evaluation is required.  We don't
     193              :  * bother adding variants of this for with/without qual and projection as
     194              :  * EPQ doesn't seem as exciting a case to optimize for.
     195              :  */
     196              : static TupleTableSlot *
     197          261 : ExecSeqScanEPQ(PlanState *pstate)
     198              : {
     199          261 :     SeqScanState *node = castNode(SeqScanState, pstate);
     200              : 
     201          261 :     return ExecScan(&node->ss,
     202              :                     (ExecScanAccessMtd) SeqNext,
     203              :                     (ExecScanRecheckMtd) SeqRecheck);
     204              : }
     205              : 
     206              : /* ----------------------------------------------------------------
     207              :  *      ExecInitSeqScan
     208              :  * ----------------------------------------------------------------
     209              :  */
     210              : SeqScanState *
     211       126669 : ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
     212              : {
     213              :     SeqScanState *scanstate;
     214              : 
     215              :     /*
     216              :      * Once upon a time it was possible to have an outerPlan of a SeqScan, but
     217              :      * not any more.
     218              :      */
     219              :     Assert(outerPlan(node) == NULL);
     220              :     Assert(innerPlan(node) == NULL);
     221              : 
     222              :     /*
     223              :      * create state structure
     224              :      */
     225       126669 :     scanstate = makeNode(SeqScanState);
     226       126669 :     scanstate->ss.ps.plan = (Plan *) node;
     227       126669 :     scanstate->ss.ps.state = estate;
     228              : 
     229              :     /*
     230              :      * Miscellaneous initialization
     231              :      *
     232              :      * create expression context for node
     233              :      */
     234       126669 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     235              : 
     236              :     /*
     237              :      * open the scan relation
     238              :      */
     239       126663 :     scanstate->ss.ss_currentRelation =
     240       126669 :         ExecOpenScanRelation(estate,
     241              :                              node->scan.scanrelid,
     242              :                              eflags);
     243              : 
     244              :     /* and create slot with the appropriate rowtype */
     245       126663 :     ExecInitScanTupleSlot(estate, &scanstate->ss,
     246       126663 :                           RelationGetDescr(scanstate->ss.ss_currentRelation),
     247              :                           table_slot_callbacks(scanstate->ss.ss_currentRelation));
     248              : 
     249              :     /*
     250              :      * Initialize result type and projection.
     251              :      */
     252       126663 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     253       126663 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     254              : 
     255              :     /*
     256              :      * initialize child expressions
     257              :      */
     258       126663 :     scanstate->ss.ps.qual =
     259       126663 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     260              : 
     261              :     /*
     262              :      * When EvalPlanQual() is not in use, assign ExecProcNode for this node
     263              :      * based on the presence of qual and projection. Each ExecSeqScan*()
     264              :      * variant is optimized for the specific combination of these conditions.
     265              :      */
     266       126663 :     if (scanstate->ss.ps.state->es_epq_active != NULL)
     267          146 :         scanstate->ss.ps.ExecProcNode = ExecSeqScanEPQ;
     268       126517 :     else if (scanstate->ss.ps.qual == NULL)
     269              :     {
     270        64680 :         if (scanstate->ss.ps.ps_ProjInfo == NULL)
     271        29657 :             scanstate->ss.ps.ExecProcNode = ExecSeqScan;
     272              :         else
     273        35023 :             scanstate->ss.ps.ExecProcNode = ExecSeqScanWithProject;
     274              :     }
     275              :     else
     276              :     {
     277        61837 :         if (scanstate->ss.ps.ps_ProjInfo == NULL)
     278        30014 :             scanstate->ss.ps.ExecProcNode = ExecSeqScanWithQual;
     279              :         else
     280        31823 :             scanstate->ss.ps.ExecProcNode = ExecSeqScanWithQualProject;
     281              :     }
     282              : 
     283       126663 :     return scanstate;
     284              : }
     285              : 
     286              : /* ----------------------------------------------------------------
     287              :  *      ExecEndSeqScan
     288              :  *
     289              :  *      frees any storage allocated through C routines.
     290              :  * ----------------------------------------------------------------
     291              :  */
     292              : void
     293       125262 : ExecEndSeqScan(SeqScanState *node)
     294              : {
     295              :     TableScanDesc scanDesc;
     296              : 
     297              :     /*
     298              :      * get information from node
     299              :      */
     300       125262 :     scanDesc = node->ss.ss_currentScanDesc;
     301              : 
     302              :     /*
     303              :      * close heap scan
     304              :      */
     305       125262 :     if (scanDesc != NULL)
     306       102322 :         table_endscan(scanDesc);
     307       125262 : }
     308              : 
     309              : /* ----------------------------------------------------------------
     310              :  *                      Join Support
     311              :  * ----------------------------------------------------------------
     312              :  */
     313              : 
     314              : /* ----------------------------------------------------------------
     315              :  *      ExecReScanSeqScan
     316              :  *
     317              :  *      Rescans the relation.
     318              :  * ----------------------------------------------------------------
     319              :  */
     320              : void
     321       837496 : ExecReScanSeqScan(SeqScanState *node)
     322              : {
     323              :     TableScanDesc scan;
     324              : 
     325       837496 :     scan = node->ss.ss_currentScanDesc;
     326              : 
     327       837496 :     if (scan != NULL)
     328       824250 :         table_rescan(scan,      /* scan desc */
     329              :                      NULL);     /* new scan keys */
     330              : 
     331       837496 :     ExecScanReScan((ScanState *) node);
     332       837496 : }
     333              : 
     334              : /* ----------------------------------------------------------------
     335              :  *                      Parallel Scan Support
     336              :  * ----------------------------------------------------------------
     337              :  */
     338              : 
     339              : /* ----------------------------------------------------------------
     340              :  *      ExecSeqScanEstimate
     341              :  *
     342              :  *      Compute the amount of space we'll need in the parallel
     343              :  *      query DSM, and inform pcxt->estimator about our needs.
     344              :  * ----------------------------------------------------------------
     345              :  */
     346              : void
     347          468 : ExecSeqScanEstimate(SeqScanState *node,
     348              :                     ParallelContext *pcxt)
     349              : {
     350          468 :     EState     *estate = node->ss.ps.state;
     351              : 
     352          468 :     node->pscan_len = table_parallelscan_estimate(node->ss.ss_currentRelation,
     353              :                                                   estate->es_snapshot);
     354          468 :     shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
     355          468 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     356          468 : }
     357              : 
     358              : /* ----------------------------------------------------------------
     359              :  *      ExecSeqScanInitializeDSM
     360              :  *
     361              :  *      Set up a parallel heap scan descriptor.
     362              :  * ----------------------------------------------------------------
     363              :  */
     364              : void
     365          468 : ExecSeqScanInitializeDSM(SeqScanState *node,
     366              :                          ParallelContext *pcxt)
     367              : {
     368          468 :     EState     *estate = node->ss.ps.state;
     369              :     ParallelTableScanDesc pscan;
     370              : 
     371          468 :     pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
     372          468 :     table_parallelscan_initialize(node->ss.ss_currentRelation,
     373              :                                   pscan,
     374              :                                   estate->es_snapshot);
     375          468 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
     376          468 :     node->ss.ss_currentScanDesc =
     377          468 :         table_beginscan_parallel(node->ss.ss_currentRelation, pscan);
     378          468 : }
     379              : 
     380              : /* ----------------------------------------------------------------
     381              :  *      ExecSeqScanReInitializeDSM
     382              :  *
     383              :  *      Reset shared state before beginning a fresh scan.
     384              :  * ----------------------------------------------------------------
     385              :  */
     386              : void
     387          114 : ExecSeqScanReInitializeDSM(SeqScanState *node,
     388              :                            ParallelContext *pcxt)
     389              : {
     390              :     ParallelTableScanDesc pscan;
     391              : 
     392          114 :     pscan = node->ss.ss_currentScanDesc->rs_parallel;
     393          114 :     table_parallelscan_reinitialize(node->ss.ss_currentRelation, pscan);
     394          114 : }
     395              : 
     396              : /* ----------------------------------------------------------------
     397              :  *      ExecSeqScanInitializeWorker
     398              :  *
     399              :  *      Copy relevant information from TOC into planstate.
     400              :  * ----------------------------------------------------------------
     401              :  */
     402              : void
     403         1379 : ExecSeqScanInitializeWorker(SeqScanState *node,
     404              :                             ParallelWorkerContext *pwcxt)
     405              : {
     406              :     ParallelTableScanDesc pscan;
     407              : 
     408         1379 :     pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
     409         1379 :     node->ss.ss_currentScanDesc =
     410         1379 :         table_beginscan_parallel(node->ss.ss_currentRelation, pscan);
     411         1379 : }
        

Generated by: LCOV version 2.0-1