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-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 *
228 amitlan@postgresql.o 32 :CBC 59502064 : ExecScanFetch(ScanState *node,
33 : : EPQState *epqstate,
34 : : ExecScanAccessMtd accessMtd,
35 : : ExecScanRecheckMtd recheckMtd)
36 : : {
37 [ + + ]: 59502064 : CHECK_FOR_INTERRUPTS();
38 : :
39 [ + + ]: 59502064 : 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 : 345 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
47 : :
48 [ - + ]: 345 : if (scanrelid == 0)
49 : : {
50 : : /*
51 : : * This is a ForeignScan or CustomScan which has pushed down a
52 : : * join to the remote side. The recheck method is responsible not
53 : : * only for rechecking the scan/join quals but also for storing
54 : : * the correct tuple in the slot.
55 : : */
56 : :
228 amitlan@postgresql.o 57 :UBC 0 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
58 : :
59 [ # # ]: 0 : if (!(*recheckMtd) (node, slot))
60 : 0 : ExecClearTuple(slot); /* would not be returned by scan */
61 : 0 : return slot;
62 : : }
228 amitlan@postgresql.o 63 [ + + ]:CBC 345 : else if (epqstate->relsubs_done[scanrelid - 1])
64 : : {
65 : : /*
66 : : * Return empty slot, as either there is no EPQ tuple for this rel
67 : : * or we already returned it.
68 : : */
69 : :
70 : 157 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
71 : :
72 : 157 : return ExecClearTuple(slot);
73 : : }
74 [ + + ]: 188 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
75 : : {
76 : : /*
77 : : * Return replacement tuple provided by the EPQ caller.
78 : : */
79 : :
80 : 164 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
81 : :
82 [ - + ]: 164 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
83 : :
84 : : /* Mark to remember that we shouldn't return it again */
85 : 164 : epqstate->relsubs_done[scanrelid - 1] = true;
86 : :
87 : : /* Return empty slot if we haven't got a test tuple */
88 [ + - + + ]: 164 : if (TupIsNull(slot))
89 : 3 : return NULL;
90 : :
91 : : /* Check if it meets the access-method conditions */
92 [ + + ]: 161 : if (!(*recheckMtd) (node, slot))
93 : 4 : return ExecClearTuple(slot); /* would not be returned by
94 : : * scan */
95 : 157 : return slot;
96 : : }
97 [ + + ]: 24 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
98 : : {
99 : : /*
100 : : * Fetch and return replacement tuple using a non-locking rowmark.
101 : : */
102 : :
103 : 13 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
104 : :
105 : : /* Mark to remember that we shouldn't return more */
106 : 13 : epqstate->relsubs_done[scanrelid - 1] = true;
107 : :
108 [ - + ]: 13 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
228 amitlan@postgresql.o 109 :UBC 0 : return NULL;
110 : :
111 : : /* Return empty slot if we haven't got a test tuple */
228 amitlan@postgresql.o 112 [ + - - + ]:CBC 13 : if (TupIsNull(slot))
228 amitlan@postgresql.o 113 :UBC 0 : return NULL;
114 : :
115 : : /* Check if it meets the access-method conditions */
228 amitlan@postgresql.o 116 [ - + ]:CBC 13 : if (!(*recheckMtd) (node, slot))
228 amitlan@postgresql.o 117 :UBC 0 : return ExecClearTuple(slot); /* would not be returned by
118 : : * scan */
228 amitlan@postgresql.o 119 :CBC 13 : return slot;
120 : : }
121 : : }
122 : :
123 : : /*
124 : : * Run the node-type-specific access method function to get the next tuple
125 : : */
126 : 59501730 : return (*accessMtd) (node);
127 : : }
128 : :
129 : : /* ----------------------------------------------------------------
130 : : * ExecScanExtended
131 : : * Scans the relation using the specified 'access method' and returns the
132 : : * next tuple. Optionally checks the tuple against 'qual' and applies
133 : : * 'projInfo' if provided.
134 : : *
135 : : * The 'recheck method' validates an arbitrary tuple of the relation against
136 : : * conditions enforced by the access method.
137 : : *
138 : : * This function is an alternative to ExecScan, used when callers may omit
139 : : * 'qual' or 'projInfo'. The pg_attribute_always_inline attribute allows the
140 : : * compiler to eliminate non-relevant branches at compile time, avoiding
141 : : * run-time checks in those cases.
142 : : *
143 : : * Conditions:
144 : : * -- The AMI "cursor" is positioned at the previously returned tuple.
145 : : *
146 : : * Initial States:
147 : : * -- The relation is opened for scanning, with the "cursor"
148 : : * positioned before the first qualifying tuple.
149 : : * ----------------------------------------------------------------
150 : : */
151 : : static pg_attribute_always_inline TupleTableSlot *
152 : 41125131 : ExecScanExtended(ScanState *node,
153 : : ExecScanAccessMtd accessMtd, /* function returning a tuple */
154 : : ExecScanRecheckMtd recheckMtd,
155 : : EPQState *epqstate,
156 : : ExprState *qual,
157 : : ProjectionInfo *projInfo)
158 : : {
159 : 41125131 : ExprContext *econtext = node->ps.ps_ExprContext;
160 : :
161 : : /* interrupt checks are in ExecScanFetch */
162 : :
163 : : /*
164 : : * If we have neither a qual to check nor a projection to do, just skip
165 : : * all the overhead and return the raw scan tuple.
166 : : */
167 [ + + + + ]: 41125131 : if (!qual && !projInfo)
168 : : {
169 : 13163667 : ResetExprContext(econtext);
170 : 13163667 : return ExecScanFetch(node, epqstate, accessMtd, recheckMtd);
171 : : }
172 : :
173 : : /*
174 : : * Reset per-tuple memory context to free any expression evaluation
175 : : * storage allocated in the previous tuple cycle.
176 : : */
177 : 27961464 : ResetExprContext(econtext);
178 : :
179 : : /*
180 : : * get a tuple from the access method. Loop until we obtain a tuple that
181 : : * passes the qualification.
182 : : */
183 : : for (;;)
184 : 18376933 : {
185 : : TupleTableSlot *slot;
186 : :
187 : 46338397 : slot = ExecScanFetch(node, epqstate, accessMtd, recheckMtd);
188 : :
189 : : /*
190 : : * if the slot returned by the accessMtd contains NULL, then it means
191 : : * there is nothing more to scan so we just return an empty slot,
192 : : * being careful to use the projection result slot so it has correct
193 : : * tupleDesc.
194 : : */
195 [ + + + + ]: 46338129 : if (TupIsNull(slot))
196 : : {
197 [ + + ]: 982228 : if (projInfo)
198 : 910017 : return ExecClearTuple(projInfo->pi_state.resultslot);
199 : : else
200 : 72211 : return slot;
201 : : }
202 : :
203 : : /*
204 : : * place the current tuple into the expr context
205 : : */
206 : 45355901 : econtext->ecxt_scantuple = slot;
207 : :
208 : : /*
209 : : * check that the current tuple satisfies the qual-clause
210 : : *
211 : : * check for non-null qual here to avoid a function call to ExecQual()
212 : : * when the qual is null ... saves only a few cycles, but they add up
213 : : * ...
214 : : */
215 [ + + + + ]: 45355901 : if (qual == NULL || ExecQual(qual, econtext))
216 : : {
217 : : /*
218 : : * Found a satisfactory scan tuple.
219 : : */
220 [ + + ]: 26978952 : if (projInfo)
221 : : {
222 : : /*
223 : : * Form a projection tuple, store it in the result tuple slot
224 : : * and return it.
225 : : */
226 : 23462916 : return ExecProject(projInfo);
227 : : }
228 : : else
229 : : {
230 : : /*
231 : : * Here, we aren't projecting, so just return scan tuple.
232 : : */
233 : 3516036 : return slot;
234 : : }
235 : : }
236 : : else
237 [ + + ]: 18376933 : InstrCountFiltered1(node, 1);
238 : :
239 : : /*
240 : : * Tuple fails qual, so free per-tuple memory and try again.
241 : : */
242 : 18376933 : ResetExprContext(econtext);
243 : : }
244 : : }
245 : :
246 : : #endif /* EXECSCAN_H */
|