Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/nodeWorktablescan.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * nodeWorktablescan.c
4
 *    routines to handle WorkTableScan nodes.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/executor/nodeWorktablescan.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
16
#include "postgres.h"
17
18
#include "executor/executor.h"
19
#include "executor/nodeWorktablescan.h"
20
#include "utils/tuplestore.h"
21
22
static TupleTableSlot *WorkTableScanNext(WorkTableScanState *node);
23
24
/* ----------------------------------------------------------------
25
 *    WorkTableScanNext
26
 *
27
 *    This is a workhorse for ExecWorkTableScan
28
 * ----------------------------------------------------------------
29
 */
30
static TupleTableSlot *
31
WorkTableScanNext(WorkTableScanState *node)
32
0
{
33
0
  TupleTableSlot *slot;
34
0
  Tuplestorestate *tuplestorestate;
35
36
  /*
37
   * get information from the estate and scan state
38
   *
39
   * Note: we intentionally do not support backward scan.  Although it would
40
   * take only a couple more lines here, it would force nodeRecursiveunion.c
41
   * to create the tuplestore with backward scan enabled, which has a
42
   * performance cost.  In practice backward scan is never useful for a
43
   * worktable plan node, since it cannot appear high enough in the plan
44
   * tree of a scrollable cursor to be exposed to a backward-scan
45
   * requirement.  So it's not worth expending effort to support it.
46
   *
47
   * Note: we are also assuming that this node is the only reader of the
48
   * worktable.  Therefore, we don't need a private read pointer for the
49
   * tuplestore, nor do we need to tell tuplestore_gettupleslot to copy.
50
   */
51
0
  Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
52
53
0
  tuplestorestate = node->rustate->working_table;
54
55
  /*
56
   * Get the next tuple from tuplestore. Return NULL if no more tuples.
57
   */
58
0
  slot = node->ss.ss_ScanTupleSlot;
59
0
  (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot);
60
0
  return slot;
61
0
}
62
63
/*
64
 * WorkTableScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
65
 */
66
static bool
67
WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot)
68
0
{
69
  /* nothing to check */
70
0
  return true;
71
0
}
72
73
/* ----------------------------------------------------------------
74
 *    ExecWorkTableScan(node)
75
 *
76
 *    Scans the worktable sequentially and returns the next qualifying tuple.
77
 *    We call the ExecScan() routine and pass it the appropriate
78
 *    access method functions.
79
 * ----------------------------------------------------------------
80
 */
81
static TupleTableSlot *
82
ExecWorkTableScan(PlanState *pstate)
83
0
{
84
0
  WorkTableScanState *node = castNode(WorkTableScanState, pstate);
85
86
  /*
87
   * On the first call, find the ancestor RecursiveUnion's state via the
88
   * Param slot reserved for it.  (We can't do this during node init because
89
   * there are corner cases where we'll get the init call before the
90
   * RecursiveUnion does.)
91
   */
92
0
  if (node->rustate == NULL)
93
0
  {
94
0
    WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan;
95
0
    EState     *estate = node->ss.ps.state;
96
0
    ParamExecData *param;
97
98
0
    param = &(estate->es_param_exec_vals[plan->wtParam]);
99
0
    Assert(param->execPlan == NULL);
100
0
    Assert(!param->isnull);
101
0
    node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
102
0
    Assert(node->rustate);
103
104
    /*
105
     * The scan tuple type (ie, the rowtype we expect to find in the work
106
     * table) is the same as the result rowtype of the ancestor
107
     * RecursiveUnion node.  Note this depends on the assumption that
108
     * RecursiveUnion doesn't allow projection.
109
     */
110
0
    ExecAssignScanType(&node->ss,
111
0
               ExecGetResultType(&node->rustate->ps));
112
113
    /*
114
     * Now we can initialize the projection info.  This must be completed
115
     * before we can call ExecScan().
116
     */
117
0
    ExecAssignScanProjectionInfo(&node->ss);
118
0
  }
119
120
0
  return ExecScan(&node->ss,
121
0
          (ExecScanAccessMtd) WorkTableScanNext,
122
0
          (ExecScanRecheckMtd) WorkTableScanRecheck);
123
0
}
124
125
126
/* ----------------------------------------------------------------
127
 *    ExecInitWorkTableScan
128
 * ----------------------------------------------------------------
129
 */
130
WorkTableScanState *
131
ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
132
0
{
133
0
  WorkTableScanState *scanstate;
134
135
  /* check for unsupported flags */
136
0
  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
137
138
  /*
139
   * WorkTableScan should not have any children.
140
   */
141
0
  Assert(outerPlan(node) == NULL);
142
0
  Assert(innerPlan(node) == NULL);
143
144
  /*
145
   * create new WorkTableScanState for node
146
   */
147
0
  scanstate = makeNode(WorkTableScanState);
148
0
  scanstate->ss.ps.plan = (Plan *) node;
149
0
  scanstate->ss.ps.state = estate;
150
0
  scanstate->ss.ps.ExecProcNode = ExecWorkTableScan;
151
0
  scanstate->rustate = NULL; /* we'll set this later */
152
153
  /*
154
   * Miscellaneous initialization
155
   *
156
   * create expression context for node
157
   */
158
0
  ExecAssignExprContext(estate, &scanstate->ss.ps);
159
160
  /*
161
   * tuple table initialization
162
   */
163
0
  ExecInitResultTypeTL(&scanstate->ss.ps);
164
165
  /* signal that return type is not yet known */
166
0
  scanstate->ss.ps.resultopsset = true;
167
0
  scanstate->ss.ps.resultopsfixed = false;
168
169
0
  ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, &TTSOpsMinimalTuple, 0);
170
171
  /*
172
   * initialize child expressions
173
   */
174
0
  scanstate->ss.ps.qual =
175
0
    ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
176
177
  /*
178
   * Do not yet initialize projection info, see ExecWorkTableScan() for
179
   * details.
180
   */
181
182
0
  return scanstate;
183
0
}
184
185
/* ----------------------------------------------------------------
186
 *    ExecReScanWorkTableScan
187
 *
188
 *    Rescans the relation.
189
 * ----------------------------------------------------------------
190
 */
191
void
192
ExecReScanWorkTableScan(WorkTableScanState *node)
193
0
{
194
0
  if (node->ss.ps.ps_ResultTupleSlot)
195
0
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
196
197
0
  ExecScanReScan(&node->ss);
198
199
  /* No need (or way) to rescan if ExecWorkTableScan not called yet */
200
0
  if (node->rustate)
201
0
    tuplestore_rescan(node->rustate->working_table);
202
0
}