Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * execScan.c
4 : * This code provides support for generalized relation scans. ExecScan
5 : * is passed a node and a pointer to a function to "do the right thing"
6 : * and return a tuple from the relation. ExecScan then does the tedious
7 : * stuff - checking the qualification and projecting the tuple
8 : * appropriately.
9 : *
10 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
11 : * Portions Copyright (c) 1994, Regents of the University of California
12 : *
13 : *
14 : * IDENTIFICATION
15 : * src/backend/executor/execScan.c
16 : *
17 : *-------------------------------------------------------------------------
18 : */
19 : #include "postgres.h"
20 :
21 : #include "executor/executor.h"
22 : #include "miscadmin.h"
23 : #include "utils/memutils.h"
24 :
25 :
26 :
27 : /*
28 : * ExecScanFetch -- check interrupts & fetch next potential tuple
29 : *
30 : * This routine is concerned with substituting a test tuple if we are
31 : * inside an EvalPlanQual recheck. If we aren't, just execute
32 : * the access method's next-tuple routine.
33 : */
34 : static inline TupleTableSlot *
35 95217326 : ExecScanFetch(ScanState *node,
36 : ExecScanAccessMtd accessMtd,
37 : ExecScanRecheckMtd recheckMtd)
38 : {
39 95217326 : EState *estate = node->ps.state;
40 :
41 95217326 : CHECK_FOR_INTERRUPTS();
42 :
43 95217326 : if (estate->es_epq_active != NULL)
44 : {
45 662 : EPQState *epqstate = estate->es_epq_active;
46 :
47 : /*
48 : * We are inside an EvalPlanQual recheck. Return the test tuple if
49 : * one is available, after rechecking any access-method-specific
50 : * conditions.
51 : */
52 662 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
53 :
54 662 : if (scanrelid == 0)
55 : {
56 : /*
57 : * This is a ForeignScan or CustomScan which has pushed down a
58 : * join to the remote side. The recheck method is responsible not
59 : * only for rechecking the scan/join quals but also for storing
60 : * the correct tuple in the slot.
61 : */
62 :
63 0 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
64 :
65 0 : if (!(*recheckMtd) (node, slot))
66 0 : ExecClearTuple(slot); /* would not be returned by scan */
67 0 : return slot;
68 : }
69 662 : else if (epqstate->relsubs_done[scanrelid - 1])
70 : {
71 : /*
72 : * Return empty slot, as either there is no EPQ tuple for this rel
73 : * or we already returned it.
74 : */
75 :
76 312 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
77 :
78 312 : return ExecClearTuple(slot);
79 : }
80 350 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
81 : {
82 : /*
83 : * Return replacement tuple provided by the EPQ caller.
84 : */
85 :
86 308 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
87 :
88 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
89 :
90 : /* Mark to remember that we shouldn't return it again */
91 308 : epqstate->relsubs_done[scanrelid - 1] = true;
92 :
93 : /* Return empty slot if we haven't got a test tuple */
94 308 : if (TupIsNull(slot))
95 6 : return NULL;
96 :
97 : /* Check if it meets the access-method conditions */
98 302 : if (!(*recheckMtd) (node, slot))
99 10 : return ExecClearTuple(slot); /* would not be returned by
100 : * scan */
101 292 : return slot;
102 : }
103 42 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
104 : {
105 : /*
106 : * Fetch and return replacement tuple using a non-locking rowmark.
107 : */
108 :
109 32 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
110 :
111 : /* Mark to remember that we shouldn't return more */
112 32 : epqstate->relsubs_done[scanrelid - 1] = true;
113 :
114 32 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
115 0 : return NULL;
116 :
117 : /* Return empty slot if we haven't got a test tuple */
118 32 : if (TupIsNull(slot))
119 0 : return NULL;
120 :
121 : /* Check if it meets the access-method conditions */
122 32 : if (!(*recheckMtd) (node, slot))
123 0 : return ExecClearTuple(slot); /* would not be returned by
124 : * scan */
125 32 : return slot;
126 : }
127 : }
128 :
129 : /*
130 : * Run the node-type-specific access method function to get the next tuple
131 : */
132 95216674 : return (*accessMtd) (node);
133 : }
134 :
135 : /* ----------------------------------------------------------------
136 : * ExecScan
137 : *
138 : * Scans the relation using the 'access method' indicated and
139 : * returns the next qualifying tuple.
140 : * The access method returns the next tuple and ExecScan() is
141 : * responsible for checking the tuple returned against the qual-clause.
142 : *
143 : * A 'recheck method' must also be provided that can check an
144 : * arbitrary tuple of the relation against any qual conditions
145 : * that are implemented internal to the access method.
146 : *
147 : * Conditions:
148 : * -- the "cursor" maintained by the AMI is positioned at the tuple
149 : * returned previously.
150 : *
151 : * Initial States:
152 : * -- the relation indicated is opened for scanning so that the
153 : * "cursor" is positioned before the first qualifying tuple.
154 : * ----------------------------------------------------------------
155 : */
156 : TupleTableSlot *
157 72200408 : ExecScan(ScanState *node,
158 : ExecScanAccessMtd accessMtd, /* function returning a tuple */
159 : ExecScanRecheckMtd recheckMtd)
160 : {
161 : ExprContext *econtext;
162 : ExprState *qual;
163 : ProjectionInfo *projInfo;
164 :
165 : /*
166 : * Fetch data from node
167 : */
168 72200408 : qual = node->ps.qual;
169 72200408 : projInfo = node->ps.ps_ProjInfo;
170 72200408 : econtext = node->ps.ps_ExprContext;
171 :
172 : /* interrupt checks are in ExecScanFetch */
173 :
174 : /*
175 : * If we have neither a qual to check nor a projection to do, just skip
176 : * all the overhead and return the raw scan tuple.
177 : */
178 72200408 : if (!qual && !projInfo)
179 : {
180 22465530 : ResetExprContext(econtext);
181 22465530 : return ExecScanFetch(node, accessMtd, recheckMtd);
182 : }
183 :
184 : /*
185 : * Reset per-tuple memory context to free any expression evaluation
186 : * storage allocated in the previous tuple cycle.
187 : */
188 49734878 : ResetExprContext(econtext);
189 :
190 : /*
191 : * get a tuple from the access method. Loop until we obtain a tuple that
192 : * passes the qualification.
193 : */
194 : for (;;)
195 23016918 : {
196 : TupleTableSlot *slot;
197 :
198 72751796 : slot = ExecScanFetch(node, accessMtd, recheckMtd);
199 :
200 : /*
201 : * if the slot returned by the accessMtd contains NULL, then it means
202 : * there is nothing more to scan so we just return an empty slot,
203 : * being careful to use the projection result slot so it has correct
204 : * tupleDesc.
205 : */
206 72751314 : if (TupIsNull(slot))
207 : {
208 1460204 : if (projInfo)
209 1351560 : return ExecClearTuple(projInfo->pi_state.resultslot);
210 : else
211 108644 : return slot;
212 : }
213 :
214 : /*
215 : * place the current tuple into the expr context
216 : */
217 71291110 : econtext->ecxt_scantuple = slot;
218 :
219 : /*
220 : * check that the current tuple satisfies the qual-clause
221 : *
222 : * check for non-null qual here to avoid a function call to ExecQual()
223 : * when the qual is null ... saves only a few cycles, but they add up
224 : * ...
225 : */
226 71291110 : if (qual == NULL || ExecQual(qual, econtext))
227 : {
228 : /*
229 : * Found a satisfactory scan tuple.
230 : */
231 48274158 : if (projInfo)
232 : {
233 : /*
234 : * Form a projection tuple, store it in the result tuple slot
235 : * and return it.
236 : */
237 43063416 : return ExecProject(projInfo);
238 : }
239 : else
240 : {
241 : /*
242 : * Here, we aren't projecting, so just return scan tuple.
243 : */
244 5210742 : return slot;
245 : }
246 : }
247 : else
248 23016918 : InstrCountFiltered1(node, 1);
249 :
250 : /*
251 : * Tuple fails qual, so free per-tuple memory and try again.
252 : */
253 23016918 : ResetExprContext(econtext);
254 : }
255 : }
256 :
257 : /*
258 : * ExecAssignScanProjectionInfo
259 : * Set up projection info for a scan node, if necessary.
260 : *
261 : * We can avoid a projection step if the requested tlist exactly matches
262 : * the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
263 : * Note that this case occurs not only for simple "SELECT * FROM ...", but
264 : * also in most cases where there are joins or other processing nodes above
265 : * the scan node, because the planner will preferentially generate a matching
266 : * tlist.
267 : *
268 : * The scan slot's descriptor must have been set already.
269 : */
270 : void
271 422354 : ExecAssignScanProjectionInfo(ScanState *node)
272 : {
273 422354 : Scan *scan = (Scan *) node->ps.plan;
274 422354 : TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
275 :
276 422354 : ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
277 422354 : }
278 :
279 : /*
280 : * ExecAssignScanProjectionInfoWithVarno
281 : * As above, but caller can specify varno expected in Vars in the tlist.
282 : */
283 : void
284 16672 : ExecAssignScanProjectionInfoWithVarno(ScanState *node, int varno)
285 : {
286 16672 : TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
287 :
288 16672 : ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, varno);
289 16672 : }
290 :
291 : /*
292 : * ExecScanReScan
293 : *
294 : * This must be called within the ReScan function of any plan node type
295 : * that uses ExecScan().
296 : */
297 : void
298 1548476 : ExecScanReScan(ScanState *node)
299 : {
300 1548476 : EState *estate = node->ps.state;
301 :
302 : /*
303 : * We must clear the scan tuple so that observers (e.g., execCurrent.c)
304 : * can tell that this plan node is not positioned on a tuple.
305 : */
306 1548476 : ExecClearTuple(node->ss_ScanTupleSlot);
307 :
308 : /*
309 : * Rescan EvalPlanQual tuple(s) if we're inside an EvalPlanQual recheck.
310 : * But don't lose the "blocked" status of blocked target relations.
311 : */
312 1548476 : if (estate->es_epq_active != NULL)
313 : {
314 356 : EPQState *epqstate = estate->es_epq_active;
315 356 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
316 :
317 356 : if (scanrelid > 0)
318 356 : epqstate->relsubs_done[scanrelid - 1] =
319 356 : epqstate->relsubs_blocked[scanrelid - 1];
320 : else
321 : {
322 : Bitmapset *relids;
323 0 : int rtindex = -1;
324 :
325 : /*
326 : * If an FDW or custom scan provider has replaced the join with a
327 : * scan, there are multiple RTIs; reset the epqScanDone flag for
328 : * all of them.
329 : */
330 0 : if (IsA(node->ps.plan, ForeignScan))
331 0 : relids = ((ForeignScan *) node->ps.plan)->fs_base_relids;
332 0 : else if (IsA(node->ps.plan, CustomScan))
333 0 : relids = ((CustomScan *) node->ps.plan)->custom_relids;
334 : else
335 0 : elog(ERROR, "unexpected scan node: %d",
336 : (int) nodeTag(node->ps.plan));
337 :
338 0 : while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
339 : {
340 : Assert(rtindex > 0);
341 0 : epqstate->relsubs_done[rtindex - 1] =
342 0 : epqstate->relsubs_blocked[rtindex - 1];
343 : }
344 : }
345 : }
346 1548476 : }
|