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