1 /*-------------------------------------------------------------------------
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
10 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
15 * src/backend/executor/execScan.c
17 *-------------------------------------------------------------------------
21 #include "executor/executor.h"
22 #include "miscadmin.h"
27 * ExecScanFetch -- check interrupts & fetch next potential tuple
29 * This routine is concerned with substituting a test tuple if we are
30 * inside an EvalPlanQual recheck. If we aren't, just execute
31 * the access method's next-tuple routine.
33 static inline TupleTableSlot
*
34 ExecScanFetch(ScanState
*node
,
35 ExecScanAccessMtd accessMtd
,
36 ExecScanRecheckMtd recheckMtd
)
38 EState
*estate
= node
->ps
.state
;
40 CHECK_FOR_INTERRUPTS();
42 if (estate
->es_epq_active
!= NULL
)
44 EPQState
*epqstate
= estate
->es_epq_active
;
47 * We are inside an EvalPlanQual recheck. Return the test tuple if
48 * one is available, after rechecking any access-method-specific
51 Index scanrelid
= ((Scan
*) node
->ps
.plan
)->scanrelid
;
56 * This is a ForeignScan or CustomScan which has pushed down a
57 * join to the remote side. The recheck method is responsible not
58 * only for rechecking the scan/join quals but also for storing
59 * the correct tuple in the slot.
62 TupleTableSlot
*slot
= node
->ss_ScanTupleSlot
;
64 if (!(*recheckMtd
) (node
, slot
))
65 ExecClearTuple(slot
); /* would not be returned by scan */
68 else if (epqstate
->relsubs_done
[scanrelid
- 1])
71 * Return empty slot, as either there is no EPQ tuple for this rel
72 * or we already returned it.
75 TupleTableSlot
*slot
= node
->ss_ScanTupleSlot
;
77 return ExecClearTuple(slot
);
79 else if (epqstate
->relsubs_slot
[scanrelid
- 1] != NULL
)
82 * Return replacement tuple provided by the EPQ caller.
85 TupleTableSlot
*slot
= epqstate
->relsubs_slot
[scanrelid
- 1];
87 Assert(epqstate
->relsubs_rowmark
[scanrelid
- 1] == NULL
);
89 /* Mark to remember that we shouldn't return it again */
90 epqstate
->relsubs_done
[scanrelid
- 1] = true;
92 /* Return empty slot if we haven't got a test tuple */
96 /* Check if it meets the access-method conditions */
97 if (!(*recheckMtd
) (node
, slot
))
98 return ExecClearTuple(slot
); /* would not be returned by
102 else if (epqstate
->relsubs_rowmark
[scanrelid
- 1] != NULL
)
105 * Fetch and return replacement tuple using a non-locking rowmark.
108 TupleTableSlot
*slot
= node
->ss_ScanTupleSlot
;
110 /* Mark to remember that we shouldn't return more */
111 epqstate
->relsubs_done
[scanrelid
- 1] = true;
113 if (!EvalPlanQualFetchRowMark(epqstate
, scanrelid
, slot
))
116 /* Return empty slot if we haven't got a test tuple */
120 /* Check if it meets the access-method conditions */
121 if (!(*recheckMtd
) (node
, slot
))
122 return ExecClearTuple(slot
); /* would not be returned by
129 * Run the node-type-specific access method function to get the next tuple
131 return (*accessMtd
) (node
);
134 /* ----------------------------------------------------------------
137 * Scans the relation using the 'access method' indicated and
138 * returns the next qualifying tuple.
139 * The access method returns the next tuple and ExecScan() is
140 * responsible for checking the tuple returned against the qual-clause.
142 * A 'recheck method' must also be provided that can check an
143 * arbitrary tuple of the relation against any qual conditions
144 * that are implemented internal to the access method.
147 * -- the "cursor" maintained by the AMI is positioned at the tuple
148 * returned previously.
151 * -- the relation indicated is opened for scanning so that the
152 * "cursor" is positioned before the first qualifying tuple.
153 * ----------------------------------------------------------------
156 ExecScan(ScanState
*node
,
157 ExecScanAccessMtd accessMtd
, /* function returning a tuple */
158 ExecScanRecheckMtd recheckMtd
)
160 ExprContext
*econtext
;
162 ProjectionInfo
*projInfo
;
165 * Fetch data from node
167 qual
= node
->ps
.qual
;
168 projInfo
= node
->ps
.ps_ProjInfo
;
169 econtext
= node
->ps
.ps_ExprContext
;
171 /* interrupt checks are in ExecScanFetch */
174 * If we have neither a qual to check nor a projection to do, just skip
175 * all the overhead and return the raw scan tuple.
177 if (!qual
&& !projInfo
)
179 ResetExprContext(econtext
);
180 return ExecScanFetch(node
, accessMtd
, recheckMtd
);
184 * Reset per-tuple memory context to free any expression evaluation
185 * storage allocated in the previous tuple cycle.
187 ResetExprContext(econtext
);
190 * get a tuple from the access method. Loop until we obtain a tuple that
191 * passes the qualification.
195 TupleTableSlot
*slot
;
197 slot
= ExecScanFetch(node
, accessMtd
, recheckMtd
);
200 * if the slot returned by the accessMtd contains NULL, then it means
201 * there is nothing more to scan so we just return an empty slot,
202 * being careful to use the projection result slot so it has correct
208 return ExecClearTuple(projInfo
->pi_state
.resultslot
);
214 * place the current tuple into the expr context
216 econtext
->ecxt_scantuple
= slot
;
219 * check that the current tuple satisfies the qual-clause
221 * check for non-null qual here to avoid a function call to ExecQual()
222 * when the qual is null ... saves only a few cycles, but they add up
225 if (qual
== NULL
|| ExecQual(qual
, econtext
))
228 * Found a satisfactory scan tuple.
233 * Form a projection tuple, store it in the result tuple slot
236 return ExecProject(projInfo
);
241 * Here, we aren't projecting, so just return scan tuple.
247 InstrCountFiltered1(node
, 1);
250 * Tuple fails qual, so free per-tuple memory and try again.
252 ResetExprContext(econtext
);
257 * ExecAssignScanProjectionInfo
258 * Set up projection info for a scan node, if necessary.
260 * We can avoid a projection step if the requested tlist exactly matches
261 * the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
262 * Note that this case occurs not only for simple "SELECT * FROM ...", but
263 * also in most cases where there are joins or other processing nodes above
264 * the scan node, because the planner will preferentially generate a matching
267 * The scan slot's descriptor must have been set already.
270 ExecAssignScanProjectionInfo(ScanState
*node
)
272 Scan
*scan
= (Scan
*) node
->ps
.plan
;
273 TupleDesc tupdesc
= node
->ss_ScanTupleSlot
->tts_tupleDescriptor
;
275 ExecConditionalAssignProjectionInfo(&node
->ps
, tupdesc
, scan
->scanrelid
);
279 * ExecAssignScanProjectionInfoWithVarno
280 * As above, but caller can specify varno expected in Vars in the tlist.
283 ExecAssignScanProjectionInfoWithVarno(ScanState
*node
, int varno
)
285 TupleDesc tupdesc
= node
->ss_ScanTupleSlot
->tts_tupleDescriptor
;
287 ExecConditionalAssignProjectionInfo(&node
->ps
, tupdesc
, varno
);
293 * This must be called within the ReScan function of any plan node type
294 * that uses ExecScan().
297 ExecScanReScan(ScanState
*node
)
299 EState
*estate
= node
->ps
.state
;
302 * We must clear the scan tuple so that observers (e.g., execCurrent.c)
303 * can tell that this plan node is not positioned on a tuple.
305 ExecClearTuple(node
->ss_ScanTupleSlot
);
308 * Rescan EvalPlanQual tuple(s) if we're inside an EvalPlanQual recheck.
309 * But don't lose the "blocked" status of blocked target relations.
311 if (estate
->es_epq_active
!= NULL
)
313 EPQState
*epqstate
= estate
->es_epq_active
;
314 Index scanrelid
= ((Scan
*) node
->ps
.plan
)->scanrelid
;
317 epqstate
->relsubs_done
[scanrelid
- 1] =
318 epqstate
->relsubs_blocked
[scanrelid
- 1];
325 * If an FDW or custom scan provider has replaced the join with a
326 * scan, there are multiple RTIs; reset the epqScanDone flag for
329 if (IsA(node
->ps
.plan
, ForeignScan
))
330 relids
= ((ForeignScan
*) node
->ps
.plan
)->fs_base_relids
;
331 else if (IsA(node
->ps
.plan
, CustomScan
))
332 relids
= ((CustomScan
*) node
->ps
.plan
)->custom_relids
;
334 elog(ERROR
, "unexpected scan node: %d",
335 (int) nodeTag(node
->ps
.plan
));
337 while ((rtindex
= bms_next_member(relids
, rtindex
)) >= 0)
340 epqstate
->relsubs_done
[rtindex
- 1] =
341 epqstate
->relsubs_blocked
[rtindex
- 1];