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

Generated by: LCOV version 2.0-1