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_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 *
469 amitlan@postgresql.o 33 :CBC 84698289 : ExecScanFetch(ScanState *node,
34 : : EPQState *epqstate,
35 : : ExecScanAccessMtd accessMtd,
36 : : ExecScanRecheckMtd recheckMtd)
37 : : {
38 [ + + ]: 84698289 : CHECK_FOR_INTERRUPTS();
39 : :
40 [ + + ]: 84698289 : 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 : 391 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
48 : :
49 [ + + ]: 391 : 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 : : */
202 efujita@postgresql.o 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 : : }
469 amitlan@postgresql.o 72 [ + + ]: 388 : 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 : 167 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
80 : :
81 : 167 : return ExecClearTuple(slot);
82 : : }
83 [ + + ]: 221 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
84 : : {
85 : : /*
86 : : * Return replacement tuple provided by the EPQ caller.
87 : : */
88 : :
89 : 194 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
90 : :
91 [ - + ]: 194 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
92 : :
93 : : /* Mark to remember that we shouldn't return it again */
94 : 194 : epqstate->relsubs_done[scanrelid - 1] = true;
95 : :
96 : : /* Return empty slot if we haven't got a test tuple */
97 [ + - + + ]: 194 : if (TupIsNull(slot))
98 : 3 : return NULL;
99 : :
100 : : /* Check if it meets the access-method conditions */
101 [ + + ]: 191 : if (!(*recheckMtd) (node, slot))
102 : 10 : return ExecClearTuple(slot); /* would not be returned by
103 : : * scan */
104 : 181 : 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))
469 amitlan@postgresql.o 118 :UBC 0 : return NULL;
119 : :
120 : : /* Return empty slot if we haven't got a test tuple */
469 amitlan@postgresql.o 121 [ + - - + ]:CBC 22 : if (TupIsNull(slot))
469 amitlan@postgresql.o 122 :UBC 0 : return NULL;
123 : :
124 : : /* Check if it meets the access-method conditions */
469 amitlan@postgresql.o 125 [ + + ]:CBC 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 : 84697904 : 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 : 58710527 : ExecScanExtended(ScanState *node,
162 : : ExecScanAccessMtd accessMtd, /* function returning a tuple */
163 : : ExecScanRecheckMtd recheckMtd,
164 : : EPQState *epqstate,
165 : : ExprState *qual,
166 : : ProjectionInfo *projInfo)
167 : : {
168 : 58710527 : 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 [ + + + + ]: 58710527 : if (!qual && !projInfo)
177 : : {
178 : 18473171 : ResetExprContext(econtext);
179 : 18473171 : 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 : 40237356 : 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 : 25987762 : {
194 : : TupleTableSlot *slot;
195 : :
196 : 66225118 : 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 [ + + + + ]: 66224770 : if (TupIsNull(slot))
205 : : {
206 [ + + ]: 1383216 : if (projInfo)
207 : 1284916 : return ExecClearTuple(projInfo->pi_state.resultslot);
208 : : else
209 : 98300 : return slot;
210 : : }
211 : :
212 : : /*
213 : : * place the current tuple into the expr context
214 : : */
215 : 64841554 : 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 [ + + + + ]: 64841554 : if (qual == NULL || ExecQual(qual, econtext))
225 : : {
226 : : /*
227 : : * Found a satisfactory scan tuple.
228 : : */
229 [ + + ]: 38853772 : if (projInfo)
230 : : {
231 : : /*
232 : : * Form a projection tuple, store it in the result tuple slot
233 : : * and return it.
234 : : */
235 : 33826635 : return ExecProject(projInfo);
236 : : }
237 : : else
238 : : {
239 : : /*
240 : : * Here, we aren't projecting, so just return scan tuple.
241 : : */
242 : 5027137 : return slot;
243 : : }
244 : : }
245 : : else
246 [ + + ]: 25987762 : InstrCountFiltered1(node, 1);
247 : :
248 : : /*
249 : : * Tuple fails qual, so free per-tuple memory and try again.
250 : : */
251 : 25987762 : ResetExprContext(econtext);
252 : : }
253 : : }
254 : :
255 : : #endif /* EXECSCAN_H */
|