LCOV - code coverage report
Current view: top level - src/backend/executor - nodeMaterial.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.2 % 86 81
Test Date: 2026-03-21 04:16:06 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeMaterial.c
       4              :  *    Routines to handle materialization nodes.
       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/nodeMaterial.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : /*
      16              :  * INTERFACE ROUTINES
      17              :  *      ExecMaterial            - materialize the result of a subplan
      18              :  *      ExecInitMaterial        - initialize node and subnodes
      19              :  *      ExecEndMaterial         - shutdown node and subnodes
      20              :  *
      21              :  */
      22              : #include "postgres.h"
      23              : 
      24              : #include "executor/executor.h"
      25              : #include "executor/nodeMaterial.h"
      26              : #include "miscadmin.h"
      27              : #include "utils/tuplestore.h"
      28              : 
      29              : /* ----------------------------------------------------------------
      30              :  *      ExecMaterial
      31              :  *
      32              :  *      As long as we are at the end of the data collected in the tuplestore,
      33              :  *      we collect one new row from the subplan on each call, and stash it
      34              :  *      aside in the tuplestore before returning it.  The tuplestore is
      35              :  *      only read if we are asked to scan backwards, rescan, or mark/restore.
      36              :  *
      37              :  * ----------------------------------------------------------------
      38              :  */
      39              : static TupleTableSlot *         /* result tuple from subplan */
      40      2656384 : ExecMaterial(PlanState *pstate)
      41              : {
      42      2656384 :     MaterialState *node = castNode(MaterialState, pstate);
      43              :     EState     *estate;
      44              :     ScanDirection dir;
      45              :     bool        forward;
      46              :     Tuplestorestate *tuplestorestate;
      47              :     bool        eof_tuplestore;
      48              :     TupleTableSlot *slot;
      49              : 
      50      2656384 :     CHECK_FOR_INTERRUPTS();
      51              : 
      52              :     /*
      53              :      * get state info from node
      54              :      */
      55      2656384 :     estate = node->ss.ps.state;
      56      2656384 :     dir = estate->es_direction;
      57      2656384 :     forward = ScanDirectionIsForward(dir);
      58      2656384 :     tuplestorestate = node->tuplestorestate;
      59              : 
      60              :     /*
      61              :      * If first time through, and we need a tuplestore, initialize it.
      62              :      */
      63      2656384 :     if (tuplestorestate == NULL && node->eflags != 0)
      64              :     {
      65         1861 :         tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
      66         1861 :         tuplestore_set_eflags(tuplestorestate, node->eflags);
      67         1861 :         if (node->eflags & EXEC_FLAG_MARK)
      68              :         {
      69              :             /*
      70              :              * Allocate a second read pointer to serve as the mark. We know it
      71              :              * must have index 1, so needn't store that.
      72              :              */
      73              :             int         ptrno PG_USED_FOR_ASSERTS_ONLY;
      74              : 
      75           79 :             ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
      76              :                                                   node->eflags);
      77              :             Assert(ptrno == 1);
      78              :         }
      79         1861 :         node->tuplestorestate = tuplestorestate;
      80              :     }
      81              : 
      82              :     /*
      83              :      * If we are not at the end of the tuplestore, or are going backwards, try
      84              :      * to fetch a tuple from tuplestore.
      85              :      */
      86      5312703 :     eof_tuplestore = (tuplestorestate == NULL) ||
      87      2656319 :         tuplestore_ateof(tuplestorestate);
      88              : 
      89      2656384 :     if (!forward && eof_tuplestore)
      90              :     {
      91            8 :         if (!node->eof_underlying)
      92              :         {
      93              :             /*
      94              :              * When reversing direction at tuplestore EOF, the first
      95              :              * gettupleslot call will fetch the last-added tuple; but we want
      96              :              * to return the one before that, if possible. So do an extra
      97              :              * fetch.
      98              :              */
      99            0 :             if (!tuplestore_advance(tuplestorestate, forward))
     100            0 :                 return NULL;    /* the tuplestore must be empty */
     101              :         }
     102            8 :         eof_tuplestore = false;
     103              :     }
     104              : 
     105              :     /*
     106              :      * If we can fetch another tuple from the tuplestore, return it.
     107              :      */
     108      2656384 :     slot = node->ss.ps.ps_ResultTupleSlot;
     109      2656384 :     if (!eof_tuplestore)
     110              :     {
     111      2604292 :         if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))
     112      2518459 :             return slot;
     113        85833 :         if (forward)
     114        85825 :             eof_tuplestore = true;
     115              :     }
     116              : 
     117              :     /*
     118              :      * If necessary, try to fetch another row from the subplan.
     119              :      *
     120              :      * Note: the eof_underlying state variable exists to short-circuit further
     121              :      * subplan calls.  It's not optional, unfortunately, because some plan
     122              :      * node types are not robust about being called again when they've already
     123              :      * returned NULL.
     124              :      */
     125       137925 :     if (eof_tuplestore && !node->eof_underlying)
     126              :     {
     127              :         PlanState  *outerNode;
     128              :         TupleTableSlot *outerslot;
     129              : 
     130              :         /*
     131              :          * We can only get here with forward==true, so no need to worry about
     132              :          * which direction the subplan will go.
     133              :          */
     134        58051 :         outerNode = outerPlanState(node);
     135        58051 :         outerslot = ExecProcNode(outerNode);
     136        58051 :         if (TupIsNull(outerslot))
     137              :         {
     138         1702 :             node->eof_underlying = true;
     139         1702 :             return NULL;
     140              :         }
     141              : 
     142              :         /*
     143              :          * Append a copy of the returned tuple to tuplestore.  NOTE: because
     144              :          * the tuplestore is certainly in EOF state, its read position will
     145              :          * move forward over the added tuple.  This is what we want.
     146              :          */
     147        56349 :         if (tuplestorestate)
     148        56287 :             tuplestore_puttupleslot(tuplestorestate, outerslot);
     149              : 
     150        56349 :         ExecCopySlot(slot, outerslot);
     151        56349 :         return slot;
     152              :     }
     153              : 
     154              :     /*
     155              :      * Nothing left ...
     156              :      */
     157        79874 :     return ExecClearTuple(slot);
     158              : }
     159              : 
     160              : /* ----------------------------------------------------------------
     161              :  *      ExecInitMaterial
     162              :  * ----------------------------------------------------------------
     163              :  */
     164              : MaterialState *
     165         2787 : ExecInitMaterial(Material *node, EState *estate, int eflags)
     166              : {
     167              :     MaterialState *matstate;
     168              :     Plan       *outerPlan;
     169              : 
     170              :     /*
     171              :      * create state structure
     172              :      */
     173         2787 :     matstate = makeNode(MaterialState);
     174         2787 :     matstate->ss.ps.plan = (Plan *) node;
     175         2787 :     matstate->ss.ps.state = estate;
     176         2787 :     matstate->ss.ps.ExecProcNode = ExecMaterial;
     177              : 
     178              :     /*
     179              :      * We must have a tuplestore buffering the subplan output to do backward
     180              :      * scan or mark/restore.  We also prefer to materialize the subplan output
     181              :      * if we might be called on to rewind and replay it many times. However,
     182              :      * if none of these cases apply, we can skip storing the data.
     183              :      */
     184         2787 :     matstate->eflags = (eflags & (EXEC_FLAG_REWIND |
     185              :                                   EXEC_FLAG_BACKWARD |
     186              :                                   EXEC_FLAG_MARK));
     187              : 
     188              :     /*
     189              :      * Tuplestore's interpretation of the flag bits is subtly different from
     190              :      * the general executor meaning: it doesn't think BACKWARD necessarily
     191              :      * means "backwards all the way to start".  If told to support BACKWARD we
     192              :      * must include REWIND in the tuplestore eflags, else tuplestore_trim
     193              :      * might throw away too much.
     194              :      */
     195         2787 :     if (eflags & EXEC_FLAG_BACKWARD)
     196           13 :         matstate->eflags |= EXEC_FLAG_REWIND;
     197              : 
     198         2787 :     matstate->eof_underlying = false;
     199         2787 :     matstate->tuplestorestate = NULL;
     200              : 
     201              :     /*
     202              :      * Miscellaneous initialization
     203              :      *
     204              :      * Materialization nodes don't need ExprContexts because they never call
     205              :      * ExecQual or ExecProject.
     206              :      */
     207              : 
     208              :     /*
     209              :      * initialize child nodes
     210              :      *
     211              :      * We shield the child node from the need to support REWIND, BACKWARD, or
     212              :      * MARK/RESTORE.
     213              :      */
     214         2787 :     eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
     215              : 
     216         2787 :     outerPlan = outerPlan(node);
     217         2787 :     outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags);
     218              : 
     219              :     /*
     220              :      * Initialize result type and slot. No need to initialize projection info
     221              :      * because this node doesn't do projections.
     222              :      *
     223              :      * material nodes only return tuples from their materialized relation.
     224              :      */
     225         2787 :     ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple);
     226         2787 :     matstate->ss.ps.ps_ProjInfo = NULL;
     227              : 
     228              :     /*
     229              :      * initialize tuple type.
     230              :      */
     231         2787 :     ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple);
     232              : 
     233         2787 :     return matstate;
     234              : }
     235              : 
     236              : /* ----------------------------------------------------------------
     237              :  *      ExecEndMaterial
     238              :  * ----------------------------------------------------------------
     239              :  */
     240              : void
     241         2745 : ExecEndMaterial(MaterialState *node)
     242              : {
     243              :     /*
     244              :      * Release tuplestore resources
     245              :      */
     246         2745 :     if (node->tuplestorestate != NULL)
     247         1814 :         tuplestore_end(node->tuplestorestate);
     248         2745 :     node->tuplestorestate = NULL;
     249              : 
     250              :     /*
     251              :      * shut down the subplan
     252              :      */
     253         2745 :     ExecEndNode(outerPlanState(node));
     254         2745 : }
     255              : 
     256              : /* ----------------------------------------------------------------
     257              :  *      ExecMaterialMarkPos
     258              :  *
     259              :  *      Calls tuplestore to save the current position in the stored file.
     260              :  * ----------------------------------------------------------------
     261              :  */
     262              : void
     263         4332 : ExecMaterialMarkPos(MaterialState *node)
     264              : {
     265              :     Assert(node->eflags & EXEC_FLAG_MARK);
     266              : 
     267              :     /*
     268              :      * if we haven't materialized yet, just return.
     269              :      */
     270         4332 :     if (!node->tuplestorestate)
     271            8 :         return;
     272              : 
     273              :     /*
     274              :      * copy the active read pointer to the mark.
     275              :      */
     276         4324 :     tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1);
     277              : 
     278              :     /*
     279              :      * since we may have advanced the mark, try to truncate the tuplestore.
     280              :      */
     281         4324 :     tuplestore_trim(node->tuplestorestate);
     282              : }
     283              : 
     284              : /* ----------------------------------------------------------------
     285              :  *      ExecMaterialRestrPos
     286              :  *
     287              :  *      Calls tuplestore to restore the last saved file position.
     288              :  * ----------------------------------------------------------------
     289              :  */
     290              : void
     291        36020 : ExecMaterialRestrPos(MaterialState *node)
     292              : {
     293              :     Assert(node->eflags & EXEC_FLAG_MARK);
     294              : 
     295              :     /*
     296              :      * if we haven't materialized yet, just return.
     297              :      */
     298        36020 :     if (!node->tuplestorestate)
     299            0 :         return;
     300              : 
     301              :     /*
     302              :      * copy the mark to the active read pointer.
     303              :      */
     304        36020 :     tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0);
     305              : }
     306              : 
     307              : /* ----------------------------------------------------------------
     308              :  *      ExecReScanMaterial
     309              :  *
     310              :  *      Rescans the materialized relation.
     311              :  * ----------------------------------------------------------------
     312              :  */
     313              : void
     314        87277 : ExecReScanMaterial(MaterialState *node)
     315              : {
     316        87277 :     PlanState  *outerPlan = outerPlanState(node);
     317              : 
     318        87277 :     ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     319              : 
     320        87277 :     if (node->eflags != 0)
     321              :     {
     322              :         /*
     323              :          * If we haven't materialized yet, just return. If outerplan's
     324              :          * chgParam is not NULL then it will be re-scanned by ExecProcNode,
     325              :          * else no reason to re-scan it at all.
     326              :          */
     327        87275 :         if (!node->tuplestorestate)
     328         1769 :             return;
     329              : 
     330              :         /*
     331              :          * If subnode is to be rescanned then we forget previous stored
     332              :          * results; we have to re-read the subplan and re-store.  Also, if we
     333              :          * told tuplestore it needn't support rescan, we lose and must
     334              :          * re-read.  (This last should not happen in common cases; else our
     335              :          * caller lied by not passing EXEC_FLAG_REWIND to us.)
     336              :          *
     337              :          * Otherwise we can just rewind and rescan the stored output. The
     338              :          * state of the subnode does not change.
     339              :          */
     340        85506 :         if (outerPlan->chgParam != NULL ||
     341        85498 :             (node->eflags & EXEC_FLAG_REWIND) == 0)
     342              :         {
     343            8 :             tuplestore_end(node->tuplestorestate);
     344            8 :             node->tuplestorestate = NULL;
     345            8 :             if (outerPlan->chgParam == NULL)
     346            0 :                 ExecReScan(outerPlan);
     347            8 :             node->eof_underlying = false;
     348              :         }
     349              :         else
     350        85498 :             tuplestore_rescan(node->tuplestorestate);
     351              :     }
     352              :     else
     353              :     {
     354              :         /* In this case we are just passing on the subquery's output */
     355              : 
     356              :         /*
     357              :          * if chgParam of subnode is not null then plan will be re-scanned by
     358              :          * first ExecProcNode.
     359              :          */
     360            2 :         if (outerPlan->chgParam == NULL)
     361            0 :             ExecReScan(outerPlan);
     362            2 :         node->eof_underlying = false;
     363              :     }
     364              : }
        

Generated by: LCOV version 2.0-1