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