Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/nodeCtescan.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * nodeCtescan.c
4
 *    routines to handle CteScan 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/nodeCtescan.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
16
#include "postgres.h"
17
18
#include "executor/executor.h"
19
#include "executor/nodeCtescan.h"
20
#include "miscadmin.h"
21
#include "utils/tuplestore.h"
22
23
static TupleTableSlot *CteScanNext(CteScanState *node);
24
25
/* ----------------------------------------------------------------
26
 *    CteScanNext
27
 *
28
 *    This is a workhorse for ExecCteScan
29
 * ----------------------------------------------------------------
30
 */
31
static TupleTableSlot *
32
CteScanNext(CteScanState *node)
33
0
{
34
0
  EState     *estate;
35
0
  ScanDirection dir;
36
0
  bool    forward;
37
0
  Tuplestorestate *tuplestorestate;
38
0
  bool    eof_tuplestore;
39
0
  TupleTableSlot *slot;
40
41
  /*
42
   * get state info from node
43
   */
44
0
  estate = node->ss.ps.state;
45
0
  dir = estate->es_direction;
46
0
  forward = ScanDirectionIsForward(dir);
47
0
  tuplestorestate = node->leader->cte_table;
48
0
  tuplestore_select_read_pointer(tuplestorestate, node->readptr);
49
0
  slot = node->ss.ss_ScanTupleSlot;
50
51
  /*
52
   * If we are not at the end of the tuplestore, or are going backwards, try
53
   * to fetch a tuple from tuplestore.
54
   */
55
0
  eof_tuplestore = tuplestore_ateof(tuplestorestate);
56
57
0
  if (!forward && eof_tuplestore)
58
0
  {
59
0
    if (!node->leader->eof_cte)
60
0
    {
61
      /*
62
       * When reversing direction at tuplestore EOF, the first
63
       * gettupleslot call will fetch the last-added tuple; but we want
64
       * to return the one before that, if possible. So do an extra
65
       * fetch.
66
       */
67
0
      if (!tuplestore_advance(tuplestorestate, forward))
68
0
        return NULL; /* the tuplestore must be empty */
69
0
    }
70
0
    eof_tuplestore = false;
71
0
  }
72
73
  /*
74
   * If we can fetch another tuple from the tuplestore, return it.
75
   *
76
   * Note: we have to use copy=true in the tuplestore_gettupleslot call,
77
   * because we are sharing the tuplestore with other nodes that might write
78
   * into the tuplestore before we get called again.
79
   */
80
0
  if (!eof_tuplestore)
81
0
  {
82
0
    if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
83
0
      return slot;
84
0
    if (forward)
85
0
      eof_tuplestore = true;
86
0
  }
87
88
  /*
89
   * If necessary, try to fetch another row from the CTE query.
90
   *
91
   * Note: the eof_cte state variable exists to short-circuit further calls
92
   * of the CTE plan.  It's not optional, unfortunately, because some plan
93
   * node types are not robust about being called again when they've already
94
   * returned NULL.
95
   */
96
0
  if (eof_tuplestore && !node->leader->eof_cte)
97
0
  {
98
0
    TupleTableSlot *cteslot;
99
100
    /*
101
     * We can only get here with forward==true, so no need to worry about
102
     * which direction the subplan will go.
103
     */
104
0
    cteslot = ExecProcNode(node->cteplanstate);
105
0
    if (TupIsNull(cteslot))
106
0
    {
107
0
      node->leader->eof_cte = true;
108
0
      return NULL;
109
0
    }
110
111
    /*
112
     * There are corner cases where the subplan could change which
113
     * tuplestore read pointer is active, so be sure to reselect ours
114
     * before storing the tuple we got.
115
     */
116
0
    tuplestore_select_read_pointer(tuplestorestate, node->readptr);
117
118
    /*
119
     * Append a copy of the returned tuple to tuplestore.  NOTE: because
120
     * our read pointer is certainly in EOF state, its read position will
121
     * move forward over the added tuple.  This is what we want.  Also,
122
     * any other readers will *not* move past the new tuple, which is what
123
     * they want.
124
     */
125
0
    tuplestore_puttupleslot(tuplestorestate, cteslot);
126
127
    /*
128
     * We MUST copy the CTE query's output tuple into our own slot. This
129
     * is because other CteScan nodes might advance the CTE query before
130
     * we are called again, and our output tuple must stay stable over
131
     * that.
132
     */
133
0
    return ExecCopySlot(slot, cteslot);
134
0
  }
135
136
  /*
137
   * Nothing left ...
138
   */
139
0
  return ExecClearTuple(slot);
140
0
}
141
142
/*
143
 * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
144
 */
145
static bool
146
CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
147
0
{
148
  /* nothing to check */
149
0
  return true;
150
0
}
151
152
/* ----------------------------------------------------------------
153
 *    ExecCteScan(node)
154
 *
155
 *    Scans the CTE sequentially and returns the next qualifying tuple.
156
 *    We call the ExecScan() routine and pass it the appropriate
157
 *    access method functions.
158
 * ----------------------------------------------------------------
159
 */
160
static TupleTableSlot *
161
ExecCteScan(PlanState *pstate)
162
0
{
163
0
  CteScanState *node = castNode(CteScanState, pstate);
164
165
0
  return ExecScan(&node->ss,
166
0
          (ExecScanAccessMtd) CteScanNext,
167
0
          (ExecScanRecheckMtd) CteScanRecheck);
168
0
}
169
170
171
/* ----------------------------------------------------------------
172
 *    ExecInitCteScan
173
 * ----------------------------------------------------------------
174
 */
175
CteScanState *
176
ExecInitCteScan(CteScan *node, EState *estate, int eflags)
177
0
{
178
0
  CteScanState *scanstate;
179
0
  ParamExecData *prmdata;
180
181
  /* check for unsupported flags */
182
0
  Assert(!(eflags & EXEC_FLAG_MARK));
183
184
  /*
185
   * For the moment we have to force the tuplestore to allow REWIND, because
186
   * we might be asked to rescan the CTE even though upper levels didn't
187
   * tell us to be prepared to do it efficiently.  Annoying, since this
188
   * prevents truncation of the tuplestore.  XXX FIXME
189
   *
190
   * Note: if we are in an EPQ recheck plan tree, it's likely that no access
191
   * to the tuplestore is needed at all, making this even more annoying.
192
   * It's not worth improving that as long as all the read pointers would
193
   * have REWIND anyway, but if we ever improve this logic then that aspect
194
   * should be considered too.
195
   */
196
0
  eflags |= EXEC_FLAG_REWIND;
197
198
  /*
199
   * CteScan should not have any children.
200
   */
201
0
  Assert(outerPlan(node) == NULL);
202
0
  Assert(innerPlan(node) == NULL);
203
204
  /*
205
   * create new CteScanState for node
206
   */
207
0
  scanstate = makeNode(CteScanState);
208
0
  scanstate->ss.ps.plan = (Plan *) node;
209
0
  scanstate->ss.ps.state = estate;
210
0
  scanstate->ss.ps.ExecProcNode = ExecCteScan;
211
0
  scanstate->eflags = eflags;
212
0
  scanstate->cte_table = NULL;
213
0
  scanstate->eof_cte = false;
214
215
  /*
216
   * Find the already-initialized plan for the CTE query.
217
   */
218
0
  scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
219
0
                           node->ctePlanId - 1);
220
221
  /*
222
   * The Param slot associated with the CTE query is used to hold a pointer
223
   * to the CteState of the first CteScan node that initializes for this
224
   * CTE.  This node will be the one that holds the shared state for all the
225
   * CTEs, particularly the shared tuplestore.
226
   */
227
0
  prmdata = &(estate->es_param_exec_vals[node->cteParam]);
228
0
  Assert(prmdata->execPlan == NULL);
229
0
  Assert(!prmdata->isnull);
230
0
  scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
231
0
  if (scanstate->leader == NULL)
232
0
  {
233
    /* I am the leader */
234
0
    prmdata->value = PointerGetDatum(scanstate);
235
0
    scanstate->leader = scanstate;
236
0
    scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
237
0
    tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
238
0
    scanstate->readptr = 0;
239
0
  }
240
0
  else
241
0
  {
242
    /* Not the leader */
243
    /* Create my own read pointer, and ensure it is at start */
244
0
    scanstate->readptr =
245
0
      tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
246
0
                      scanstate->eflags);
247
0
    tuplestore_select_read_pointer(scanstate->leader->cte_table,
248
0
                     scanstate->readptr);
249
0
    tuplestore_rescan(scanstate->leader->cte_table);
250
0
  }
251
252
  /*
253
   * Miscellaneous initialization
254
   *
255
   * create expression context for node
256
   */
257
0
  ExecAssignExprContext(estate, &scanstate->ss.ps);
258
259
  /*
260
   * The scan tuple type (ie, the rowtype we expect to find in the work
261
   * table) is the same as the result rowtype of the CTE query.
262
   */
263
0
  ExecInitScanTupleSlot(estate, &scanstate->ss,
264
0
              ExecGetResultType(scanstate->cteplanstate),
265
0
              &TTSOpsMinimalTuple, 0);
266
267
  /*
268
   * Initialize result type and projection.
269
   */
270
0
  ExecInitResultTypeTL(&scanstate->ss.ps);
271
0
  ExecAssignScanProjectionInfo(&scanstate->ss);
272
273
  /*
274
   * initialize child expressions
275
   */
276
0
  scanstate->ss.ps.qual =
277
0
    ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
278
279
0
  return scanstate;
280
0
}
281
282
/* ----------------------------------------------------------------
283
 *    ExecEndCteScan
284
 *
285
 *    frees any storage allocated through C routines.
286
 * ----------------------------------------------------------------
287
 */
288
void
289
ExecEndCteScan(CteScanState *node)
290
0
{
291
  /*
292
   * If I am the leader, free the tuplestore.
293
   */
294
0
  if (node->leader == node)
295
0
  {
296
0
    tuplestore_end(node->cte_table);
297
0
    node->cte_table = NULL;
298
0
  }
299
0
}
300
301
/* ----------------------------------------------------------------
302
 *    ExecReScanCteScan
303
 *
304
 *    Rescans the relation.
305
 * ----------------------------------------------------------------
306
 */
307
void
308
ExecReScanCteScan(CteScanState *node)
309
0
{
310
0
  Tuplestorestate *tuplestorestate = node->leader->cte_table;
311
312
0
  if (node->ss.ps.ps_ResultTupleSlot)
313
0
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
314
315
0
  ExecScanReScan(&node->ss);
316
317
  /*
318
   * Clear the tuplestore if a new scan of the underlying CTE is required.
319
   * This implicitly resets all the tuplestore's read pointers.  Note that
320
   * multiple CTE nodes might redundantly clear the tuplestore; that's OK,
321
   * and not unduly expensive.  We'll stop taking this path as soon as
322
   * somebody has attempted to read something from the underlying CTE
323
   * (thereby causing its chgParam to be cleared).
324
   */
325
0
  if (node->leader->cteplanstate->chgParam != NULL)
326
0
  {
327
0
    tuplestore_clear(tuplestorestate);
328
0
    node->leader->eof_cte = false;
329
0
  }
330
0
  else
331
0
  {
332
    /*
333
     * Else, just rewind my own pointer.  Either the underlying CTE
334
     * doesn't need a rescan (and we can re-read what's in the tuplestore
335
     * now), or somebody else already took care of it.
336
     */
337
0
    tuplestore_select_read_pointer(tuplestorestate, node->readptr);
338
0
    tuplestore_rescan(tuplestorestate);
339
0
  }
340
0
}