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

Generated by: LCOV version 2.0-1