LCOV - code coverage report
Current view: top level - src/backend/executor - execAmi.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 229 289 79.2 %
Date: 2023-12-05 07:10:55 Functions: 7 7 100.0 %
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-2023, 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 "executor/execdebug.h"
      18             : #include "executor/nodeAgg.h"
      19             : #include "executor/nodeAppend.h"
      20             : #include "executor/nodeBitmapAnd.h"
      21             : #include "executor/nodeBitmapHeapscan.h"
      22             : #include "executor/nodeBitmapIndexscan.h"
      23             : #include "executor/nodeBitmapOr.h"
      24             : #include "executor/nodeCtescan.h"
      25             : #include "executor/nodeCustom.h"
      26             : #include "executor/nodeForeignscan.h"
      27             : #include "executor/nodeFunctionscan.h"
      28             : #include "executor/nodeGather.h"
      29             : #include "executor/nodeGatherMerge.h"
      30             : #include "executor/nodeGroup.h"
      31             : #include "executor/nodeHash.h"
      32             : #include "executor/nodeHashjoin.h"
      33             : #include "executor/nodeIncrementalSort.h"
      34             : #include "executor/nodeIndexonlyscan.h"
      35             : #include "executor/nodeIndexscan.h"
      36             : #include "executor/nodeLimit.h"
      37             : #include "executor/nodeLockRows.h"
      38             : #include "executor/nodeMaterial.h"
      39             : #include "executor/nodeMemoize.h"
      40             : #include "executor/nodeMergeAppend.h"
      41             : #include "executor/nodeMergejoin.h"
      42             : #include "executor/nodeModifyTable.h"
      43             : #include "executor/nodeNamedtuplestorescan.h"
      44             : #include "executor/nodeNestloop.h"
      45             : #include "executor/nodeProjectSet.h"
      46             : #include "executor/nodeRecursiveunion.h"
      47             : #include "executor/nodeResult.h"
      48             : #include "executor/nodeSamplescan.h"
      49             : #include "executor/nodeSeqscan.h"
      50             : #include "executor/nodeSetOp.h"
      51             : #include "executor/nodeSort.h"
      52             : #include "executor/nodeSubplan.h"
      53             : #include "executor/nodeSubqueryscan.h"
      54             : #include "executor/nodeTableFuncscan.h"
      55             : #include "executor/nodeTidrangescan.h"
      56             : #include "executor/nodeTidscan.h"
      57             : #include "executor/nodeUnique.h"
      58             : #include "executor/nodeValuesscan.h"
      59             : #include "executor/nodeWindowAgg.h"
      60             : #include "executor/nodeWorktablescan.h"
      61             : #include "nodes/extensible.h"
      62             : #include "nodes/nodeFuncs.h"
      63             : #include "nodes/pathnodes.h"
      64             : #include "utils/rel.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     2295802 : ExecReScan(PlanState *node)
      79             : {
      80             :     /* If collecting timing stats, update them */
      81     2295802 :     if (node->instrument)
      82       29614 :         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     2295802 :     if (node->chgParam != NULL)
      98             :     {
      99             :         ListCell   *l;
     100             : 
     101     2087510 :         foreach(l, node->initPlan)
     102             :         {
     103        1300 :             SubPlanState *sstate = (SubPlanState *) lfirst(l);
     104        1300 :             PlanState  *splan = sstate->planstate;
     105             : 
     106        1300 :             if (splan->plan->extParam != NULL)    /* don't care about child
     107             :                                                  * local Params */
     108        1152 :                 UpdateChangedParamSet(splan, node->chgParam);
     109        1300 :             if (splan->chgParam != NULL)
     110         912 :                 ExecReScanSetParamPlan(sstate, node);
     111             :         }
     112     2087832 :         foreach(l, node->subPlan)
     113             :         {
     114        1622 :             SubPlanState *sstate = (SubPlanState *) lfirst(l);
     115        1622 :             PlanState  *splan = sstate->planstate;
     116             : 
     117        1622 :             if (splan->plan->extParam != NULL)
     118        1616 :                 UpdateChangedParamSet(splan, node->chgParam);
     119             :         }
     120             :         /* Well. Now set chgParam for child trees. */
     121     2086210 :         if (outerPlanState(node) != NULL)
     122      584464 :             UpdateChangedParamSet(outerPlanState(node), node->chgParam);
     123     2086210 :         if (innerPlanState(node) != NULL)
     124       12510 :             UpdateChangedParamSet(innerPlanState(node), node->chgParam);
     125             :     }
     126             : 
     127             :     /* Call expression callbacks */
     128     2295802 :     if (node->ps_ExprContext)
     129     2130924 :         ReScanExprContext(node->ps_ExprContext);
     130             : 
     131             :     /* And do node-type-specific processing */
     132     2295802 :     switch (nodeTag(node))
     133             :     {
     134       44040 :         case T_ResultState:
     135       44040 :             ExecReScanResult((ResultState *) node);
     136       44040 :             break;
     137             : 
     138       24386 :         case T_ProjectSetState:
     139       24386 :             ExecReScanProjectSet((ProjectSetState *) node);
     140       24386 :             break;
     141             : 
     142           0 :         case T_ModifyTableState:
     143           0 :             ExecReScanModifyTable((ModifyTableState *) node);
     144           0 :             break;
     145             : 
     146       15498 :         case T_AppendState:
     147       15498 :             ExecReScanAppend((AppendState *) node);
     148       15498 :             break;
     149             : 
     150          18 :         case T_MergeAppendState:
     151          18 :             ExecReScanMergeAppend((MergeAppendState *) node);
     152          18 :             break;
     153             : 
     154          12 :         case T_RecursiveUnionState:
     155          12 :             ExecReScanRecursiveUnion((RecursiveUnionState *) node);
     156          12 :             break;
     157             : 
     158          50 :         case T_BitmapAndState:
     159          50 :             ExecReScanBitmapAnd((BitmapAndState *) node);
     160          50 :             break;
     161             : 
     162          28 :         case T_BitmapOrState:
     163          28 :             ExecReScanBitmapOr((BitmapOrState *) node);
     164          28 :             break;
     165             : 
     166      930486 :         case T_SeqScanState:
     167      930486 :             ExecReScanSeqScan((SeqScanState *) node);
     168      930486 :             break;
     169             : 
     170          58 :         case T_SampleScanState:
     171          58 :             ExecReScanSampleScan((SampleScanState *) node);
     172          58 :             break;
     173             : 
     174         300 :         case T_GatherState:
     175         300 :             ExecReScanGather((GatherState *) node);
     176         300 :             break;
     177             : 
     178          48 :         case T_GatherMergeState:
     179          48 :             ExecReScanGatherMerge((GatherMergeState *) node);
     180          48 :             break;
     181             : 
     182      368702 :         case T_IndexScanState:
     183      368702 :             ExecReScanIndexScan((IndexScanState *) node);
     184      368696 :             break;
     185             : 
     186       82436 :         case T_IndexOnlyScanState:
     187       82436 :             ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
     188       82436 :             break;
     189             : 
     190        4986 :         case T_BitmapIndexScanState:
     191        4986 :             ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
     192        4986 :             break;
     193             : 
     194        4388 :         case T_BitmapHeapScanState:
     195        4388 :             ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
     196        4388 :             break;
     197             : 
     198          18 :         case T_TidScanState:
     199          18 :             ExecReScanTidScan((TidScanState *) node);
     200          18 :             break;
     201             : 
     202          66 :         case T_TidRangeScanState:
     203          66 :             ExecReScanTidRangeScan((TidRangeScanState *) node);
     204          66 :             break;
     205             : 
     206        2162 :         case T_SubqueryScanState:
     207        2162 :             ExecReScanSubqueryScan((SubqueryScanState *) node);
     208        2162 :             break;
     209             : 
     210       84160 :         case T_FunctionScanState:
     211       84160 :             ExecReScanFunctionScan((FunctionScanState *) node);
     212       84160 :             break;
     213             : 
     214         192 :         case T_TableFuncScanState:
     215         192 :             ExecReScanTableFuncScan((TableFuncScanState *) node);
     216         192 :             break;
     217             : 
     218       60386 :         case T_ValuesScanState:
     219       60386 :             ExecReScanValuesScan((ValuesScanState *) node);
     220       60386 :             break;
     221             : 
     222        5708 :         case T_CteScanState:
     223        5708 :             ExecReScanCteScan((CteScanState *) node);
     224        5708 :             break;
     225             : 
     226           0 :         case T_NamedTuplestoreScanState:
     227           0 :             ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
     228           0 :             break;
     229             : 
     230        6270 :         case T_WorkTableScanState:
     231        6270 :             ExecReScanWorkTableScan((WorkTableScanState *) node);
     232        6270 :             break;
     233             : 
     234         804 :         case T_ForeignScanState:
     235         804 :             ExecReScanForeignScan((ForeignScanState *) node);
     236         804 :             break;
     237             : 
     238           0 :         case T_CustomScanState:
     239           0 :             ExecReScanCustomScan((CustomScanState *) node);
     240           0 :             break;
     241             : 
     242       10810 :         case T_NestLoopState:
     243       10810 :             ExecReScanNestLoop((NestLoopState *) node);
     244       10810 :             break;
     245             : 
     246         458 :         case T_MergeJoinState:
     247         458 :             ExecReScanMergeJoin((MergeJoinState *) node);
     248         458 :             break;
     249             : 
     250        2084 :         case T_HashJoinState:
     251        2084 :             ExecReScanHashJoin((HashJoinState *) node);
     252        2084 :             break;
     253             : 
     254      107476 :         case T_MaterialState:
     255      107476 :             ExecReScanMaterial((MaterialState *) node);
     256      107476 :             break;
     257             : 
     258      455038 :         case T_MemoizeState:
     259      455038 :             ExecReScanMemoize((MemoizeState *) node);
     260      455038 :             break;
     261             : 
     262       40050 :         case T_SortState:
     263       40050 :             ExecReScanSort((SortState *) node);
     264       40050 :             break;
     265             : 
     266          12 :         case T_IncrementalSortState:
     267          12 :             ExecReScanIncrementalSort((IncrementalSortState *) node);
     268          12 :             break;
     269             : 
     270          24 :         case T_GroupState:
     271          24 :             ExecReScanGroup((GroupState *) node);
     272          24 :             break;
     273             : 
     274       41900 :         case T_AggState:
     275       41900 :             ExecReScanAgg((AggState *) node);
     276       41900 :             break;
     277             : 
     278          78 :         case T_WindowAggState:
     279          78 :             ExecReScanWindowAgg((WindowAggState *) node);
     280          78 :             break;
     281             : 
     282           0 :         case T_UniqueState:
     283           0 :             ExecReScanUnique((UniqueState *) node);
     284           0 :             break;
     285             : 
     286        1154 :         case T_HashState:
     287        1154 :             ExecReScanHash((HashState *) node);
     288        1154 :             break;
     289             : 
     290         600 :         case T_SetOpState:
     291         600 :             ExecReScanSetOp((SetOpState *) node);
     292         600 :             break;
     293             : 
     294          16 :         case T_LockRowsState:
     295          16 :             ExecReScanLockRows((LockRowsState *) node);
     296          16 :             break;
     297             : 
     298         900 :         case T_LimitState:
     299         900 :             ExecReScanLimit((LimitState *) node);
     300         900 :             break;
     301             : 
     302           0 :         default:
     303           0 :             elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
     304             :             break;
     305             :     }
     306             : 
     307     2295796 :     if (node->chgParam != NULL)
     308             :     {
     309     2086210 :         bms_free(node->chgParam);
     310     2086210 :         node->chgParam = NULL;
     311             :     }
     312     2295796 : }
     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      709366 : ExecMarkPos(PlanState *node)
     329             : {
     330      709366 :     switch (nodeTag(node))
     331             :     {
     332        6034 :         case T_IndexScanState:
     333        6034 :             ExecIndexMarkPos((IndexScanState *) node);
     334        6034 :             break;
     335             : 
     336      124010 :         case T_IndexOnlyScanState:
     337      124010 :             ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
     338      124010 :             break;
     339             : 
     340           0 :         case T_CustomScanState:
     341           0 :             ExecCustomMarkPos((CustomScanState *) node);
     342           0 :             break;
     343             : 
     344        6606 :         case T_MaterialState:
     345        6606 :             ExecMaterialMarkPos((MaterialState *) node);
     346        6606 :             break;
     347             : 
     348      572716 :         case T_SortState:
     349      572716 :             ExecSortMarkPos((SortState *) node);
     350      572716 :             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      709366 : }
     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      138762 : ExecRestrPos(PlanState *node)
     378             : {
     379      138762 :     switch (nodeTag(node))
     380             :     {
     381       54012 :         case T_IndexScanState:
     382       54012 :             ExecIndexRestrPos((IndexScanState *) node);
     383       54012 :             break;
     384             : 
     385           6 :         case T_IndexOnlyScanState:
     386           6 :             ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
     387           6 :             break;
     388             : 
     389           0 :         case T_CustomScanState:
     390           0 :             ExecCustomRestrPos((CustomScanState *) node);
     391           0 :             break;
     392             : 
     393       54276 :         case T_MaterialState:
     394       54276 :             ExecMaterialRestrPos((MaterialState *) node);
     395       54276 :             break;
     396             : 
     397       30468 :         case T_SortState:
     398       30468 :             ExecSortRestrPos((SortState *) node);
     399       30468 :             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      138762 : }
     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        6166 : 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        6166 :     switch (pathnode->pathtype)
     427             :     {
     428        5432 :         case T_IndexScan:
     429             :         case T_IndexOnlyScan:
     430             : 
     431             :             /*
     432             :              * Not all index types support mark/restore.
     433             :              */
     434        5432 :             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          84 :         case T_Append:
     467             :             {
     468          84 :                 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          84 :                 if (list_length(appendPath->subpaths) == 1)
     476           0 :                     return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
     477             :                 /* Otherwise, Append can't handle it */
     478          84 :                 return false;
     479             :             }
     480             : 
     481          36 :         case T_MergeAppend:
     482             :             {
     483          36 :                 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          36 :                 if (list_length(mapath->subpaths) == 1)
     491           0 :                     return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
     492             :                 /* Otherwise, MergeAppend can't handle it */
     493          36 :                 return false;
     494             :             }
     495             : 
     496         614 :         default:
     497         614 :             break;
     498             :     }
     499             : 
     500         614 :     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        3192 : ExecSupportsBackwardScan(Plan *node)
     513             : {
     514        3192 :     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        3192 :     if (node->parallel_aware)
     523           0 :         return false;
     524             : 
     525        3192 :     switch (nodeTag(node))
     526             :     {
     527          70 :         case T_Result:
     528          70 :             if (outerPlan(node) != NULL)
     529           0 :                 return ExecSupportsBackwardScan(outerPlan(node));
     530             :             else
     531          70 :                 return false;
     532             : 
     533          40 :         case T_Append:
     534             :             {
     535             :                 ListCell   *l;
     536             : 
     537             :                 /* With async, tuples may be interleaved, so can't back up. */
     538          40 :                 if (((Append *) node)->nasyncplans > 0)
     539           0 :                     return false;
     540             : 
     541         136 :                 foreach(l, ((Append *) node)->appendplans)
     542             :                 {
     543          98 :                     if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
     544           2 :                         return false;
     545             :                 }
     546             :                 /* need not check tlist because Append doesn't evaluate it */
     547          38 :                 return true;
     548             :             }
     549             : 
     550           6 :         case T_SampleScan:
     551             :             /* Simplify life for tablesample methods by disallowing this */
     552           6 :             return false;
     553             : 
     554           0 :         case T_Gather:
     555           0 :             return false;
     556             : 
     557         416 :         case T_IndexScan:
     558         416 :             return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
     559             : 
     560         122 :         case T_IndexOnlyScan:
     561         122 :             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        1720 :         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        1720 :             return true;
     581             : 
     582           4 :         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           4 :             return false;
     589             : 
     590         132 :         case T_LockRows:
     591             :         case T_Limit:
     592         132 :             return ExecSupportsBackwardScan(outerPlan(node));
     593             : 
     594         682 :         default:
     595         682 :             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         538 : IndexSupportsBackwardScan(Oid indexid)
     605             : {
     606             :     bool        result;
     607             :     HeapTuple   ht_idxrel;
     608             :     Form_pg_class idxrelrec;
     609             :     IndexAmRoutine *amroutine;
     610             : 
     611             :     /* Fetch the pg_class tuple of the index relation */
     612         538 :     ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
     613         538 :     if (!HeapTupleIsValid(ht_idxrel))
     614           0 :         elog(ERROR, "cache lookup failed for relation %u", indexid);
     615         538 :     idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
     616             : 
     617             :     /* Fetch the index AM's API struct */
     618         538 :     amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
     619             : 
     620         538 :     result = amroutine->amcanbackward;
     621             : 
     622         538 :     pfree(amroutine);
     623         538 :     ReleaseSysCache(ht_idxrel);
     624             : 
     625         538 :     return result;
     626             : }
     627             : 
     628             : /*
     629             :  * ExecMaterializesOutput - does a plan type materialize its output?
     630             :  *
     631             :  * Returns true if the plan node type is one that automatically materializes
     632             :  * its output (typically by keeping it in a tuplestore).  For such plans,
     633             :  * a rescan without any parameter change will have zero startup cost and
     634             :  * very low per-tuple cost.
     635             :  */
     636             : bool
     637      418348 : ExecMaterializesOutput(NodeTag plantype)
     638             : {
     639      418348 :     switch (plantype)
     640             :     {
     641       19656 :         case T_Material:
     642             :         case T_FunctionScan:
     643             :         case T_TableFuncScan:
     644             :         case T_CteScan:
     645             :         case T_NamedTuplestoreScan:
     646             :         case T_WorkTableScan:
     647             :         case T_Sort:
     648       19656 :             return true;
     649             : 
     650      398692 :         default:
     651      398692 :             break;
     652             :     }
     653             : 
     654      398692 :     return false;
     655             : }

Generated by: LCOV version 1.14