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