Line data Source code
1 : /*-------------------------------------------------------------------------
2 : * execScan.h
3 : * Inline-able support functions for Scan nodes
4 : *
5 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
6 : * Portions Copyright (c) 1994, Regents of the University of California
7 : *
8 : * IDENTIFICATION
9 : * src/include/executor/execScan.h
10 : *-------------------------------------------------------------------------
11 : */
12 :
13 : #ifndef EXECSCAN_H
14 : #define EXECSCAN_H
15 :
16 : #include "miscadmin.h"
17 : #include "executor/executor.h"
18 : #include "executor/instrument.h"
19 : #include "nodes/execnodes.h"
20 :
21 : /*
22 : * ExecScanFetch -- check interrupts & fetch next potential tuple
23 : *
24 : * This routine substitutes a test tuple if inside an EvalPlanQual recheck.
25 : * Otherwise, it simply executes the access method's next-tuple routine.
26 : *
27 : * The pg_attribute_always_inline attribute allows the compiler to inline
28 : * this function into its caller. When EPQState is NULL, the EvalPlanQual
29 : * logic is completely eliminated at compile time, avoiding unnecessary
30 : * run-time checks and code for cases where EPQ is not required.
31 : */
32 : static pg_attribute_always_inline TupleTableSlot *
33 84472602 : ExecScanFetch(ScanState *node,
34 : EPQState *epqstate,
35 : ExecScanAccessMtd accessMtd,
36 : ExecScanRecheckMtd recheckMtd)
37 : {
38 84472602 : CHECK_FOR_INTERRUPTS();
39 :
40 84472602 : if (epqstate != NULL)
41 : {
42 : /*
43 : * We are inside an EvalPlanQual recheck. Return the test tuple if
44 : * one is available, after rechecking any access-method-specific
45 : * conditions.
46 : */
47 376 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
48 :
49 376 : if (scanrelid == 0)
50 : {
51 : /*
52 : * This is a ForeignScan or CustomScan which has pushed down a
53 : * join to the remote side. If it is a descendant node in the EPQ
54 : * recheck plan tree, run the recheck method function. Otherwise,
55 : * run the access method function below.
56 : */
57 3 : if (bms_is_member(epqstate->epqParam, node->ps.plan->extParam))
58 : {
59 : /*
60 : * The recheck method is responsible not only for rechecking
61 : * the scan/join quals but also for storing the correct tuple
62 : * in the slot.
63 : */
64 :
65 2 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
66 :
67 2 : if (!(*recheckMtd) (node, slot))
68 1 : ExecClearTuple(slot); /* would not be returned by scan */
69 2 : return slot;
70 : }
71 : }
72 373 : else if (epqstate->relsubs_done[scanrelid - 1])
73 : {
74 : /*
75 : * Return empty slot, as either there is no EPQ tuple for this rel
76 : * or we already returned it.
77 : */
78 :
79 163 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
80 :
81 163 : return ExecClearTuple(slot);
82 : }
83 210 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
84 : {
85 : /*
86 : * Return replacement tuple provided by the EPQ caller.
87 : */
88 :
89 183 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
90 :
91 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
92 :
93 : /* Mark to remember that we shouldn't return it again */
94 183 : epqstate->relsubs_done[scanrelid - 1] = true;
95 :
96 : /* Return empty slot if we haven't got a test tuple */
97 183 : if (TupIsNull(slot))
98 3 : return NULL;
99 :
100 : /* Check if it meets the access-method conditions */
101 180 : if (!(*recheckMtd) (node, slot))
102 6 : return ExecClearTuple(slot); /* would not be returned by
103 : * scan */
104 174 : return slot;
105 : }
106 27 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
107 : {
108 : /*
109 : * Fetch and return replacement tuple using a non-locking rowmark.
110 : */
111 :
112 22 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
113 :
114 : /* Mark to remember that we shouldn't return more */
115 22 : epqstate->relsubs_done[scanrelid - 1] = true;
116 :
117 22 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
118 0 : return NULL;
119 :
120 : /* Return empty slot if we haven't got a test tuple */
121 22 : if (TupIsNull(slot))
122 0 : return NULL;
123 :
124 : /* Check if it meets the access-method conditions */
125 22 : if (!(*recheckMtd) (node, slot))
126 1 : return ExecClearTuple(slot); /* would not be returned by
127 : * scan */
128 21 : return slot;
129 : }
130 : }
131 :
132 : /*
133 : * Run the node-type-specific access method function to get the next tuple
134 : */
135 84472232 : return (*accessMtd) (node);
136 : }
137 :
138 : /* ----------------------------------------------------------------
139 : * ExecScanExtended
140 : * Scans the relation using the specified 'access method' and returns the
141 : * next tuple. Optionally checks the tuple against 'qual' and applies
142 : * 'projInfo' if provided.
143 : *
144 : * The 'recheck method' validates an arbitrary tuple of the relation against
145 : * conditions enforced by the access method.
146 : *
147 : * This function is an alternative to ExecScan, used when callers may omit
148 : * 'qual' or 'projInfo'. The pg_attribute_always_inline attribute allows the
149 : * compiler to eliminate non-relevant branches at compile time, avoiding
150 : * run-time checks in those cases.
151 : *
152 : * Conditions:
153 : * -- The AMI "cursor" is positioned at the previously returned tuple.
154 : *
155 : * Initial States:
156 : * -- The relation is opened for scanning, with the "cursor"
157 : * positioned before the first qualifying tuple.
158 : * ----------------------------------------------------------------
159 : */
160 : static pg_attribute_always_inline TupleTableSlot *
161 54526278 : ExecScanExtended(ScanState *node,
162 : ExecScanAccessMtd accessMtd, /* function returning a tuple */
163 : ExecScanRecheckMtd recheckMtd,
164 : EPQState *epqstate,
165 : ExprState *qual,
166 : ProjectionInfo *projInfo)
167 : {
168 54526278 : ExprContext *econtext = node->ps.ps_ExprContext;
169 :
170 : /* interrupt checks are in ExecScanFetch */
171 :
172 : /*
173 : * If we have neither a qual to check nor a projection to do, just skip
174 : * all the overhead and return the raw scan tuple.
175 : */
176 54526278 : if (!qual && !projInfo)
177 : {
178 16638322 : ResetExprContext(econtext);
179 16638322 : return ExecScanFetch(node, epqstate, accessMtd, recheckMtd);
180 : }
181 :
182 : /*
183 : * Reset per-tuple memory context to free any expression evaluation
184 : * storage allocated in the previous tuple cycle.
185 : */
186 37887956 : ResetExprContext(econtext);
187 :
188 : /*
189 : * get a tuple from the access method. Loop until we obtain a tuple that
190 : * passes the qualification.
191 : */
192 : for (;;)
193 29946324 : {
194 : TupleTableSlot *slot;
195 :
196 67834280 : slot = ExecScanFetch(node, epqstate, accessMtd, recheckMtd);
197 :
198 : /*
199 : * if the slot returned by the accessMtd contains NULL, then it means
200 : * there is nothing more to scan so we just return an empty slot,
201 : * being careful to use the projection result slot so it has correct
202 : * tupleDesc.
203 : */
204 67833937 : if (TupIsNull(slot))
205 : {
206 1376069 : if (projInfo)
207 1277393 : return ExecClearTuple(projInfo->pi_state.resultslot);
208 : else
209 98676 : return slot;
210 : }
211 :
212 : /*
213 : * place the current tuple into the expr context
214 : */
215 66457868 : econtext->ecxt_scantuple = slot;
216 :
217 : /*
218 : * check that the current tuple satisfies the qual-clause
219 : *
220 : * check for non-null qual here to avoid a function call to ExecQual()
221 : * when the qual is null ... saves only a few cycles, but they add up
222 : * ...
223 : */
224 66457868 : if (qual == NULL || ExecQual(qual, econtext))
225 : {
226 : /*
227 : * Found a satisfactory scan tuple.
228 : */
229 36511523 : if (projInfo)
230 : {
231 : /*
232 : * Form a projection tuple, store it in the result tuple slot
233 : * and return it.
234 : */
235 31640752 : return ExecProject(projInfo);
236 : }
237 : else
238 : {
239 : /*
240 : * Here, we aren't projecting, so just return scan tuple.
241 : */
242 4870771 : return slot;
243 : }
244 : }
245 : else
246 29946324 : InstrCountFiltered1(node, 1);
247 :
248 : /*
249 : * Tuple fails qual, so free per-tuple memory and try again.
250 : */
251 29946324 : ResetExprContext(econtext);
252 : }
253 : }
254 :
255 : #endif /* EXECSCAN_H */
|