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 *
279 amitlan@postgresql.o 32 :CBC 59881011 : ExecScanFetch(ScanState *node,
33 : : EPQState *epqstate,
34 : : ExecScanAccessMtd accessMtd,
35 : : ExecScanRecheckMtd recheckMtd)
36 : : {
37 [ + + ]: 59881011 : CHECK_FOR_INTERRUPTS();
38 : :
39 [ + + ]: 59881011 : 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 : 357 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
47 : :
48 [ + + ]: 357 : 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 : : */
12 efujita@postgresql.o 56 [ - + ]: 1 : 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 : :
12 efujita@postgresql.o 64 :UBC 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 : : }
279 amitlan@postgresql.o 71 [ + + ]:CBC 356 : 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 : 157 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
79 : :
80 : 157 : return ExecClearTuple(slot);
81 : : }
82 [ + + ]: 199 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
83 : : {
84 : : /*
85 : : * Return replacement tuple provided by the EPQ caller.
86 : : */
87 : :
88 : 175 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
89 : :
90 [ - + ]: 175 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
91 : :
92 : : /* Mark to remember that we shouldn't return it again */
93 : 175 : epqstate->relsubs_done[scanrelid - 1] = true;
94 : :
95 : : /* Return empty slot if we haven't got a test tuple */
96 [ + - + + ]: 175 : if (TupIsNull(slot))
97 : 3 : return NULL;
98 : :
99 : : /* Check if it meets the access-method conditions */
100 [ + + ]: 172 : if (!(*recheckMtd) (node, slot))
101 : 6 : return ExecClearTuple(slot); /* would not be returned by
102 : : * scan */
103 : 166 : return slot;
104 : : }
105 [ + + ]: 24 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
106 : : {
107 : : /*
108 : : * Fetch and return replacement tuple using a non-locking rowmark.
109 : : */
110 : :
111 : 13 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
112 : :
113 : : /* Mark to remember that we shouldn't return more */
114 : 13 : epqstate->relsubs_done[scanrelid - 1] = true;
115 : :
116 [ - + ]: 13 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
279 amitlan@postgresql.o 117 :UBC 0 : return NULL;
118 : :
119 : : /* Return empty slot if we haven't got a test tuple */
279 amitlan@postgresql.o 120 [ + - - + ]:CBC 13 : if (TupIsNull(slot))
279 amitlan@postgresql.o 121 :UBC 0 : return NULL;
122 : :
123 : : /* Check if it meets the access-method conditions */
279 amitlan@postgresql.o 124 [ - + ]:CBC 13 : if (!(*recheckMtd) (node, slot))
279 amitlan@postgresql.o 125 :UBC 0 : return ExecClearTuple(slot); /* would not be returned by
126 : : * scan */
279 amitlan@postgresql.o 127 :CBC 13 : return slot;
128 : : }
129 : : }
130 : :
131 : : /*
132 : : * Run the node-type-specific access method function to get the next tuple
133 : : */
134 : 59880666 : 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 : 41440722 : ExecScanExtended(ScanState *node,
161 : : ExecScanAccessMtd accessMtd, /* function returning a tuple */
162 : : ExecScanRecheckMtd recheckMtd,
163 : : EPQState *epqstate,
164 : : ExprState *qual,
165 : : ProjectionInfo *projInfo)
166 : : {
167 : 41440722 : 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 [ + + + + ]: 41440722 : if (!qual && !projInfo)
176 : : {
177 : 13271850 : ResetExprContext(econtext);
178 : 13271850 : 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 : 28168872 : 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 : 18440289 : {
193 : : TupleTableSlot *slot;
194 : :
195 : 46609161 : 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 [ + + + + ]: 46608887 : if (TupIsNull(slot))
204 : : {
205 [ + + ]: 1001912 : if (projInfo)
206 : 928229 : return ExecClearTuple(projInfo->pi_state.resultslot);
207 : : else
208 : 73683 : return slot;
209 : : }
210 : :
211 : : /*
212 : : * place the current tuple into the expr context
213 : : */
214 : 45606975 : 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 [ + + + + ]: 45606975 : if (qual == NULL || ExecQual(qual, econtext))
224 : : {
225 : : /*
226 : : * Found a satisfactory scan tuple.
227 : : */
228 [ + + ]: 27166670 : if (projInfo)
229 : : {
230 : : /*
231 : : * Form a projection tuple, store it in the result tuple slot
232 : : * and return it.
233 : : */
234 : 23662823 : return ExecProject(projInfo);
235 : : }
236 : : else
237 : : {
238 : : /*
239 : : * Here, we aren't projecting, so just return scan tuple.
240 : : */
241 : 3503847 : return slot;
242 : : }
243 : : }
244 : : else
245 [ + + ]: 18440289 : InstrCountFiltered1(node, 1);
246 : :
247 : : /*
248 : : * Tuple fails qual, so free per-tuple memory and try again.
249 : : */
250 : 18440289 : ResetExprContext(econtext);
251 : : }
252 : : }
253 : :
254 : : #endif /* EXECSCAN_H */
|