Line data Source code
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 45416 : WorkTableScanNext(WorkTableScanState *node)
32 : {
33 : TupleTableSlot *slot;
34 : 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 : Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
52 :
53 45416 : tuplestorestate = node->rustate->working_table;
54 :
55 : /*
56 : * Get the next tuple from tuplestore. Return NULL if no more tuples.
57 : */
58 45416 : slot = node->ss.ss_ScanTupleSlot;
59 45416 : (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot);
60 45416 : return slot;
61 : }
62 :
63 : /*
64 : * WorkTableScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
65 : */
66 : static bool
67 0 : WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot)
68 : {
69 : /* nothing to check */
70 0 : return true;
71 : }
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 37890 : ExecWorkTableScan(PlanState *pstate)
83 : {
84 37890 : 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 37890 : if (node->rustate == NULL)
93 : {
94 579 : WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan;
95 579 : EState *estate = node->ss.ps.state;
96 : ParamExecData *param;
97 :
98 579 : param = &(estate->es_param_exec_vals[plan->wtParam]);
99 : Assert(param->execPlan == NULL);
100 : Assert(!param->isnull);
101 579 : node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
102 : 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 579 : ExecAssignScanType(&node->ss,
111 579 : 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 579 : ExecAssignScanProjectionInfo(&node->ss);
118 : }
119 :
120 37890 : return ExecScan(&node->ss,
121 : (ExecScanAccessMtd) WorkTableScanNext,
122 : (ExecScanRecheckMtd) WorkTableScanRecheck);
123 : }
124 :
125 :
126 : /* ----------------------------------------------------------------
127 : * ExecInitWorkTableScan
128 : * ----------------------------------------------------------------
129 : */
130 : WorkTableScanState *
131 615 : ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
132 : {
133 : WorkTableScanState *scanstate;
134 :
135 : /* check for unsupported flags */
136 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
137 :
138 : /*
139 : * WorkTableScan should not have any children.
140 : */
141 : Assert(outerPlan(node) == NULL);
142 : Assert(innerPlan(node) == NULL);
143 :
144 : /*
145 : * create new WorkTableScanState for node
146 : */
147 615 : scanstate = makeNode(WorkTableScanState);
148 615 : scanstate->ss.ps.plan = (Plan *) node;
149 615 : scanstate->ss.ps.state = estate;
150 615 : scanstate->ss.ps.ExecProcNode = ExecWorkTableScan;
151 615 : scanstate->rustate = NULL; /* we'll set this later */
152 :
153 : /*
154 : * Miscellaneous initialization
155 : *
156 : * create expression context for node
157 : */
158 615 : ExecAssignExprContext(estate, &scanstate->ss.ps);
159 :
160 : /*
161 : * tuple table initialization
162 : */
163 615 : ExecInitResultTypeTL(&scanstate->ss.ps);
164 :
165 : /* signal that return type is not yet known */
166 615 : scanstate->ss.ps.resultopsset = true;
167 615 : scanstate->ss.ps.resultopsfixed = false;
168 :
169 615 : ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, &TTSOpsMinimalTuple, 0);
170 :
171 : /*
172 : * initialize child expressions
173 : */
174 615 : scanstate->ss.ps.qual =
175 615 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
176 :
177 : /*
178 : * Do not yet initialize projection info, see ExecWorkTableScan() for
179 : * details.
180 : */
181 :
182 615 : return scanstate;
183 : }
184 :
185 : /* ----------------------------------------------------------------
186 : * ExecReScanWorkTableScan
187 : *
188 : * Rescans the relation.
189 : * ----------------------------------------------------------------
190 : */
191 : void
192 4280 : ExecReScanWorkTableScan(WorkTableScanState *node)
193 : {
194 4280 : if (node->ss.ps.ps_ResultTupleSlot)
195 3913 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
196 :
197 4280 : ExecScanReScan(&node->ss);
198 :
199 : /* No need (or way) to rescan if ExecWorkTableScan not called yet */
200 4280 : if (node->rustate)
201 4276 : tuplestore_rescan(node->rustate->working_table);
202 4280 : }
|