/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 | } |