Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/executor/execJunk.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * execJunk.c
4
 *    Junk attribute support stuff....
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/execJunk.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include "executor/executor.h"
18
19
/*-------------------------------------------------------------------------
20
 *    XXX this stuff should be rewritten to take advantage
21
 *      of ExecProject() and the ProjectionInfo node.
22
 *      -cim 6/3/91
23
 *
24
 * An attribute of a tuple living inside the executor, can be
25
 * either a normal attribute or a "junk" attribute. "junk" attributes
26
 * never make it out of the executor, i.e. they are never printed,
27
 * returned or stored on disk. Their only purpose in life is to
28
 * store some information useful only to the executor, mainly the values
29
 * of system attributes like "ctid", or sort key columns that are not to
30
 * be output.
31
 *
32
 * The general idea is the following: A target list consists of a list of
33
 * TargetEntry nodes containing expressions. Each TargetEntry has a field
34
 * called 'resjunk'. If the value of this field is true then the
35
 * corresponding attribute is a "junk" attribute.
36
 *
37
 * When we initialize a plan we call ExecInitJunkFilter to create a filter.
38
 *
39
 * We then execute the plan, treating the resjunk attributes like any others.
40
 *
41
 * Finally, when at the top level we get back a tuple, we can call
42
 * ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
43
 * junk attributes we are interested in, and ExecFilterJunk to remove all the
44
 * junk attributes from a tuple.  This new "clean" tuple is then printed,
45
 * inserted, or updated.
46
 *
47
 *-------------------------------------------------------------------------
48
 */
49
50
/*
51
 * ExecInitJunkFilter
52
 *
53
 * Initialize the Junk filter.
54
 *
55
 * The source targetlist is passed in.  The output tuple descriptor is
56
 * built from the non-junk tlist entries.
57
 * An optional resultSlot can be passed as well; otherwise, we create one.
58
 */
59
JunkFilter *
60
ExecInitJunkFilter(List *targetList, TupleTableSlot *slot)
61
0
{
62
0
  JunkFilter *junkfilter;
63
0
  TupleDesc cleanTupType;
64
0
  int     cleanLength;
65
0
  AttrNumber *cleanMap;
66
67
  /*
68
   * Compute the tuple descriptor for the cleaned tuple.
69
   */
70
0
  cleanTupType = ExecCleanTypeFromTL(targetList);
71
72
  /*
73
   * Use the given slot, or make a new slot if we weren't given one.
74
   */
75
0
  if (slot)
76
0
    ExecSetSlotDescriptor(slot, cleanTupType);
77
0
  else
78
0
    slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual);
79
80
  /*
81
   * Now calculate the mapping between the original tuple's attributes and
82
   * the "clean" tuple's attributes.
83
   *
84
   * The "map" is an array of "cleanLength" attribute numbers, i.e. one
85
   * entry for every attribute of the "clean" tuple. The value of this entry
86
   * is the attribute number of the corresponding attribute of the
87
   * "original" tuple.  (Zero indicates a NULL output attribute, but we do
88
   * not use that feature in this routine.)
89
   */
90
0
  cleanLength = cleanTupType->natts;
91
0
  if (cleanLength > 0)
92
0
  {
93
0
    AttrNumber  cleanResno;
94
0
    ListCell   *t;
95
96
0
    cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber));
97
0
    cleanResno = 0;
98
0
    foreach(t, targetList)
99
0
    {
100
0
      TargetEntry *tle = lfirst(t);
101
102
0
      if (!tle->resjunk)
103
0
      {
104
0
        cleanMap[cleanResno] = tle->resno;
105
0
        cleanResno++;
106
0
      }
107
0
    }
108
0
    Assert(cleanResno == cleanLength);
109
0
  }
110
0
  else
111
0
    cleanMap = NULL;
112
113
  /*
114
   * Finally create and initialize the JunkFilter struct.
115
   */
116
0
  junkfilter = makeNode(JunkFilter);
117
118
0
  junkfilter->jf_targetList = targetList;
119
0
  junkfilter->jf_cleanTupType = cleanTupType;
120
0
  junkfilter->jf_cleanMap = cleanMap;
121
0
  junkfilter->jf_resultSlot = slot;
122
123
0
  return junkfilter;
124
0
}
125
126
/*
127
 * ExecInitJunkFilterConversion
128
 *
129
 * Initialize a JunkFilter for rowtype conversions.
130
 *
131
 * Here, we are given the target "clean" tuple descriptor rather than
132
 * inferring it from the targetlist.  The target descriptor can contain
133
 * deleted columns.  It is assumed that the caller has checked that the
134
 * non-deleted columns match up with the non-junk columns of the targetlist.
135
 */
136
JunkFilter *
137
ExecInitJunkFilterConversion(List *targetList,
138
               TupleDesc cleanTupType,
139
               TupleTableSlot *slot)
140
0
{
141
0
  JunkFilter *junkfilter;
142
0
  int     cleanLength;
143
0
  AttrNumber *cleanMap;
144
0
  ListCell   *t;
145
0
  int     i;
146
147
  /*
148
   * Use the given slot, or make a new slot if we weren't given one.
149
   */
150
0
  if (slot)
151
0
    ExecSetSlotDescriptor(slot, cleanTupType);
152
0
  else
153
0
    slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual);
154
155
  /*
156
   * Calculate the mapping between the original tuple's attributes and the
157
   * "clean" tuple's attributes.
158
   *
159
   * The "map" is an array of "cleanLength" attribute numbers, i.e. one
160
   * entry for every attribute of the "clean" tuple. The value of this entry
161
   * is the attribute number of the corresponding attribute of the
162
   * "original" tuple.  We store zero for any deleted attributes, marking
163
   * that a NULL is needed in the output tuple.
164
   */
165
0
  cleanLength = cleanTupType->natts;
166
0
  if (cleanLength > 0)
167
0
  {
168
0
    cleanMap = (AttrNumber *) palloc0(cleanLength * sizeof(AttrNumber));
169
0
    t = list_head(targetList);
170
0
    for (i = 0; i < cleanLength; i++)
171
0
    {
172
0
      if (TupleDescCompactAttr(cleanTupType, i)->attisdropped)
173
0
        continue;   /* map entry is already zero */
174
0
      for (;;)
175
0
      {
176
0
        TargetEntry *tle = lfirst(t);
177
178
0
        t = lnext(targetList, t);
179
0
        if (!tle->resjunk)
180
0
        {
181
0
          cleanMap[i] = tle->resno;
182
0
          break;
183
0
        }
184
0
      }
185
0
    }
186
0
  }
187
0
  else
188
0
    cleanMap = NULL;
189
190
  /*
191
   * Finally create and initialize the JunkFilter struct.
192
   */
193
0
  junkfilter = makeNode(JunkFilter);
194
195
0
  junkfilter->jf_targetList = targetList;
196
0
  junkfilter->jf_cleanTupType = cleanTupType;
197
0
  junkfilter->jf_cleanMap = cleanMap;
198
0
  junkfilter->jf_resultSlot = slot;
199
200
0
  return junkfilter;
201
0
}
202
203
/*
204
 * ExecFindJunkAttribute
205
 *
206
 * Locate the specified junk attribute in the junk filter's targetlist,
207
 * and return its resno.  Returns InvalidAttrNumber if not found.
208
 */
209
AttrNumber
210
ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
211
0
{
212
0
  return ExecFindJunkAttributeInTlist(junkfilter->jf_targetList, attrName);
213
0
}
214
215
/*
216
 * ExecFindJunkAttributeInTlist
217
 *
218
 * Find a junk attribute given a subplan's targetlist (not necessarily
219
 * part of a JunkFilter).
220
 */
221
AttrNumber
222
ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
223
0
{
224
0
  ListCell   *t;
225
226
0
  foreach(t, targetlist)
227
0
  {
228
0
    TargetEntry *tle = lfirst(t);
229
230
0
    if (tle->resjunk && tle->resname &&
231
0
      (strcmp(tle->resname, attrName) == 0))
232
0
    {
233
      /* We found it ! */
234
0
      return tle->resno;
235
0
    }
236
0
  }
237
238
0
  return InvalidAttrNumber;
239
0
}
240
241
/*
242
 * ExecFilterJunk
243
 *
244
 * Construct and return a slot with all the junk attributes removed.
245
 */
246
TupleTableSlot *
247
ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
248
0
{
249
0
  TupleTableSlot *resultSlot;
250
0
  AttrNumber *cleanMap;
251
0
  TupleDesc cleanTupType;
252
0
  int     cleanLength;
253
0
  int     i;
254
0
  Datum    *values;
255
0
  bool     *isnull;
256
0
  Datum    *old_values;
257
0
  bool     *old_isnull;
258
259
  /*
260
   * Extract all the values of the old tuple.
261
   */
262
0
  slot_getallattrs(slot);
263
0
  old_values = slot->tts_values;
264
0
  old_isnull = slot->tts_isnull;
265
266
  /*
267
   * get info from the junk filter
268
   */
269
0
  cleanTupType = junkfilter->jf_cleanTupType;
270
0
  cleanLength = cleanTupType->natts;
271
0
  cleanMap = junkfilter->jf_cleanMap;
272
0
  resultSlot = junkfilter->jf_resultSlot;
273
274
  /*
275
   * Prepare to build a virtual result tuple.
276
   */
277
0
  ExecClearTuple(resultSlot);
278
0
  values = resultSlot->tts_values;
279
0
  isnull = resultSlot->tts_isnull;
280
281
  /*
282
   * Transpose data into proper fields of the new tuple.
283
   */
284
0
  for (i = 0; i < cleanLength; i++)
285
0
  {
286
0
    int     j = cleanMap[i];
287
288
0
    if (j == 0)
289
0
    {
290
0
      values[i] = (Datum) 0;
291
0
      isnull[i] = true;
292
0
    }
293
0
    else
294
0
    {
295
0
      values[i] = old_values[j - 1];
296
0
      isnull[i] = old_isnull[j - 1];
297
0
    }
298
0
  }
299
300
  /*
301
   * And return the virtual tuple.
302
   */
303
0
  return ExecStoreVirtualTuple(resultSlot);
304
0
}