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