1/*-------------------------------------------------------------------------
2 *
3 * execJunk.c
4 * Junk attribute support stuff....
5 *
6 * Portions Copyright (c) 1996-2019, 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.
58 */
59JunkFilter *
60ExecInitJunkFilter(List *targetList, TupleTableSlot *slot)
61{
62 JunkFilter *junkfilter;
63 TupleDesc cleanTupType;
64 int cleanLength;
65 AttrNumber *cleanMap;
66 ListCell *t;
67 AttrNumber cleanResno;
68
69 /*
70 * Compute the tuple descriptor for the cleaned tuple.
71 */
72 cleanTupType = ExecCleanTypeFromTL(targetList);
73
74 /*
75 * Use the given slot, or make a new slot if we weren't given one.
76 */
77 if (slot)
78 ExecSetSlotDescriptor(slot, cleanTupType);
79 else
80 slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual);
81
82 /*
83 * Now calculate the mapping between the original tuple's attributes and
84 * the "clean" tuple's attributes.
85 *
86 * The "map" is an array of "cleanLength" attribute numbers, i.e. one
87 * entry for every attribute of the "clean" tuple. The value of this entry
88 * is the attribute number of the corresponding attribute of the
89 * "original" tuple. (Zero indicates a NULL output attribute, but we do
90 * not use that feature in this routine.)
91 */
92 cleanLength = cleanTupType->natts;
93 if (cleanLength > 0)
94 {
95 cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber));
96 cleanResno = 1;
97 foreach(t, targetList)
98 {
99 TargetEntry *tle = lfirst(t);
100
101 if (!tle->resjunk)
102 {
103 cleanMap[cleanResno - 1] = tle->resno;
104 cleanResno++;
105 }
106 }
107 }
108 else
109 cleanMap = NULL;
110
111 /*
112 * Finally create and initialize the JunkFilter struct.
113 */
114 junkfilter = makeNode(JunkFilter);
115
116 junkfilter->jf_targetList = targetList;
117 junkfilter->jf_cleanTupType = cleanTupType;
118 junkfilter->jf_cleanMap = cleanMap;
119 junkfilter->jf_resultSlot = slot;
120
121 return junkfilter;
122}
123
124/*
125 * ExecInitJunkFilterConversion
126 *
127 * Initialize a JunkFilter for rowtype conversions.
128 *
129 * Here, we are given the target "clean" tuple descriptor rather than
130 * inferring it from the targetlist. The target descriptor can contain
131 * deleted columns. It is assumed that the caller has checked that the
132 * non-deleted columns match up with the non-junk columns of the targetlist.
133 */
134JunkFilter *
135ExecInitJunkFilterConversion(List *targetList,
136 TupleDesc cleanTupType,
137 TupleTableSlot *slot)
138{
139 JunkFilter *junkfilter;
140 int cleanLength;
141 AttrNumber *cleanMap;
142 ListCell *t;
143 int i;
144
145 /*
146 * Use the given slot, or make a new slot if we weren't given one.
147 */
148 if (slot)
149 ExecSetSlotDescriptor(slot, cleanTupType);
150 else
151 slot = MakeSingleTupleTableSlot(cleanTupType, &TTSOpsVirtual);
152
153 /*
154 * Calculate the mapping between the original tuple's attributes and the
155 * "clean" tuple's attributes.
156 *
157 * The "map" is an array of "cleanLength" attribute numbers, i.e. one
158 * entry for every attribute of the "clean" tuple. The value of this entry
159 * is the attribute number of the corresponding attribute of the
160 * "original" tuple. We store zero for any deleted attributes, marking
161 * that a NULL is needed in the output tuple.
162 */
163 cleanLength = cleanTupType->natts;
164 if (cleanLength > 0)
165 {
166 cleanMap = (AttrNumber *) palloc0(cleanLength * sizeof(AttrNumber));
167 t = list_head(targetList);
168 for (i = 0; i < cleanLength; i++)
169 {
170 if (TupleDescAttr(cleanTupType, i)->attisdropped)
171 continue; /* map entry is already zero */
172 for (;;)
173 {
174 TargetEntry *tle = lfirst(t);
175
176 t = lnext(t);
177 if (!tle->resjunk)
178 {
179 cleanMap[i] = tle->resno;
180 break;
181 }
182 }
183 }
184 }
185 else
186 cleanMap = NULL;
187
188 /*
189 * Finally create and initialize the JunkFilter struct.
190 */
191 junkfilter = makeNode(JunkFilter);
192
193 junkfilter->jf_targetList = targetList;
194 junkfilter->jf_cleanTupType = cleanTupType;
195 junkfilter->jf_cleanMap = cleanMap;
196 junkfilter->jf_resultSlot = slot;
197
198 return junkfilter;
199}
200
201/*
202 * ExecFindJunkAttribute
203 *
204 * Locate the specified junk attribute in the junk filter's targetlist,
205 * and return its resno. Returns InvalidAttrNumber if not found.
206 */
207AttrNumber
208ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
209{
210 return ExecFindJunkAttributeInTlist(junkfilter->jf_targetList, attrName);
211}
212
213/*
214 * ExecFindJunkAttributeInTlist
215 *
216 * Find a junk attribute given a subplan's targetlist (not necessarily
217 * part of a JunkFilter).
218 */
219AttrNumber
220ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
221{
222 ListCell *t;
223
224 foreach(t, targetlist)
225 {
226 TargetEntry *tle = lfirst(t);
227
228 if (tle->resjunk && tle->resname &&
229 (strcmp(tle->resname, attrName) == 0))
230 {
231 /* We found it ! */
232 return tle->resno;
233 }
234 }
235
236 return InvalidAttrNumber;
237}
238
239/*
240 * ExecGetJunkAttribute
241 *
242 * Given a junk filter's input tuple (slot) and a junk attribute's number
243 * previously found by ExecFindJunkAttribute, extract & return the value and
244 * isNull flag of the attribute.
245 */
246Datum
247ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
248 bool *isNull)
249{
250 Assert(attno > 0);
251
252 return slot_getattr(slot, attno, isNull);
253}
254
255/*
256 * ExecFilterJunk
257 *
258 * Construct and return a slot with all the junk attributes removed.
259 */
260TupleTableSlot *
261ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
262{
263 TupleTableSlot *resultSlot;
264 AttrNumber *cleanMap;
265 TupleDesc cleanTupType;
266 int cleanLength;
267 int i;
268 Datum *values;
269 bool *isnull;
270 Datum *old_values;
271 bool *old_isnull;
272
273 /*
274 * Extract all the values of the old tuple.
275 */
276 slot_getallattrs(slot);
277 old_values = slot->tts_values;
278 old_isnull = slot->tts_isnull;
279
280 /*
281 * get info from the junk filter
282 */
283 cleanTupType = junkfilter->jf_cleanTupType;
284 cleanLength = cleanTupType->natts;
285 cleanMap = junkfilter->jf_cleanMap;
286 resultSlot = junkfilter->jf_resultSlot;
287
288 /*
289 * Prepare to build a virtual result tuple.
290 */
291 ExecClearTuple(resultSlot);
292 values = resultSlot->tts_values;
293 isnull = resultSlot->tts_isnull;
294
295 /*
296 * Transpose data into proper fields of the new tuple.
297 */
298 for (i = 0; i < cleanLength; i++)
299 {
300 int j = cleanMap[i];
301
302 if (j == 0)
303 {
304 values[i] = (Datum) 0;
305 isnull[i] = true;
306 }
307 else
308 {
309 values[i] = old_values[j - 1];
310 isnull[i] = old_isnull[j - 1];
311 }
312 }
313
314 /*
315 * And return the virtual tuple.
316 */
317 return ExecStoreVirtualTuple(resultSlot);
318}
319