Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeSeqscan.c
4 : : * Support routines for sequential scans of relations.
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/executor/nodeSeqscan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : /*
16 : : * INTERFACE ROUTINES
17 : : * ExecSeqScan sequentially scans a relation.
18 : : * ExecSeqNext retrieve next tuple in sequential order.
19 : : * ExecInitSeqScan creates and initializes a seqscan node.
20 : : * ExecEndSeqScan releases any storage allocated.
21 : : * ExecReScanSeqScan rescans the relation
22 : : *
23 : : * ExecSeqScanEstimate estimates DSM space needed for parallel scan
24 : : * ExecSeqScanInitializeDSM initialize DSM for parallel scan
25 : : * ExecSeqScanReInitializeDSM reinitialize DSM for fresh parallel scan
26 : : * ExecSeqScanInitializeWorker attach to DSM info in parallel worker
27 : : */
28 : : #include "postgres.h"
29 : :
30 : : #include "access/relscan.h"
31 : : #include "access/tableam.h"
32 : : #include "executor/execScan.h"
33 : : #include "executor/executor.h"
34 : : #include "executor/nodeSeqscan.h"
35 : : #include "utils/rel.h"
36 : :
37 : : static TupleTableSlot *SeqNext(SeqScanState *node);
38 : :
39 : : /* ----------------------------------------------------------------
40 : : * Scan Support
41 : : * ----------------------------------------------------------------
42 : : */
43 : :
44 : : /* ----------------------------------------------------------------
45 : : * SeqNext
46 : : *
47 : : * This is a workhorse for ExecSeqScan
48 : : * ----------------------------------------------------------------
49 : : */
50 : : static TupleTableSlot *
8065 bruce@momjian.us 51 :CBC 42971652 : SeqNext(SeqScanState *node)
52 : : {
53 : : TableScanDesc scandesc;
54 : : EState *estate;
55 : : ScanDirection direction;
56 : : TupleTableSlot *slot;
57 : :
58 : : /*
59 : : * get information from the estate and scan state
60 : : */
3587 rhaas@postgresql.org 61 : 42971652 : scandesc = node->ss.ss_currentScanDesc;
62 : 42971652 : estate = node->ss.ps.state;
10226 bruce@momjian.us 63 : 42971652 : direction = estate->es_direction;
3587 rhaas@postgresql.org 64 : 42971652 : slot = node->ss.ss_ScanTupleSlot;
65 : :
66 [ + + ]: 42971652 : if (scandesc == NULL)
67 : : {
68 : : /*
69 : : * We reach here if the scan is not parallel, or if we're serially
70 : : * executing a scan that was planned to be parallel.
71 : : */
2371 andres@anarazel.de 72 : 91533 : scandesc = table_beginscan(node->ss.ss_currentRelation,
73 : : estate->es_snapshot,
74 : : 0, NULL);
3587 rhaas@postgresql.org 75 : 91533 : node->ss.ss_currentScanDesc = scandesc;
76 : : }
77 : :
78 : : /*
79 : : * get the next tuple from the table
80 : : */
2371 andres@anarazel.de 81 [ + + ]: 42971652 : if (table_scan_getnextslot(scandesc, direction, slot))
82 : 42280585 : return slot;
83 : 691044 : return NULL;
84 : : }
85 : :
86 : : /*
87 : : * SeqRecheck -- access method routine to recheck a tuple in EvalPlanQual
88 : : */
89 : : static bool
5794 tgl@sss.pgh.pa.us 90 : 120 : SeqRecheck(SeqScanState *node, TupleTableSlot *slot)
91 : : {
92 : : /*
93 : : * Note that unlike IndexScan, SeqScan never use keys in heap_beginscan
94 : : * (and this is very bad) - so, here we do not check are keys ok or not.
95 : : */
96 : 120 : return true;
97 : : }
98 : :
99 : : /* ----------------------------------------------------------------
100 : : * ExecSeqScan(node)
101 : : *
102 : : * Scans the relation sequentially and returns the next qualifying
103 : : * tuple. This variant is used when there is no es_epq_active, no qual
104 : : * and no projection. Passing const-NULLs for these to ExecScanExtended
105 : : * allows the compiler to eliminate the additional code that would
106 : : * ordinarily be required for the evaluation of these.
107 : : * ----------------------------------------------------------------
108 : : */
109 : : static TupleTableSlot *
2973 andres@anarazel.de 110 : 7040162 : ExecSeqScan(PlanState *pstate)
111 : : {
112 : 7040162 : SeqScanState *node = castNode(SeqScanState, pstate);
113 : :
228 amitlan@postgresql.o 114 [ - + ]: 7040162 : Assert(pstate->state->es_epq_active == NULL);
115 [ - + ]: 7040162 : Assert(pstate->qual == NULL);
116 [ - + ]: 7040162 : Assert(pstate->ps_ProjInfo == NULL);
117 : :
118 : 7040162 : return ExecScanExtended(&node->ss,
119 : : (ExecScanAccessMtd) SeqNext,
120 : : (ExecScanRecheckMtd) SeqRecheck,
121 : : NULL,
122 : : NULL,
123 : : NULL);
124 : : }
125 : :
126 : : /*
127 : : * Variant of ExecSeqScan() but when qual evaluation is required.
128 : : */
129 : : static TupleTableSlot *
130 : 3327987 : ExecSeqScanWithQual(PlanState *pstate)
131 : : {
132 : 3327987 : SeqScanState *node = castNode(SeqScanState, pstate);
133 : :
134 : : /*
135 : : * Use pg_assume() for != NULL tests to make the compiler realize no
136 : : * runtime check for the field is needed in ExecScanExtended().
137 : : */
138 [ - + ]: 3327987 : Assert(pstate->state->es_epq_active == NULL);
26 andres@anarazel.de 139 [ - + ]:GNC 3327987 : pg_assume(pstate->qual != NULL);
228 amitlan@postgresql.o 140 [ - + ]:CBC 3327987 : Assert(pstate->ps_ProjInfo == NULL);
141 : :
142 : 3327987 : return ExecScanExtended(&node->ss,
143 : : (ExecScanAccessMtd) SeqNext,
144 : : (ExecScanRecheckMtd) SeqRecheck,
145 : : NULL,
146 : : pstate->qual,
147 : : NULL);
148 : : }
149 : :
150 : : /*
151 : : * Variant of ExecSeqScan() but when projection is required.
152 : : */
153 : : static TupleTableSlot *
154 : 11976396 : ExecSeqScanWithProject(PlanState *pstate)
155 : : {
156 : 11976396 : SeqScanState *node = castNode(SeqScanState, pstate);
157 : :
158 [ - + ]: 11976396 : Assert(pstate->state->es_epq_active == NULL);
159 [ - + ]: 11976396 : Assert(pstate->qual == NULL);
26 andres@anarazel.de 160 [ - + ]:GNC 11976396 : pg_assume(pstate->ps_ProjInfo != NULL);
161 : :
228 amitlan@postgresql.o 162 :CBC 11976396 : return ExecScanExtended(&node->ss,
163 : : (ExecScanAccessMtd) SeqNext,
164 : : (ExecScanRecheckMtd) SeqRecheck,
165 : : NULL,
166 : : NULL,
167 : : pstate->ps_ProjInfo);
168 : : }
169 : :
170 : : /*
171 : : * Variant of ExecSeqScan() but when qual evaluation and projection are
172 : : * required.
173 : : */
174 : : static TupleTableSlot *
175 : 3699902 : ExecSeqScanWithQualProject(PlanState *pstate)
176 : : {
177 : 3699902 : SeqScanState *node = castNode(SeqScanState, pstate);
178 : :
179 [ - + ]: 3699902 : Assert(pstate->state->es_epq_active == NULL);
26 andres@anarazel.de 180 [ - + ]:GNC 3699902 : pg_assume(pstate->qual != NULL);
181 [ - + ]: 3699902 : pg_assume(pstate->ps_ProjInfo != NULL);
182 : :
228 amitlan@postgresql.o 183 :CBC 3699902 : return ExecScanExtended(&node->ss,
184 : : (ExecScanAccessMtd) SeqNext,
185 : : (ExecScanRecheckMtd) SeqRecheck,
186 : : NULL,
187 : : pstate->qual,
188 : : pstate->ps_ProjInfo);
189 : : }
190 : :
191 : : /*
192 : : * Variant of ExecSeqScan for when EPQ evaluation is required. We don't
193 : : * bother adding variants of this for with/without qual and projection as
194 : : * EPQ doesn't seem as exciting a case to optimize for.
195 : : */
196 : : static TupleTableSlot *
197 : 247 : ExecSeqScanEPQ(PlanState *pstate)
198 : : {
199 : 247 : SeqScanState *node = castNode(SeqScanState, pstate);
200 : :
2973 andres@anarazel.de 201 : 247 : return ExecScan(&node->ss,
202 : : (ExecScanAccessMtd) SeqNext,
203 : : (ExecScanRecheckMtd) SeqRecheck);
204 : : }
205 : :
206 : : /* ----------------------------------------------------------------
207 : : * ExecInitSeqScan
208 : : * ----------------------------------------------------------------
209 : : */
210 : : SeqScanState *
7130 tgl@sss.pgh.pa.us 211 : 112353 : ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
212 : : {
213 : : SeqScanState *scanstate;
214 : :
215 : : /*
216 : : * Once upon a time it was possible to have an outerPlan of a SeqScan, but
217 : : * not any more.
218 : : */
8311 219 [ - + ]: 112353 : Assert(outerPlan(node) == NULL);
220 [ - + ]: 112353 : Assert(innerPlan(node) == NULL);
221 : :
222 : : /*
223 : : * create state structure
224 : : */
225 : 112353 : scanstate = makeNode(SeqScanState);
3587 rhaas@postgresql.org 226 : 112353 : scanstate->ss.ps.plan = (Plan *) node;
227 : 112353 : scanstate->ss.ps.state = estate;
228 : :
229 : : /*
230 : : * Miscellaneous initialization
231 : : *
232 : : * create expression context for node
233 : : */
234 : 112353 : ExecAssignExprContext(estate, &scanstate->ss.ps);
235 : :
236 : : /*
237 : : * open the scan relation
238 : : */
2759 andres@anarazel.de 239 : 112347 : scanstate->ss.ss_currentRelation =
240 : 112353 : ExecOpenScanRelation(estate,
241 : : node->scan.scanrelid,
242 : : eflags);
243 : :
244 : : /* and create slot with the appropriate rowtype */
245 : 112347 : ExecInitScanTupleSlot(estate, &scanstate->ss,
2487 246 : 112347 : RelationGetDescr(scanstate->ss.ss_currentRelation),
247 : : table_slot_callbacks(scanstate->ss.ss_currentRelation));
248 : :
249 : : /*
250 : : * Initialize result type and projection.
251 : : */
2493 252 : 112347 : ExecInitResultTypeTL(&scanstate->ss.ps);
2759 253 : 112347 : ExecAssignScanProjectionInfo(&scanstate->ss);
254 : :
255 : : /*
256 : : * initialize child expressions
257 : : */
258 : 112347 : scanstate->ss.ps.qual =
1490 peter@eisentraut.org 259 : 112347 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
260 : :
261 : : /*
262 : : * When EvalPlanQual() is not in use, assign ExecProcNode for this node
263 : : * based on the presence of qual and projection. Each ExecSeqScan*()
264 : : * variant is optimized for the specific combination of these conditions.
265 : : */
228 amitlan@postgresql.o 266 [ + + ]: 112347 : if (scanstate->ss.ps.state->es_epq_active != NULL)
267 : 132 : scanstate->ss.ps.ExecProcNode = ExecSeqScanEPQ;
268 [ + + ]: 112215 : else if (scanstate->ss.ps.qual == NULL)
269 : : {
270 [ + + ]: 56470 : if (scanstate->ss.ps.ps_ProjInfo == NULL)
271 : 27115 : scanstate->ss.ps.ExecProcNode = ExecSeqScan;
272 : : else
273 : 29355 : scanstate->ss.ps.ExecProcNode = ExecSeqScanWithProject;
274 : : }
275 : : else
276 : : {
277 [ + + ]: 55745 : if (scanstate->ss.ps.ps_ProjInfo == NULL)
278 : 26500 : scanstate->ss.ps.ExecProcNode = ExecSeqScanWithQual;
279 : : else
280 : 29245 : scanstate->ss.ps.ExecProcNode = ExecSeqScanWithQualProject;
281 : : }
282 : :
8311 tgl@sss.pgh.pa.us 283 : 112347 : return scanstate;
284 : : }
285 : :
286 : : /* ----------------------------------------------------------------
287 : : * ExecEndSeqScan
288 : : *
289 : : * frees any storage allocated through C routines.
290 : : * ----------------------------------------------------------------
291 : : */
292 : : void
8065 bruce@momjian.us 293 : 111030 : ExecEndSeqScan(SeqScanState *node)
294 : : {
295 : : TableScanDesc scanDesc;
296 : :
297 : : /*
298 : : * get information from node
299 : : */
3587 rhaas@postgresql.org 300 : 111030 : scanDesc = node->ss.ss_currentScanDesc;
301 : :
302 : : /*
303 : : * close heap scan
304 : : */
305 [ + + ]: 111030 : if (scanDesc != NULL)
2371 andres@anarazel.de 306 : 92250 : table_endscan(scanDesc);
10651 scrappy@hub.org 307 : 111030 : }
308 : :
309 : : /* ----------------------------------------------------------------
310 : : * Join Support
311 : : * ----------------------------------------------------------------
312 : : */
313 : :
314 : : /* ----------------------------------------------------------------
315 : : * ExecReScanSeqScan
316 : : *
317 : : * Rescans the relation.
318 : : * ----------------------------------------------------------------
319 : : */
320 : : void
5535 tgl@sss.pgh.pa.us 321 : 621851 : ExecReScanSeqScan(SeqScanState *node)
322 : : {
323 : : TableScanDesc scan;
324 : :
3587 rhaas@postgresql.org 325 : 621851 : scan = node->ss.ss_currentScanDesc;
326 : :
327 [ + + ]: 621851 : if (scan != NULL)
2299 tgl@sss.pgh.pa.us 328 : 609059 : table_rescan(scan, /* scan desc */
329 : : NULL); /* new scan keys */
330 : :
5794 331 : 621851 : ExecScanReScan((ScanState *) node);
10651 scrappy@hub.org 332 : 621851 : }
333 : :
334 : : /* ----------------------------------------------------------------
335 : : * Parallel Scan Support
336 : : * ----------------------------------------------------------------
337 : : */
338 : :
339 : : /* ----------------------------------------------------------------
340 : : * ExecSeqScanEstimate
341 : : *
342 : : * Compute the amount of space we'll need in the parallel
343 : : * query DSM, and inform pcxt->estimator about our needs.
344 : : * ----------------------------------------------------------------
345 : : */
346 : : void
3587 rhaas@postgresql.org 347 : 454 : ExecSeqScanEstimate(SeqScanState *node,
348 : : ParallelContext *pcxt)
349 : : {
350 : 454 : EState *estate = node->ss.ps.state;
351 : :
2371 andres@anarazel.de 352 : 454 : node->pscan_len = table_parallelscan_estimate(node->ss.ss_currentRelation,
353 : : estate->es_snapshot);
3587 rhaas@postgresql.org 354 : 454 : shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
355 : 454 : shm_toc_estimate_keys(&pcxt->estimator, 1);
356 : 454 : }
357 : :
358 : : /* ----------------------------------------------------------------
359 : : * ExecSeqScanInitializeDSM
360 : : *
361 : : * Set up a parallel heap scan descriptor.
362 : : * ----------------------------------------------------------------
363 : : */
364 : : void
365 : 454 : ExecSeqScanInitializeDSM(SeqScanState *node,
366 : : ParallelContext *pcxt)
367 : : {
368 : 454 : EState *estate = node->ss.ps.state;
369 : : ParallelTableScanDesc pscan;
370 : :
371 : 454 : pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
2371 andres@anarazel.de 372 : 454 : table_parallelscan_initialize(node->ss.ss_currentRelation,
373 : : pscan,
374 : : estate->es_snapshot);
3587 rhaas@postgresql.org 375 : 454 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
376 : 454 : node->ss.ss_currentScanDesc =
2371 andres@anarazel.de 377 : 454 : table_beginscan_parallel(node->ss.ss_currentRelation, pscan);
3587 rhaas@postgresql.org 378 : 454 : }
379 : :
380 : : /* ----------------------------------------------------------------
381 : : * ExecSeqScanReInitializeDSM
382 : : *
383 : : * Reset shared state before beginning a fresh scan.
384 : : * ----------------------------------------------------------------
385 : : */
386 : : void
2929 tgl@sss.pgh.pa.us 387 : 114 : ExecSeqScanReInitializeDSM(SeqScanState *node,
388 : : ParallelContext *pcxt)
389 : : {
390 : : ParallelTableScanDesc pscan;
391 : :
2371 andres@anarazel.de 392 : 114 : pscan = node->ss.ss_currentScanDesc->rs_parallel;
393 : 114 : table_parallelscan_reinitialize(node->ss.ss_currentRelation, pscan);
2929 tgl@sss.pgh.pa.us 394 : 114 : }
395 : :
396 : : /* ----------------------------------------------------------------
397 : : * ExecSeqScanInitializeWorker
398 : : *
399 : : * Copy relevant information from TOC into planstate.
400 : : * ----------------------------------------------------------------
401 : : */
402 : : void
2851 andres@anarazel.de 403 : 1347 : ExecSeqScanInitializeWorker(SeqScanState *node,
404 : : ParallelWorkerContext *pwcxt)
405 : : {
406 : : ParallelTableScanDesc pscan;
407 : :
408 : 1347 : pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
3587 rhaas@postgresql.org 409 : 1347 : node->ss.ss_currentScanDesc =
2371 andres@anarazel.de 410 : 1347 : table_beginscan_parallel(node->ss.ss_currentRelation, pscan);
3587 rhaas@postgresql.org 411 : 1347 : }
|