Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/include/executor/execScan.h
Line
Count
Source (jump to first uncovered line)
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 *
32
ExecScanFetch(ScanState *node,
33
        EPQState *epqstate,
34
        ExecScanAccessMtd accessMtd,
35
        ExecScanRecheckMtd recheckMtd)
36
0
{
37
0
  CHECK_FOR_INTERRUPTS();
38
39
0
  if (epqstate != NULL)
40
0
  {
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
0
    Index   scanrelid = ((Scan *) node->ps.plan)->scanrelid;
47
48
0
    if (scanrelid == 0)
49
0
    {
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
57
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
0
    }
63
0
    else if (epqstate->relsubs_done[scanrelid - 1])
64
0
    {
65
      /*
66
       * Return empty slot, as either there is no EPQ tuple for this rel
67
       * or we already returned it.
68
       */
69
70
0
      TupleTableSlot *slot = node->ss_ScanTupleSlot;
71
72
0
      return ExecClearTuple(slot);
73
0
    }
74
0
    else if (epqstate->relsubs_slot[scanrelid - 1] != NULL)
75
0
    {
76
      /*
77
       * Return replacement tuple provided by the EPQ caller.
78
       */
79
80
0
      TupleTableSlot *slot = epqstate->relsubs_slot[scanrelid - 1];
81
82
0
      Assert(epqstate->relsubs_rowmark[scanrelid - 1] == NULL);
83
84
      /* Mark to remember that we shouldn't return it again */
85
0
      epqstate->relsubs_done[scanrelid - 1] = true;
86
87
      /* Return empty slot if we haven't got a test tuple */
88
0
      if (TupIsNull(slot))
89
0
        return NULL;
90
91
      /* Check if it meets the access-method conditions */
92
0
      if (!(*recheckMtd) (node, slot))
93
0
        return ExecClearTuple(slot); /* would not be returned by
94
                         * scan */
95
0
      return slot;
96
0
    }
97
0
    else if (epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
98
0
    {
99
      /*
100
       * Fetch and return replacement tuple using a non-locking rowmark.
101
       */
102
103
0
      TupleTableSlot *slot = node->ss_ScanTupleSlot;
104
105
      /* Mark to remember that we shouldn't return more */
106
0
      epqstate->relsubs_done[scanrelid - 1] = true;
107
108
0
      if (!EvalPlanQualFetchRowMark(epqstate, scanrelid, slot))
109
0
        return NULL;
110
111
      /* Return empty slot if we haven't got a test tuple */
112
0
      if (TupIsNull(slot))
113
0
        return NULL;
114
115
      /* Check if it meets the access-method conditions */
116
0
      if (!(*recheckMtd) (node, slot))
117
0
        return ExecClearTuple(slot); /* would not be returned by
118
                         * scan */
119
0
      return slot;
120
0
    }
121
0
  }
122
123
  /*
124
   * Run the node-type-specific access method function to get the next tuple
125
   */
126
0
  return (*accessMtd) (node);
127
0
}
Unexecuted instantiation: execScan.c:ExecScanFetch
Unexecuted instantiation: nodeSeqscan.c:ExecScanFetch
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
ExecScanExtended(ScanState *node,
153
         ExecScanAccessMtd accessMtd, /* function returning a tuple */
154
         ExecScanRecheckMtd recheckMtd,
155
         EPQState *epqstate,
156
         ExprState *qual,
157
         ProjectionInfo *projInfo)
158
0
{
159
0
  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
0
  if (!qual && !projInfo)
168
0
  {
169
0
    ResetExprContext(econtext);
170
0
    return ExecScanFetch(node, epqstate, accessMtd, recheckMtd);
171
0
  }
172
173
  /*
174
   * Reset per-tuple memory context to free any expression evaluation
175
   * storage allocated in the previous tuple cycle.
176
   */
177
0
  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
0
  for (;;)
184
0
  {
185
0
    TupleTableSlot *slot;
186
187
0
    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
0
    if (TupIsNull(slot))
196
0
    {
197
0
      if (projInfo)
198
0
        return ExecClearTuple(projInfo->pi_state.resultslot);
199
0
      else
200
0
        return slot;
201
0
    }
202
203
    /*
204
     * place the current tuple into the expr context
205
     */
206
0
    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
0
    if (qual == NULL || ExecQual(qual, econtext))
216
0
    {
217
      /*
218
       * Found a satisfactory scan tuple.
219
       */
220
0
      if (projInfo)
221
0
      {
222
        /*
223
         * Form a projection tuple, store it in the result tuple slot
224
         * and return it.
225
         */
226
0
        return ExecProject(projInfo);
227
0
      }
228
0
      else
229
0
      {
230
        /*
231
         * Here, we aren't projecting, so just return scan tuple.
232
         */
233
0
        return slot;
234
0
      }
235
0
    }
236
0
    else
237
0
      InstrCountFiltered1(node, 1);
238
239
    /*
240
     * Tuple fails qual, so free per-tuple memory and try again.
241
     */
242
0
    ResetExprContext(econtext);
243
0
  }
244
0
}
Unexecuted instantiation: execScan.c:ExecScanExtended
Unexecuted instantiation: nodeSeqscan.c:ExecScanExtended
245
246
#endif              /* EXECSCAN_H */