Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execScan.c
4 : : * This code provides support for generalized relation scans. ExecScan
5 : : * is passed a node and a pointer to a function to "do the right thing"
6 : : * and return a tuple from the relation. ExecScan then does the tedious
7 : : * stuff - checking the qualification and projecting the tuple
8 : : * appropriately.
9 : : *
10 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
11 : : * Portions Copyright (c) 1994, Regents of the University of California
12 : : *
13 : : *
14 : : * IDENTIFICATION
15 : : * src/backend/executor/execScan.c
16 : : *
17 : : *-------------------------------------------------------------------------
18 : : */
19 : : #include "postgres.h"
20 : :
21 : : #include "executor/executor.h"
22 : : #include "miscadmin.h"
23 : :
24 : :
25 : :
26 : : /*
27 : : * ExecScanFetch -- check interrupts & fetch next potential tuple
28 : : *
29 : : * This routine is concerned with substituting a test tuple if we are
30 : : * inside an EvalPlanQual recheck. If we aren't, just execute
31 : : * the access method's next-tuple routine.
32 : : */
33 : : static inline TupleTableSlot *
5284 tgl@sss.pgh.pa.us 34 :CBC 50321442 : ExecScanFetch(ScanState *node,
35 : : ExecScanAccessMtd accessMtd,
36 : : ExecScanRecheckMtd recheckMtd)
37 : : {
38 : 50321442 : EState *estate = node->ps.state;
39 : :
2404 andres@anarazel.de 40 [ + + ]: 50321442 : CHECK_FOR_INTERRUPTS();
41 : :
1683 42 [ + + ]: 50321442 : if (estate->es_epq_active != NULL)
43 : : {
44 : 340 : EPQState *epqstate = estate->es_epq_active;
45 : :
46 : : /*
47 : : * We are inside an EvalPlanQual recheck. Return the test tuple if
48 : : * one is available, after rechecking any access-method-specific
49 : : * conditions.
50 : : */
5284 tgl@sss.pgh.pa.us 51 : 340 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
52 : :
3050 rhaas@postgresql.org 53 [ - + ]: 340 : if (scanrelid == 0)
54 : : {
55 : : /*
56 : : * This is a ForeignScan or CustomScan which has pushed down a
57 : : * join to the remote side. The recheck method is responsible not
58 : : * only for rechecking the scan/join quals but also for storing
59 : : * the correct tuple in the slot.
60 : : */
61 : :
1683 andres@anarazel.de 62 :UBC 0 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
63 : :
3050 rhaas@postgresql.org 64 [ # # ]: 0 : if (!(*recheckMtd) (node, slot))
65 : 0 : ExecClearTuple(slot); /* would not be returned by scan */
66 : 0 : return slot;
67 : : }
1683 andres@anarazel.de 68 [ + + ]:CBC 340 : else if (epqstate->relsubs_done[scanrelid - 1])
69 : : {
70 : : /*
71 : : * Return empty slot, as either there is no EPQ tuple for this rel
72 : : * or we already returned it.
73 : : */
74 : :
5284 tgl@sss.pgh.pa.us 75 : 158 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
76 : :
1683 andres@anarazel.de 77 : 158 : return ExecClearTuple(slot);
78 : : }
79 [ + + ]: 182 : else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
80 : : {
81 : : /*
82 : : * Return replacement tuple provided by the EPQ caller.
83 : : */
84 : :
85 : 160 : TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
86 : :
87 [ - + ]: 160 : Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
88 : :
89 : : /* Mark to remember that we shouldn't return it again */
90 : 160 : epqstate->relsubs_done[scanrelid - 1] = true;
91 : :
92 : : /* Return empty slot if we haven't got a test tuple */
1871 93 [ + - + + ]: 160 : if (TupIsNull(slot))
94 : 3 : return NULL;
95 : :
96 : : /* Check if it meets the access-method conditions */
5284 tgl@sss.pgh.pa.us 97 [ + + ]: 157 : if (!(*recheckMtd) (node, slot))
1789 98 : 4 : return ExecClearTuple(slot); /* would not be returned by
99 : : * scan */
1683 andres@anarazel.de 100 : 153 : return slot;
101 : : }
102 [ + + ]: 22 : else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
103 : : {
104 : : /*
105 : : * Fetch and return replacement tuple using a non-locking rowmark.
106 : : */
107 : :
108 : 17 : TupleTableSlot *slot = node->ss_ScanTupleSlot;
109 : :
110 : : /* Mark to remember that we shouldn't return more */
111 : 17 : epqstate->relsubs_done[scanrelid - 1] = true;
112 : :
113 [ - + ]: 17 : if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
1683 andres@anarazel.de 114 :UBC 0 : return NULL;
115 : :
116 : : /* Return empty slot if we haven't got a test tuple */
1683 andres@anarazel.de 117 [ + - - + ]:CBC 17 : if (TupIsNull(slot))
1683 andres@anarazel.de 118 :UBC 0 : return NULL;
119 : :
120 : : /* Check if it meets the access-method conditions */
1683 andres@anarazel.de 121 [ - + ]:CBC 17 : if (!(*recheckMtd) (node, slot))
1683 andres@anarazel.de 122 :UBC 0 : return ExecClearTuple(slot); /* would not be returned by
123 : : * scan */
5284 tgl@sss.pgh.pa.us 124 :CBC 17 : return slot;
125 : : }
126 : : }
127 : :
128 : : /*
129 : : * Run the node-type-specific access method function to get the next tuple
130 : : */
131 : 50321107 : return (*accessMtd) (node);
132 : : }
133 : :
134 : : /* ----------------------------------------------------------------
135 : : * ExecScan
136 : : *
137 : : * Scans the relation using the 'access method' indicated and
138 : : * returns the next qualifying tuple.
139 : : * The access method returns the next tuple and ExecScan() is
140 : : * responsible for checking the tuple returned against the qual-clause.
141 : : *
142 : : * A 'recheck method' must also be provided that can check an
143 : : * arbitrary tuple of the relation against any qual conditions
144 : : * that are implemented internal to the access method.
145 : : *
146 : : * Conditions:
147 : : * -- the "cursor" maintained by the AMI is positioned at the tuple
148 : : * returned previously.
149 : : *
150 : : * Initial States:
151 : : * -- the relation indicated is opened for scanning so that the
152 : : * "cursor" is positioned before the first qualifying tuple.
153 : : * ----------------------------------------------------------------
154 : : */
155 : : TupleTableSlot *
7555 bruce@momjian.us 156 : 38398944 : ExecScan(ScanState *node,
157 : : ExecScanAccessMtd accessMtd, /* function returning a tuple */
158 : : ExecScanRecheckMtd recheckMtd)
159 : : {
160 : : ExprContext *econtext;
161 : : ExprState *qual;
162 : : ProjectionInfo *projInfo;
163 : :
164 : : /*
165 : : * Fetch data from node
166 : : */
7801 tgl@sss.pgh.pa.us 167 : 38398944 : qual = node->ps.qual;
7741 168 : 38398944 : projInfo = node->ps.ps_ProjInfo;
4712 heikki.linnakangas@i 169 : 38398944 : 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 : : */
6902 tgl@sss.pgh.pa.us 177 [ + + + + ]: 38398944 : if (!qual && !projInfo)
178 : : {
4712 heikki.linnakangas@i 179 : 11920491 : ResetExprContext(econtext);
5284 tgl@sss.pgh.pa.us 180 : 11920491 : return ExecScanFetch(node, 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 : : */
8634 187 : 26478453 : 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 (;;)
9716 bruce@momjian.us 194 : 11922498 : {
195 : : TupleTableSlot *slot;
196 : :
5284 tgl@sss.pgh.pa.us 197 : 38400951 : slot = ExecScanFetch(node, 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 : : */
9716 bruce@momjian.us 205 [ + + + + ]: 38400697 : if (TupIsNull(slot))
206 : : {
7741 tgl@sss.pgh.pa.us 207 [ + + ]: 794302 : if (projInfo)
2588 andres@anarazel.de 208 : 732166 : return ExecClearTuple(projInfo->pi_state.resultslot);
209 : : else
7741 tgl@sss.pgh.pa.us 210 : 62136 : return slot;
211 : : }
212 : :
213 : : /*
214 : : * place the current tuple into the expr context
215 : : */
9716 bruce@momjian.us 216 : 37606395 : 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 : : */
2588 andres@anarazel.de 225 [ + + + + ]: 37606395 : if (qual == NULL || ExecQual(qual, econtext))
226 : : {
227 : : /*
228 : : * Found a satisfactory scan tuple.
229 : : */
7741 tgl@sss.pgh.pa.us 230 [ + + ]: 25683869 : if (projInfo)
231 : : {
232 : : /*
233 : : * Form a projection tuple, store it in the result tuple slot
234 : : * and return it.
235 : : */
2642 andres@anarazel.de 236 : 22924309 : return ExecProject(projInfo);
237 : : }
238 : : else
239 : : {
240 : : /*
241 : : * Here, we aren't projecting, so just return scan tuple.
242 : : */
7741 tgl@sss.pgh.pa.us 243 : 2759560 : return slot;
244 : : }
245 : : }
246 : : else
4588 247 [ + + ]: 11922498 : InstrCountFiltered1(node, 1);
248 : :
249 : : /*
250 : : * Tuple fails qual, so free per-tuple memory and try again.
251 : : */
8677 252 : 11922498 : ResetExprContext(econtext);
253 : : }
254 : : }
255 : :
256 : : /*
257 : : * ExecAssignScanProjectionInfo
258 : : * Set up projection info for a scan node, if necessary.
259 : : *
260 : : * We can avoid a projection step if the requested tlist exactly matches
261 : : * the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
262 : : * Note that this case occurs not only for simple "SELECT * FROM ...", but
263 : : * also in most cases where there are joins or other processing nodes above
264 : : * the scan node, because the planner will preferentially generate a matching
265 : : * tlist.
266 : : *
267 : : * The scan slot's descriptor must have been set already.
268 : : */
269 : : void
7555 bruce@momjian.us 270 : 225687 : ExecAssignScanProjectionInfo(ScanState *node)
271 : : {
7559 272 : 225687 : Scan *scan = (Scan *) node->ps.plan;
2332 rhaas@postgresql.org 273 : 225687 : TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
274 : :
275 : 225687 : ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
3262 tgl@sss.pgh.pa.us 276 : 225687 : }
277 : :
278 : : /*
279 : : * ExecAssignScanProjectionInfoWithVarno
280 : : * As above, but caller can specify varno expected in Vars in the tlist.
281 : : */
282 : : void
942 283 : 7945 : ExecAssignScanProjectionInfoWithVarno(ScanState *node, int varno)
284 : : {
2332 rhaas@postgresql.org 285 : 7945 : TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
286 : :
287 : 7945 : ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, varno);
7741 tgl@sss.pgh.pa.us 288 : 7945 : }
289 : :
290 : : /*
291 : : * ExecScanReScan
292 : : *
293 : : * This must be called within the ReScan function of any plan node type
294 : : * that uses ExecScan().
295 : : */
296 : : void
5284 297 : 840088 : ExecScanReScan(ScanState *node)
298 : : {
299 : 840088 : EState *estate = node->ps.state;
300 : :
301 : : /*
302 : : * We must clear the scan tuple so that observers (e.g., execCurrent.c)
303 : : * can tell that this plan node is not positioned on a tuple.
304 : : */
2030 305 : 840088 : ExecClearTuple(node->ss_ScanTupleSlot);
306 : :
307 : : /*
308 : : * Rescan EvalPlanQual tuple(s) if we're inside an EvalPlanQual recheck.
309 : : * But don't lose the "blocked" status of blocked target relations.
310 : : */
1683 andres@anarazel.de 311 [ + + ]: 840088 : if (estate->es_epq_active != NULL)
312 : : {
313 : 178 : EPQState *epqstate = estate->es_epq_active;
5284 tgl@sss.pgh.pa.us 314 : 178 : Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;
315 : :
3050 rhaas@postgresql.org 316 [ + - ]: 178 : if (scanrelid > 0)
331 tgl@sss.pgh.pa.us 317 : 178 : epqstate->relsubs_done[scanrelid - 1] =
318 : 178 : epqstate->relsubs_blocked[scanrelid - 1];
319 : : else
320 : : {
321 : : Bitmapset *relids;
3050 rhaas@postgresql.org 322 :UBC 0 : int rtindex = -1;
323 : :
324 : : /*
325 : : * If an FDW or custom scan provider has replaced the join with a
326 : : * scan, there are multiple RTIs; reset the epqScanDone flag for
327 : : * all of them.
328 : : */
329 [ # # ]: 0 : if (IsA(node->ps.plan, ForeignScan))
440 tgl@sss.pgh.pa.us 330 : 0 : relids = ((ForeignScan *) node->ps.plan)->fs_base_relids;
3050 rhaas@postgresql.org 331 [ # # ]: 0 : else if (IsA(node->ps.plan, CustomScan))
332 : 0 : relids = ((CustomScan *) node->ps.plan)->custom_relids;
333 : : else
334 [ # # ]: 0 : elog(ERROR, "unexpected scan node: %d",
335 : : (int) nodeTag(node->ps.plan));
336 : :
337 [ # # ]: 0 : while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
338 : : {
339 [ # # ]: 0 : Assert(rtindex > 0);
331 tgl@sss.pgh.pa.us 340 : 0 : epqstate->relsubs_done[rtindex - 1] =
341 : 0 : epqstate->relsubs_blocked[rtindex - 1];
342 : : }
343 : : }
344 : : }
5284 tgl@sss.pgh.pa.us 345 :CBC 840088 : }
|