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

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * execAmi.c
       4              :  *    miscellaneous executor access method routines
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *  src/backend/executor/execAmi.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/amapi.h"
      16              : #include "access/htup_details.h"
      17              : #include "catalog/pg_class.h"
      18              : #include "executor/executor.h"
      19              : #include "executor/nodeAgg.h"
      20              : #include "executor/nodeAppend.h"
      21              : #include "executor/nodeBitmapAnd.h"
      22              : #include "executor/nodeBitmapHeapscan.h"
      23              : #include "executor/nodeBitmapIndexscan.h"
      24              : #include "executor/nodeBitmapOr.h"
      25              : #include "executor/nodeCtescan.h"
      26              : #include "executor/nodeCustom.h"
      27              : #include "executor/nodeForeignscan.h"
      28              : #include "executor/nodeFunctionscan.h"
      29              : #include "executor/nodeGather.h"
      30              : #include "executor/nodeGatherMerge.h"
      31              : #include "executor/nodeGroup.h"
      32              : #include "executor/nodeHash.h"
      33              : #include "executor/nodeHashjoin.h"
      34              : #include "executor/nodeIncrementalSort.h"
      35              : #include "executor/nodeIndexonlyscan.h"
      36              : #include "executor/nodeIndexscan.h"
      37              : #include "executor/nodeLimit.h"
      38              : #include "executor/nodeLockRows.h"
      39              : #include "executor/nodeMaterial.h"
      40              : #include "executor/nodeMemoize.h"
      41              : #include "executor/nodeMergeAppend.h"
      42              : #include "executor/nodeMergejoin.h"
      43              : #include "executor/nodeModifyTable.h"
      44              : #include "executor/nodeNamedtuplestorescan.h"
      45              : #include "executor/nodeNestloop.h"
      46              : #include "executor/nodeProjectSet.h"
      47              : #include "executor/nodeRecursiveunion.h"
      48              : #include "executor/nodeResult.h"
      49              : #include "executor/nodeSamplescan.h"
      50              : #include "executor/nodeSeqscan.h"
      51              : #include "executor/nodeSetOp.h"
      52              : #include "executor/nodeSort.h"
      53              : #include "executor/nodeSubplan.h"
      54              : #include "executor/nodeSubqueryscan.h"
      55              : #include "executor/nodeTableFuncscan.h"
      56              : #include "executor/nodeTidrangescan.h"
      57              : #include "executor/nodeTidscan.h"
      58              : #include "executor/nodeUnique.h"
      59              : #include "executor/nodeValuesscan.h"
      60              : #include "executor/nodeWindowAgg.h"
      61              : #include "executor/nodeWorktablescan.h"
      62              : #include "nodes/extensible.h"
      63              : #include "nodes/pathnodes.h"
      64              : #include "utils/syscache.h"
      65              : 
      66              : static bool IndexSupportsBackwardScan(Oid indexid);
      67              : 
      68              : 
      69              : /*
      70              :  * ExecReScan
      71              :  *      Reset a plan node so that its output can be re-scanned.
      72              :  *
      73              :  * Note that if the plan node has parameters that have changed value,
      74              :  * the output might be different from last time.
      75              :  */
      76              : void
      77      1961962 : ExecReScan(PlanState *node)
      78              : {
      79              :     /* If collecting timing stats, update them */
      80      1961962 :     if (node->instrument)
      81        21458 :         InstrEndLoop(node->instrument);
      82              : 
      83              :     /*
      84              :      * If we have changed parameters, propagate that info.
      85              :      *
      86              :      * Note: ExecReScanSetParamPlan() can add bits to node->chgParam,
      87              :      * corresponding to the output param(s) that the InitPlan will update.
      88              :      * Since we make only one pass over the list, that means that an InitPlan
      89              :      * can depend on the output param(s) of a sibling InitPlan only if that
      90              :      * sibling appears earlier in the list.  This is workable for now given
      91              :      * the limited ways in which one InitPlan could depend on another, but
      92              :      * eventually we might need to work harder (or else make the planner
      93              :      * enlarge the extParam/allParam sets to include the params of depended-on
      94              :      * InitPlans).
      95              :      */
      96      1961962 :     if (node->chgParam != NULL)
      97              :     {
      98              :         ListCell   *l;
      99              : 
     100      1800232 :         foreach(l, node->initPlan)
     101              :         {
     102          884 :             SubPlanState *sstate = (SubPlanState *) lfirst(l);
     103          884 :             PlanState  *splan = sstate->planstate;
     104              : 
     105          884 :             if (splan->plan->extParam != NULL)    /* don't care about child
     106              :                                                  * local Params */
     107          811 :                 UpdateChangedParamSet(splan, node->chgParam);
     108          884 :             if (splan->chgParam != NULL)
     109          691 :                 ExecReScanSetParamPlan(sstate, node);
     110              :         }
     111      1799778 :         foreach(l, node->subPlan)
     112              :         {
     113          430 :             SubPlanState *sstate = (SubPlanState *) lfirst(l);
     114          430 :             PlanState  *splan = sstate->planstate;
     115              : 
     116          430 :             if (splan->plan->extParam != NULL)
     117          427 :                 UpdateChangedParamSet(splan, node->chgParam);
     118              :         }
     119              :         /* Well. Now set chgParam for child trees. */
     120      1799348 :         if (outerPlanState(node) != NULL)
     121       501749 :             UpdateChangedParamSet(outerPlanState(node), node->chgParam);
     122      1799348 :         if (innerPlanState(node) != NULL)
     123         9126 :             UpdateChangedParamSet(innerPlanState(node), node->chgParam);
     124              :     }
     125              : 
     126              :     /* Call expression callbacks */
     127      1961962 :     if (node->ps_ExprContext)
     128      1836293 :         ReScanExprContext(node->ps_ExprContext);
     129              : 
     130              :     /* And do node-type-specific processing */
     131      1961962 :     switch (nodeTag(node))
     132              :     {
     133        32781 :         case T_ResultState:
     134        32781 :             ExecReScanResult((ResultState *) node);
     135        32781 :             break;
     136              : 
     137        13539 :         case T_ProjectSetState:
     138        13539 :             ExecReScanProjectSet((ProjectSetState *) node);
     139        13539 :             break;
     140              : 
     141            0 :         case T_ModifyTableState:
     142            0 :             ExecReScanModifyTable((ModifyTableState *) node);
     143            0 :             break;
     144              : 
     145        15651 :         case T_AppendState:
     146        15651 :             ExecReScanAppend((AppendState *) node);
     147        15651 :             break;
     148              : 
     149            9 :         case T_MergeAppendState:
     150            9 :             ExecReScanMergeAppend((MergeAppendState *) node);
     151            9 :             break;
     152              : 
     153            6 :         case T_RecursiveUnionState:
     154            6 :             ExecReScanRecursiveUnion((RecursiveUnionState *) node);
     155            6 :             break;
     156              : 
     157           79 :         case T_BitmapAndState:
     158           79 :             ExecReScanBitmapAnd((BitmapAndState *) node);
     159           79 :             break;
     160              : 
     161           20 :         case T_BitmapOrState:
     162           20 :             ExecReScanBitmapOr((BitmapOrState *) node);
     163           20 :             break;
     164              : 
     165       834216 :         case T_SeqScanState:
     166       834216 :             ExecReScanSeqScan((SeqScanState *) node);
     167       834216 :             break;
     168              : 
     169           29 :         case T_SampleScanState:
     170           29 :             ExecReScanSampleScan((SampleScanState *) node);
     171           29 :             break;
     172              : 
     173          150 :         case T_GatherState:
     174          150 :             ExecReScanGather((GatherState *) node);
     175          150 :             break;
     176              : 
     177           24 :         case T_GatherMergeState:
     178           24 :             ExecReScanGatherMerge((GatherMergeState *) node);
     179           24 :             break;
     180              : 
     181       271300 :         case T_IndexScanState:
     182       271300 :             ExecReScanIndexScan((IndexScanState *) node);
     183       271300 :             break;
     184              : 
     185       111839 :         case T_IndexOnlyScanState:
     186       111839 :             ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
     187       111839 :             break;
     188              : 
     189         2911 :         case T_BitmapIndexScanState:
     190         2911 :             ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
     191         2911 :             break;
     192              : 
     193         2587 :         case T_BitmapHeapScanState:
     194         2587 :             ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
     195         2587 :             break;
     196              : 
     197            9 :         case T_TidScanState:
     198            9 :             ExecReScanTidScan((TidScanState *) node);
     199            9 :             break;
     200              : 
     201           48 :         case T_TidRangeScanState:
     202           48 :             ExecReScanTidRangeScan((TidRangeScanState *) node);
     203           48 :             break;
     204              : 
     205          238 :         case T_SubqueryScanState:
     206          238 :             ExecReScanSubqueryScan((SubqueryScanState *) node);
     207          238 :             break;
     208              : 
     209        69854 :         case T_FunctionScanState:
     210        69854 :             ExecReScanFunctionScan((FunctionScanState *) node);
     211        69854 :             break;
     212              : 
     213          222 :         case T_TableFuncScanState:
     214          222 :             ExecReScanTableFuncScan((TableFuncScanState *) node);
     215          222 :             break;
     216              : 
     217        30190 :         case T_ValuesScanState:
     218        30190 :             ExecReScanValuesScan((ValuesScanState *) node);
     219        30190 :             break;
     220              : 
     221         3211 :         case T_CteScanState:
     222         3211 :             ExecReScanCteScan((CteScanState *) node);
     223         3211 :             break;
     224              : 
     225            0 :         case T_NamedTuplestoreScanState:
     226            0 :             ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
     227            0 :             break;
     228              : 
     229         3289 :         case T_WorkTableScanState:
     230         3289 :             ExecReScanWorkTableScan((WorkTableScanState *) node);
     231         3289 :             break;
     232              : 
     233          410 :         case T_ForeignScanState:
     234          410 :             ExecReScanForeignScan((ForeignScanState *) node);
     235          410 :             break;
     236              : 
     237            0 :         case T_CustomScanState:
     238            0 :             ExecReScanCustomScan((CustomScanState *) node);
     239            0 :             break;
     240              : 
     241         6903 :         case T_NestLoopState:
     242         6903 :             ExecReScanNestLoop((NestLoopState *) node);
     243         6903 :             break;
     244              : 
     245          382 :         case T_MergeJoinState:
     246          382 :             ExecReScanMergeJoin((MergeJoinState *) node);
     247          382 :             break;
     248              : 
     249         2042 :         case T_HashJoinState:
     250         2042 :             ExecReScanHashJoin((HashJoinState *) node);
     251         2042 :             break;
     252              : 
     253        80000 :         case T_MaterialState:
     254        80000 :             ExecReScanMaterial((MaterialState *) node);
     255        80000 :             break;
     256              : 
     257       393454 :         case T_MemoizeState:
     258       393454 :             ExecReScanMemoize((MemoizeState *) node);
     259       393454 :             break;
     260              : 
     261        26979 :         case T_SortState:
     262        26979 :             ExecReScanSort((SortState *) node);
     263        26979 :             break;
     264              : 
     265            6 :         case T_IncrementalSortState:
     266            6 :             ExecReScanIncrementalSort((IncrementalSortState *) node);
     267            6 :             break;
     268              : 
     269           12 :         case T_GroupState:
     270           12 :             ExecReScanGroup((GroupState *) node);
     271           12 :             break;
     272              : 
     273        27455 :         case T_AggState:
     274        27455 :             ExecReScanAgg((AggState *) node);
     275        27455 :             break;
     276              : 
     277           39 :         case T_WindowAggState:
     278           39 :             ExecReScanWindowAgg((WindowAggState *) node);
     279           39 :             break;
     280              : 
     281           18 :         case T_UniqueState:
     282           18 :             ExecReScanUnique((UniqueState *) node);
     283           18 :             break;
     284              : 
     285          944 :         case T_HashState:
     286          944 :             ExecReScanHash((HashState *) node);
     287          944 :             break;
     288              : 
     289          600 :         case T_SetOpState:
     290          600 :             ExecReScanSetOp((SetOpState *) node);
     291          600 :             break;
     292              : 
     293            8 :         case T_LockRowsState:
     294            8 :             ExecReScanLockRows((LockRowsState *) node);
     295            8 :             break;
     296              : 
     297        30508 :         case T_LimitState:
     298        30508 :             ExecReScanLimit((LimitState *) node);
     299        30508 :             break;
     300              : 
     301            0 :         default:
     302            0 :             elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
     303              :             break;
     304              :     }
     305              : 
     306      1961962 :     if (node->chgParam != NULL)
     307              :     {
     308      1799348 :         bms_free(node->chgParam);
     309      1799348 :         node->chgParam = NULL;
     310              :     }
     311      1961962 : }
     312              : 
     313              : /*
     314              :  * ExecMarkPos
     315              :  *
     316              :  * Marks the current scan position.
     317              :  *
     318              :  * NOTE: mark/restore capability is currently needed only for plan nodes
     319              :  * that are the immediate inner child of a MergeJoin node.  Since MergeJoin
     320              :  * requires sorted input, there is never any need to support mark/restore in
     321              :  * node types that cannot produce sorted output.  There are some cases in
     322              :  * which a node can pass through sorted data from its child; if we don't
     323              :  * implement mark/restore for such a node type, the planner compensates by
     324              :  * inserting a Material node above that node.
     325              :  */
     326              : void
     327       362689 : ExecMarkPos(PlanState *node)
     328              : {
     329       362689 :     switch (nodeTag(node))
     330              :     {
     331         3056 :         case T_IndexScanState:
     332         3056 :             ExecIndexMarkPos((IndexScanState *) node);
     333         3056 :             break;
     334              : 
     335        62019 :         case T_IndexOnlyScanState:
     336        62019 :             ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
     337        62019 :             break;
     338              : 
     339            0 :         case T_CustomScanState:
     340            0 :             ExecCustomMarkPos((CustomScanState *) node);
     341            0 :             break;
     342              : 
     343         3270 :         case T_MaterialState:
     344         3270 :             ExecMaterialMarkPos((MaterialState *) node);
     345         3270 :             break;
     346              : 
     347       294344 :         case T_SortState:
     348       294344 :             ExecSortMarkPos((SortState *) node);
     349       294344 :             break;
     350              : 
     351            0 :         case T_ResultState:
     352            0 :             ExecResultMarkPos((ResultState *) node);
     353            0 :             break;
     354              : 
     355            0 :         default:
     356              :             /* don't make hard error unless caller asks to restore... */
     357            0 :             elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
     358            0 :             break;
     359              :     }
     360       362689 : }
     361              : 
     362              : /*
     363              :  * ExecRestrPos
     364              :  *
     365              :  * restores the scan position previously saved with ExecMarkPos()
     366              :  *
     367              :  * NOTE: the semantics of this are that the first ExecProcNode following
     368              :  * the restore operation will yield the same tuple as the first one following
     369              :  * the mark operation.  It is unspecified what happens to the plan node's
     370              :  * result TupleTableSlot.  (In most cases the result slot is unchanged by
     371              :  * a restore, but the node may choose to clear it or to load it with the
     372              :  * restored-to tuple.)  Hence the caller should discard any previously
     373              :  * returned TupleTableSlot after doing a restore.
     374              :  */
     375              : void
     376        72878 : ExecRestrPos(PlanState *node)
     377              : {
     378        72878 :     switch (nodeTag(node))
     379              :     {
     380        27021 :         case T_IndexScanState:
     381        27021 :             ExecIndexRestrPos((IndexScanState *) node);
     382        27021 :             break;
     383              : 
     384            0 :         case T_IndexOnlyScanState:
     385            0 :             ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
     386            0 :             break;
     387              : 
     388            0 :         case T_CustomScanState:
     389            0 :             ExecCustomRestrPos((CustomScanState *) node);
     390            0 :             break;
     391              : 
     392        27016 :         case T_MaterialState:
     393        27016 :             ExecMaterialRestrPos((MaterialState *) node);
     394        27016 :             break;
     395              : 
     396        18841 :         case T_SortState:
     397        18841 :             ExecSortRestrPos((SortState *) node);
     398        18841 :             break;
     399              : 
     400            0 :         case T_ResultState:
     401            0 :             ExecResultRestrPos((ResultState *) node);
     402            0 :             break;
     403              : 
     404            0 :         default:
     405            0 :             elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
     406              :             break;
     407              :     }
     408        72878 : }
     409              : 
     410              : /*
     411              :  * ExecSupportsMarkRestore - does a Path support mark/restore?
     412              :  *
     413              :  * This is used during planning and so must accept a Path, not a Plan.
     414              :  * We keep it here to be adjacent to the routines above, which also must
     415              :  * know which plan types support mark/restore.
     416              :  */
     417              : bool
     418         5045 : ExecSupportsMarkRestore(Path *pathnode)
     419              : {
     420              :     /*
     421              :      * For consistency with the routines above, we do not examine the nodeTag
     422              :      * but rather the pathtype, which is the Plan node type the Path would
     423              :      * produce.
     424              :      */
     425         5045 :     switch (pathnode->pathtype)
     426              :     {
     427         4091 :         case T_IndexScan:
     428              :         case T_IndexOnlyScan:
     429              : 
     430              :             /*
     431              :              * Not all index types support mark/restore.
     432              :              */
     433         4091 :             return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
     434              : 
     435            0 :         case T_Material:
     436              :         case T_Sort:
     437            0 :             return true;
     438              : 
     439            0 :         case T_CustomScan:
     440            0 :             if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
     441            0 :                 return true;
     442            0 :             return false;
     443              : 
     444            0 :         case T_Result:
     445              : 
     446              :             /*
     447              :              * Result supports mark/restore iff it has a child plan that does.
     448              :              *
     449              :              * We have to be careful here because there is more than one Path
     450              :              * type that can produce a Result plan node.
     451              :              */
     452            0 :             if (IsA(pathnode, ProjectionPath))
     453            0 :                 return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath);
     454            0 :             else if (IsA(pathnode, MinMaxAggPath))
     455            0 :                 return false;   /* childless Result */
     456            0 :             else if (IsA(pathnode, GroupResultPath))
     457            0 :                 return false;   /* childless Result */
     458              :             else
     459              :             {
     460              :                 /* Simple RTE_RESULT base relation */
     461              :                 Assert(IsA(pathnode, Path));
     462            0 :                 return false;   /* childless Result */
     463              :             }
     464              : 
     465           42 :         case T_Append:
     466              :             {
     467           42 :                 AppendPath *appendPath = castNode(AppendPath, pathnode);
     468              : 
     469              :                 /*
     470              :                  * If there's exactly one child, then there will be no Append
     471              :                  * in the final plan, so we can handle mark/restore if the
     472              :                  * child plan node can.
     473              :                  */
     474           42 :                 if (list_length(appendPath->subpaths) == 1)
     475            0 :                     return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
     476              :                 /* Otherwise, Append can't handle it */
     477           42 :                 return false;
     478              :             }
     479              : 
     480           22 :         case T_MergeAppend:
     481              :             {
     482           22 :                 MergeAppendPath *mapath = castNode(MergeAppendPath, pathnode);
     483              : 
     484              :                 /*
     485              :                  * Like the Append case above, single-subpath MergeAppends
     486              :                  * won't be in the final plan, so just return the child's
     487              :                  * mark/restore ability.
     488              :                  */
     489           22 :                 if (list_length(mapath->subpaths) == 1)
     490            0 :                     return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
     491              :                 /* Otherwise, MergeAppend can't handle it */
     492           22 :                 return false;
     493              :             }
     494              : 
     495          890 :         default:
     496          890 :             break;
     497              :     }
     498              : 
     499          890 :     return false;
     500              : }
     501              : 
     502              : /*
     503              :  * ExecSupportsBackwardScan - does a plan type support backwards scanning?
     504              :  *
     505              :  * Ideally, all plan types would support backwards scan, but that seems
     506              :  * unlikely to happen soon.  In some cases, a plan node passes the backwards
     507              :  * scan down to its children, and so supports backwards scan only if its
     508              :  * children do.  Therefore, this routine must be passed a complete plan tree.
     509              :  */
     510              : bool
     511         2518 : ExecSupportsBackwardScan(Plan *node)
     512              : {
     513         2518 :     if (node == NULL)
     514            0 :         return false;
     515              : 
     516              :     /*
     517              :      * Parallel-aware nodes return a subset of the tuples in each worker, and
     518              :      * in general we can't expect to have enough bookkeeping state to know
     519              :      * which ones we returned in this worker as opposed to some other worker.
     520              :      */
     521         2518 :     if (node->parallel_aware)
     522            0 :         return false;
     523              : 
     524         2518 :     switch (nodeTag(node))
     525              :     {
     526           36 :         case T_Result:
     527           36 :             if (outerPlan(node) != NULL)
     528            0 :                 return ExecSupportsBackwardScan(outerPlan(node));
     529              :             else
     530           36 :                 return false;
     531              : 
     532           20 :         case T_Append:
     533              :             {
     534              :                 ListCell   *l;
     535              : 
     536              :                 /* With async, tuples may be interleaved, so can't back up. */
     537           20 :                 if (((Append *) node)->nasyncplans > 0)
     538            0 :                     return false;
     539              : 
     540           68 :                 foreach(l, ((Append *) node)->appendplans)
     541              :                 {
     542           49 :                     if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
     543            1 :                         return false;
     544              :                 }
     545              :                 /* need not check tlist because Append doesn't evaluate it */
     546           19 :                 return true;
     547              :             }
     548              : 
     549            3 :         case T_SampleScan:
     550              :             /* Simplify life for tablesample methods by disallowing this */
     551            3 :             return false;
     552              : 
     553            0 :         case T_Gather:
     554            0 :             return false;
     555              : 
     556          203 :         case T_IndexScan:
     557          203 :             return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
     558              : 
     559           24 :         case T_IndexOnlyScan:
     560           24 :             return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
     561              : 
     562            0 :         case T_SubqueryScan:
     563            0 :             return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
     564              : 
     565            0 :         case T_CustomScan:
     566            0 :             if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
     567            0 :                 return true;
     568            0 :             return false;
     569              : 
     570         1789 :         case T_SeqScan:
     571              :         case T_TidScan:
     572              :         case T_TidRangeScan:
     573              :         case T_FunctionScan:
     574              :         case T_ValuesScan:
     575              :         case T_CteScan:
     576              :         case T_Material:
     577              :         case T_Sort:
     578              :             /* these don't evaluate tlist */
     579         1789 :             return true;
     580              : 
     581            3 :         case T_IncrementalSort:
     582              : 
     583              :             /*
     584              :              * Unlike full sort, incremental sort keeps only a single group of
     585              :              * tuples in memory, so it can't scan backwards.
     586              :              */
     587            3 :             return false;
     588              : 
     589           76 :         case T_LockRows:
     590              :         case T_Limit:
     591           76 :             return ExecSupportsBackwardScan(outerPlan(node));
     592              : 
     593          364 :         default:
     594          364 :             return false;
     595              :     }
     596              : }
     597              : 
     598              : /*
     599              :  * An IndexScan or IndexOnlyScan node supports backward scan only if the
     600              :  * index's AM does.
     601              :  */
     602              : static bool
     603          227 : IndexSupportsBackwardScan(Oid indexid)
     604              : {
     605              :     bool        result;
     606              :     HeapTuple   ht_idxrel;
     607              :     Form_pg_class idxrelrec;
     608              :     const IndexAmRoutine *amroutine;
     609              : 
     610              :     /* Fetch the pg_class tuple of the index relation */
     611          227 :     ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
     612          227 :     if (!HeapTupleIsValid(ht_idxrel))
     613            0 :         elog(ERROR, "cache lookup failed for relation %u", indexid);
     614          227 :     idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
     615              : 
     616              :     /* Fetch the index AM's API struct */
     617          227 :     amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
     618              : 
     619          227 :     result = amroutine->amcanbackward;
     620              : 
     621          227 :     ReleaseSysCache(ht_idxrel);
     622              : 
     623          227 :     return result;
     624              : }
     625              : 
     626              : /*
     627              :  * ExecMaterializesOutput - does a plan type materialize its output?
     628              :  *
     629              :  * Returns true if the plan node type is one that automatically materializes
     630              :  * its output (typically by keeping it in a tuplestore).  For such plans,
     631              :  * a rescan without any parameter change will have zero startup cost and
     632              :  * very low per-tuple cost.
     633              :  */
     634              : bool
     635       395035 : ExecMaterializesOutput(NodeTag plantype)
     636              : {
     637       395035 :     switch (plantype)
     638              :     {
     639        16602 :         case T_Material:
     640              :         case T_FunctionScan:
     641              :         case T_TableFuncScan:
     642              :         case T_CteScan:
     643              :         case T_NamedTuplestoreScan:
     644              :         case T_WorkTableScan:
     645              :         case T_Sort:
     646        16602 :             return true;
     647              : 
     648       378433 :         default:
     649       378433 :             break;
     650              :     }
     651              : 
     652       378433 :     return false;
     653              : }
        

Generated by: LCOV version 2.0-1