Coverage Report

Created: 2025-06-13 06:06

/src/postgres/src/backend/executor/execAmi.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * execAmi.c
4
 *    miscellaneous executor access method routines
5
 *
6
 * Portions Copyright (c) 1996-2025, 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
ExecReScan(PlanState *node)
78
0
{
79
  /* If collecting timing stats, update them */
80
0
  if (node->instrument)
81
0
    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
0
  if (node->chgParam != NULL)
97
0
  {
98
0
    ListCell   *l;
99
100
0
    foreach(l, node->initPlan)
101
0
    {
102
0
      SubPlanState *sstate = (SubPlanState *) lfirst(l);
103
0
      PlanState  *splan = sstate->planstate;
104
105
0
      if (splan->plan->extParam != NULL) /* don't care about child
106
                         * local Params */
107
0
        UpdateChangedParamSet(splan, node->chgParam);
108
0
      if (splan->chgParam != NULL)
109
0
        ExecReScanSetParamPlan(sstate, node);
110
0
    }
111
0
    foreach(l, node->subPlan)
112
0
    {
113
0
      SubPlanState *sstate = (SubPlanState *) lfirst(l);
114
0
      PlanState  *splan = sstate->planstate;
115
116
0
      if (splan->plan->extParam != NULL)
117
0
        UpdateChangedParamSet(splan, node->chgParam);
118
0
    }
119
    /* Well. Now set chgParam for child trees. */
120
0
    if (outerPlanState(node) != NULL)
121
0
      UpdateChangedParamSet(outerPlanState(node), node->chgParam);
122
0
    if (innerPlanState(node) != NULL)
123
0
      UpdateChangedParamSet(innerPlanState(node), node->chgParam);
124
0
  }
125
126
  /* Call expression callbacks */
127
0
  if (node->ps_ExprContext)
128
0
    ReScanExprContext(node->ps_ExprContext);
129
130
  /* And do node-type-specific processing */
131
0
  switch (nodeTag(node))
132
0
  {
133
0
    case T_ResultState:
134
0
      ExecReScanResult((ResultState *) node);
135
0
      break;
136
137
0
    case T_ProjectSetState:
138
0
      ExecReScanProjectSet((ProjectSetState *) node);
139
0
      break;
140
141
0
    case T_ModifyTableState:
142
0
      ExecReScanModifyTable((ModifyTableState *) node);
143
0
      break;
144
145
0
    case T_AppendState:
146
0
      ExecReScanAppend((AppendState *) node);
147
0
      break;
148
149
0
    case T_MergeAppendState:
150
0
      ExecReScanMergeAppend((MergeAppendState *) node);
151
0
      break;
152
153
0
    case T_RecursiveUnionState:
154
0
      ExecReScanRecursiveUnion((RecursiveUnionState *) node);
155
0
      break;
156
157
0
    case T_BitmapAndState:
158
0
      ExecReScanBitmapAnd((BitmapAndState *) node);
159
0
      break;
160
161
0
    case T_BitmapOrState:
162
0
      ExecReScanBitmapOr((BitmapOrState *) node);
163
0
      break;
164
165
0
    case T_SeqScanState:
166
0
      ExecReScanSeqScan((SeqScanState *) node);
167
0
      break;
168
169
0
    case T_SampleScanState:
170
0
      ExecReScanSampleScan((SampleScanState *) node);
171
0
      break;
172
173
0
    case T_GatherState:
174
0
      ExecReScanGather((GatherState *) node);
175
0
      break;
176
177
0
    case T_GatherMergeState:
178
0
      ExecReScanGatherMerge((GatherMergeState *) node);
179
0
      break;
180
181
0
    case T_IndexScanState:
182
0
      ExecReScanIndexScan((IndexScanState *) node);
183
0
      break;
184
185
0
    case T_IndexOnlyScanState:
186
0
      ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
187
0
      break;
188
189
0
    case T_BitmapIndexScanState:
190
0
      ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
191
0
      break;
192
193
0
    case T_BitmapHeapScanState:
194
0
      ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
195
0
      break;
196
197
0
    case T_TidScanState:
198
0
      ExecReScanTidScan((TidScanState *) node);
199
0
      break;
200
201
0
    case T_TidRangeScanState:
202
0
      ExecReScanTidRangeScan((TidRangeScanState *) node);
203
0
      break;
204
205
0
    case T_SubqueryScanState:
206
0
      ExecReScanSubqueryScan((SubqueryScanState *) node);
207
0
      break;
208
209
0
    case T_FunctionScanState:
210
0
      ExecReScanFunctionScan((FunctionScanState *) node);
211
0
      break;
212
213
0
    case T_TableFuncScanState:
214
0
      ExecReScanTableFuncScan((TableFuncScanState *) node);
215
0
      break;
216
217
0
    case T_ValuesScanState:
218
0
      ExecReScanValuesScan((ValuesScanState *) node);
219
0
      break;
220
221
0
    case T_CteScanState:
222
0
      ExecReScanCteScan((CteScanState *) node);
223
0
      break;
224
225
0
    case T_NamedTuplestoreScanState:
226
0
      ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
227
0
      break;
228
229
0
    case T_WorkTableScanState:
230
0
      ExecReScanWorkTableScan((WorkTableScanState *) node);
231
0
      break;
232
233
0
    case T_ForeignScanState:
234
0
      ExecReScanForeignScan((ForeignScanState *) node);
235
0
      break;
236
237
0
    case T_CustomScanState:
238
0
      ExecReScanCustomScan((CustomScanState *) node);
239
0
      break;
240
241
0
    case T_NestLoopState:
242
0
      ExecReScanNestLoop((NestLoopState *) node);
243
0
      break;
244
245
0
    case T_MergeJoinState:
246
0
      ExecReScanMergeJoin((MergeJoinState *) node);
247
0
      break;
248
249
0
    case T_HashJoinState:
250
0
      ExecReScanHashJoin((HashJoinState *) node);
251
0
      break;
252
253
0
    case T_MaterialState:
254
0
      ExecReScanMaterial((MaterialState *) node);
255
0
      break;
256
257
0
    case T_MemoizeState:
258
0
      ExecReScanMemoize((MemoizeState *) node);
259
0
      break;
260
261
0
    case T_SortState:
262
0
      ExecReScanSort((SortState *) node);
263
0
      break;
264
265
0
    case T_IncrementalSortState:
266
0
      ExecReScanIncrementalSort((IncrementalSortState *) node);
267
0
      break;
268
269
0
    case T_GroupState:
270
0
      ExecReScanGroup((GroupState *) node);
271
0
      break;
272
273
0
    case T_AggState:
274
0
      ExecReScanAgg((AggState *) node);
275
0
      break;
276
277
0
    case T_WindowAggState:
278
0
      ExecReScanWindowAgg((WindowAggState *) node);
279
0
      break;
280
281
0
    case T_UniqueState:
282
0
      ExecReScanUnique((UniqueState *) node);
283
0
      break;
284
285
0
    case T_HashState:
286
0
      ExecReScanHash((HashState *) node);
287
0
      break;
288
289
0
    case T_SetOpState:
290
0
      ExecReScanSetOp((SetOpState *) node);
291
0
      break;
292
293
0
    case T_LockRowsState:
294
0
      ExecReScanLockRows((LockRowsState *) node);
295
0
      break;
296
297
0
    case T_LimitState:
298
0
      ExecReScanLimit((LimitState *) node);
299
0
      break;
300
301
0
    default:
302
0
      elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
303
0
      break;
304
0
  }
305
306
0
  if (node->chgParam != NULL)
307
0
  {
308
0
    bms_free(node->chgParam);
309
0
    node->chgParam = NULL;
310
0
  }
311
0
}
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
ExecMarkPos(PlanState *node)
328
0
{
329
0
  switch (nodeTag(node))
330
0
  {
331
0
    case T_IndexScanState:
332
0
      ExecIndexMarkPos((IndexScanState *) node);
333
0
      break;
334
335
0
    case T_IndexOnlyScanState:
336
0
      ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
337
0
      break;
338
339
0
    case T_CustomScanState:
340
0
      ExecCustomMarkPos((CustomScanState *) node);
341
0
      break;
342
343
0
    case T_MaterialState:
344
0
      ExecMaterialMarkPos((MaterialState *) node);
345
0
      break;
346
347
0
    case T_SortState:
348
0
      ExecSortMarkPos((SortState *) node);
349
0
      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
0
  }
360
0
}
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
ExecRestrPos(PlanState *node)
377
0
{
378
0
  switch (nodeTag(node))
379
0
  {
380
0
    case T_IndexScanState:
381
0
      ExecIndexRestrPos((IndexScanState *) node);
382
0
      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
0
    case T_MaterialState:
393
0
      ExecMaterialRestrPos((MaterialState *) node);
394
0
      break;
395
396
0
    case T_SortState:
397
0
      ExecSortRestrPos((SortState *) node);
398
0
      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
0
      break;
407
0
  }
408
0
}
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
ExecSupportsMarkRestore(Path *pathnode)
419
0
{
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
0
  switch (pathnode->pathtype)
426
0
  {
427
0
    case T_IndexScan:
428
0
    case T_IndexOnlyScan:
429
430
      /*
431
       * Not all index types support mark/restore.
432
       */
433
0
      return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
434
435
0
    case T_Material:
436
0
    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
0
      else
459
0
      {
460
        /* Simple RTE_RESULT base relation */
461
0
        Assert(IsA(pathnode, Path));
462
0
        return false; /* childless Result */
463
0
      }
464
465
0
    case T_Append:
466
0
      {
467
0
        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
0
        if (list_length(appendPath->subpaths) == 1)
475
0
          return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
476
        /* Otherwise, Append can't handle it */
477
0
        return false;
478
0
      }
479
480
0
    case T_MergeAppend:
481
0
      {
482
0
        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
0
        if (list_length(mapath->subpaths) == 1)
490
0
          return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
491
        /* Otherwise, MergeAppend can't handle it */
492
0
        return false;
493
0
      }
494
495
0
    default:
496
0
      break;
497
0
  }
498
499
0
  return false;
500
0
}
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
ExecSupportsBackwardScan(Plan *node)
512
0
{
513
0
  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
0
  if (node->parallel_aware)
522
0
    return false;
523
524
0
  switch (nodeTag(node))
525
0
  {
526
0
    case T_Result:
527
0
      if (outerPlan(node) != NULL)
528
0
        return ExecSupportsBackwardScan(outerPlan(node));
529
0
      else
530
0
        return false;
531
532
0
    case T_Append:
533
0
      {
534
0
        ListCell   *l;
535
536
        /* With async, tuples may be interleaved, so can't back up. */
537
0
        if (((Append *) node)->nasyncplans > 0)
538
0
          return false;
539
540
0
        foreach(l, ((Append *) node)->appendplans)
541
0
        {
542
0
          if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
543
0
            return false;
544
0
        }
545
        /* need not check tlist because Append doesn't evaluate it */
546
0
        return true;
547
0
      }
548
549
0
    case T_SampleScan:
550
      /* Simplify life for tablesample methods by disallowing this */
551
0
      return false;
552
553
0
    case T_Gather:
554
0
      return false;
555
556
0
    case T_IndexScan:
557
0
      return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
558
559
0
    case T_IndexOnlyScan:
560
0
      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
0
    case T_SeqScan:
571
0
    case T_TidScan:
572
0
    case T_TidRangeScan:
573
0
    case T_FunctionScan:
574
0
    case T_ValuesScan:
575
0
    case T_CteScan:
576
0
    case T_Material:
577
0
    case T_Sort:
578
      /* these don't evaluate tlist */
579
0
      return true;
580
581
0
    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
0
      return false;
588
589
0
    case T_LockRows:
590
0
    case T_Limit:
591
0
      return ExecSupportsBackwardScan(outerPlan(node));
592
593
0
    default:
594
0
      return false;
595
0
  }
596
0
}
597
598
/*
599
 * An IndexScan or IndexOnlyScan node supports backward scan only if the
600
 * index's AM does.
601
 */
602
static bool
603
IndexSupportsBackwardScan(Oid indexid)
604
0
{
605
0
  bool    result;
606
0
  HeapTuple ht_idxrel;
607
0
  Form_pg_class idxrelrec;
608
0
  IndexAmRoutine *amroutine;
609
610
  /* Fetch the pg_class tuple of the index relation */
611
0
  ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
612
0
  if (!HeapTupleIsValid(ht_idxrel))
613
0
    elog(ERROR, "cache lookup failed for relation %u", indexid);
614
0
  idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
615
616
  /* Fetch the index AM's API struct */
617
0
  amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
618
619
0
  result = amroutine->amcanbackward;
620
621
0
  pfree(amroutine);
622
0
  ReleaseSysCache(ht_idxrel);
623
624
0
  return result;
625
0
}
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
ExecMaterializesOutput(NodeTag plantype)
637
0
{
638
0
  switch (plantype)
639
0
  {
640
0
    case T_Material:
641
0
    case T_FunctionScan:
642
0
    case T_TableFuncScan:
643
0
    case T_CteScan:
644
0
    case T_NamedTuplestoreScan:
645
0
    case T_WorkTableScan:
646
0
    case T_Sort:
647
0
      return true;
648
649
0
    default:
650
0
      break;
651
0
  }
652
653
0
  return false;
654
0
}