LCOV - code coverage report
Current view: top level - src/backend/executor - nodeMaterial.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.0 % 114 106
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 72.6 % 62 45

             Branch data     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                 :      278353 : ExecMaterial(PlanState *pstate)
      40                 :             : {
      41                 :      278353 :         MaterialState *node = castNode(MaterialState, pstate);
      42                 :      278353 :         EState     *estate;
      43                 :      278353 :         ScanDirection dir;
      44                 :      278353 :         bool            forward;
      45                 :      278353 :         Tuplestorestate *tuplestorestate;
      46                 :      278353 :         bool            eof_tuplestore;
      47                 :      278353 :         TupleTableSlot *slot;
      48                 :             : 
      49         [ +  - ]:      278353 :         CHECK_FOR_INTERRUPTS();
      50                 :             : 
      51                 :             :         /*
      52                 :             :          * get state info from node
      53                 :             :          */
      54                 :      278353 :         estate = node->ss.ps.state;
      55                 :      278353 :         dir = estate->es_direction;
      56                 :      278353 :         forward = ScanDirectionIsForward(dir);
      57                 :      278353 :         tuplestorestate = node->tuplestorestate;
      58                 :             : 
      59                 :             :         /*
      60                 :             :          * If first time through, and we need a tuplestore, initialize it.
      61                 :             :          */
      62   [ +  +  -  + ]:      278353 :         if (tuplestorestate == NULL && node->eflags != 0)
      63                 :             :         {
      64                 :         327 :                 tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
      65                 :         327 :                 tuplestore_set_eflags(tuplestorestate, node->eflags);
      66         [ +  + ]:         327 :                 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                 :          19 :                         int                     ptrno PG_USED_FOR_ASSERTS_ONLY;
      73                 :             : 
      74                 :          38 :                         ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
      75                 :          19 :                                                                                                   node->eflags);
      76         [ +  - ]:          19 :                         Assert(ptrno == 1);
      77                 :          19 :                 }
      78                 :         327 :                 node->tuplestorestate = tuplestorestate;
      79                 :         327 :         }
      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         [ -  + ]:      278353 :         eof_tuplestore = (tuplestorestate == NULL) ||
      86                 :      278353 :                 tuplestore_ateof(tuplestorestate);
      87                 :             : 
      88   [ +  +  +  + ]:      278353 :         if (!forward && eof_tuplestore)
      89                 :             :         {
      90         [ +  - ]:           2 :                 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                 :           0 :                 }
     101                 :           2 :                 eof_tuplestore = false;
     102                 :           2 :         }
     103                 :             : 
     104                 :             :         /*
     105                 :             :          * If we can fetch another tuple from the tuplestore, return it.
     106                 :             :          */
     107                 :      278353 :         slot = node->ss.ps.ps_ResultTupleSlot;
     108         [ +  + ]:      278353 :         if (!eof_tuplestore)
     109                 :             :         {
     110         [ +  + ]:      275670 :                 if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))
     111                 :      265717 :                         return slot;
     112         [ +  + ]:        9953 :                 if (forward)
     113                 :        9951 :                         eof_tuplestore = true;
     114                 :        9953 :         }
     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   [ +  +  +  + ]:       12636 :         if (eof_tuplestore && !node->eof_underlying)
     125                 :             :         {
     126                 :        4014 :                 PlanState  *outerNode;
     127                 :        4014 :                 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                 :        4014 :                 outerNode = outerPlanState(node);
     134                 :        4014 :                 outerslot = ExecProcNode(outerNode);
     135   [ +  +  +  + ]:        4014 :                 if (TupIsNull(outerslot))
     136                 :             :                 {
     137                 :         310 :                         node->eof_underlying = true;
     138                 :         310 :                         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         [ -  + ]:        3704 :                 if (tuplestorestate)
     147                 :        3704 :                         tuplestore_puttupleslot(tuplestorestate, outerslot);
     148                 :             : 
     149                 :        3704 :                 ExecCopySlot(slot, outerslot);
     150                 :        3704 :                 return slot;
     151                 :        4014 :         }
     152                 :             : 
     153                 :             :         /*
     154                 :             :          * Nothing left ...
     155                 :             :          */
     156                 :        8622 :         return ExecClearTuple(slot);
     157                 :      278353 : }
     158                 :             : 
     159                 :             : /* ----------------------------------------------------------------
     160                 :             :  *              ExecInitMaterial
     161                 :             :  * ----------------------------------------------------------------
     162                 :             :  */
     163                 :             : MaterialState *
     164                 :         532 : ExecInitMaterial(Material *node, EState *estate, int eflags)
     165                 :             : {
     166                 :         532 :         MaterialState *matstate;
     167                 :         532 :         Plan       *outerPlan;
     168                 :             : 
     169                 :             :         /*
     170                 :             :          * create state structure
     171                 :             :          */
     172                 :         532 :         matstate = makeNode(MaterialState);
     173                 :         532 :         matstate->ss.ps.plan = (Plan *) node;
     174                 :         532 :         matstate->ss.ps.state = estate;
     175                 :         532 :         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                 :         532 :         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         [ +  + ]:         532 :         if (eflags & EXEC_FLAG_BACKWARD)
     195                 :           3 :                 matstate->eflags |= EXEC_FLAG_REWIND;
     196                 :             : 
     197                 :         532 :         matstate->eof_underlying = false;
     198                 :         532 :         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                 :         532 :         eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
     214                 :             : 
     215                 :         532 :         outerPlan = outerPlan(node);
     216                 :         532 :         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                 :         532 :         ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple);
     225                 :         532 :         matstate->ss.ps.ps_ProjInfo = NULL;
     226                 :             : 
     227                 :             :         /*
     228                 :             :          * initialize tuple type.
     229                 :             :          */
     230                 :         532 :         ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple);
     231                 :             : 
     232                 :        1064 :         return matstate;
     233                 :         532 : }
     234                 :             : 
     235                 :             : /* ----------------------------------------------------------------
     236                 :             :  *              ExecEndMaterial
     237                 :             :  * ----------------------------------------------------------------
     238                 :             :  */
     239                 :             : void
     240                 :         524 : ExecEndMaterial(MaterialState *node)
     241                 :             : {
     242                 :             :         /*
     243                 :             :          * Release tuplestore resources
     244                 :             :          */
     245         [ +  + ]:         524 :         if (node->tuplestorestate != NULL)
     246                 :         317 :                 tuplestore_end(node->tuplestorestate);
     247                 :         524 :         node->tuplestorestate = NULL;
     248                 :             : 
     249                 :             :         /*
     250                 :             :          * shut down the subplan
     251                 :             :          */
     252                 :         524 :         ExecEndNode(outerPlanState(node));
     253                 :         524 : }
     254                 :             : 
     255                 :             : /* ----------------------------------------------------------------
     256                 :             :  *              ExecMaterialMarkPos
     257                 :             :  *
     258                 :             :  *              Calls tuplestore to save the current position in the stored file.
     259                 :             :  * ----------------------------------------------------------------
     260                 :             :  */
     261                 :             : void
     262                 :        1062 : ExecMaterialMarkPos(MaterialState *node)
     263                 :             : {
     264         [ +  - ]:        1062 :         Assert(node->eflags & EXEC_FLAG_MARK);
     265                 :             : 
     266                 :             :         /*
     267                 :             :          * if we haven't materialized yet, just return.
     268                 :             :          */
     269         [ +  + ]:        1062 :         if (!node->tuplestorestate)
     270                 :           2 :                 return;
     271                 :             : 
     272                 :             :         /*
     273                 :             :          * copy the active read pointer to the mark.
     274                 :             :          */
     275                 :        1060 :         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                 :        1060 :         tuplestore_trim(node->tuplestorestate);
     281                 :        1062 : }
     282                 :             : 
     283                 :             : /* ----------------------------------------------------------------
     284                 :             :  *              ExecMaterialRestrPos
     285                 :             :  *
     286                 :             :  *              Calls tuplestore to restore the last saved file position.
     287                 :             :  * ----------------------------------------------------------------
     288                 :             :  */
     289                 :             : void
     290                 :        9004 : ExecMaterialRestrPos(MaterialState *node)
     291                 :             : {
     292         [ +  - ]:        9004 :         Assert(node->eflags & EXEC_FLAG_MARK);
     293                 :             : 
     294                 :             :         /*
     295                 :             :          * if we haven't materialized yet, just return.
     296                 :             :          */
     297         [ +  - ]:        9004 :         if (!node->tuplestorestate)
     298                 :           0 :                 return;
     299                 :             : 
     300                 :             :         /*
     301                 :             :          * copy the mark to the active read pointer.
     302                 :             :          */
     303                 :        9004 :         tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0);
     304                 :        9004 : }
     305                 :             : 
     306                 :             : /* ----------------------------------------------------------------
     307                 :             :  *              ExecReScanMaterial
     308                 :             :  *
     309                 :             :  *              Rescans the materialized relation.
     310                 :             :  * ----------------------------------------------------------------
     311                 :             :  */
     312                 :             : void
     313                 :        8993 : ExecReScanMaterial(MaterialState *node)
     314                 :             : {
     315                 :        8993 :         PlanState  *outerPlan = outerPlanState(node);
     316                 :             : 
     317                 :        8993 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     318                 :             : 
     319         [ +  - ]:        8993 :         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         [ +  + ]:        8993 :                 if (!node->tuplestorestate)
     327                 :         305 :                         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   [ +  +  -  + ]:        8688 :                 if (outerPlan->chgParam != NULL ||
     340                 :        8686 :                         (node->eflags & EXEC_FLAG_REWIND) == 0)
     341                 :             :                 {
     342                 :           2 :                         tuplestore_end(node->tuplestorestate);
     343                 :           2 :                         node->tuplestorestate = NULL;
     344         [ +  - ]:           2 :                         if (outerPlan->chgParam == NULL)
     345                 :           0 :                                 ExecReScan(outerPlan);
     346                 :           2 :                         node->eof_underlying = false;
     347                 :           2 :                 }
     348                 :             :                 else
     349                 :        8686 :                         tuplestore_rescan(node->tuplestorestate);
     350                 :        8688 :         }
     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         [ #  # ]:           0 :                 if (outerPlan->chgParam == NULL)
     360                 :           0 :                         ExecReScan(outerPlan);
     361                 :           0 :                 node->eof_underlying = false;
     362                 :             :         }
     363         [ -  + ]:        8993 : }
        

Generated by: LCOV version 2.3.2-1