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