1 /*-------------------------------------------------------------------------
2  *
3  * execJunk.c
4  *	  Junk attribute support stuff....
5  *
6  * Portions Copyright (c) 1996-2021, 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 *
ExecInitJunkFilter(List * targetList,TupleTableSlot * slot)60 ExecInitJunkFilter(List *targetList, TupleTableSlot *slot)
61 {
62 	JunkFilter *junkfilter;
63 	TupleDesc	cleanTupType;
64 	int			cleanLength;
65 	AttrNumber *cleanMap;
66 
67 	/*
68 	 * Compute the tuple descriptor for the cleaned tuple.
69 	 */
70 	cleanTupType = ExecCleanTypeFromTL(targetList);
71 
72 	/*
73 	 * Use the given slot, or make a new slot if we weren't given one.
74 	 */
75 	if (slot)
76 		ExecSetSlotDescriptor(slot, cleanTupType);
77 	else
78 		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 	cleanLength = cleanTupType->natts;
91 	if (cleanLength > 0)
92 	{
93 		AttrNumber	cleanResno;
94 		ListCell   *t;
95 
96 		cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber));
97 		cleanResno = 0;
98 		foreach(t, targetList)
99 		{
100 			TargetEntry *tle = lfirst(t);
101 
102 			if (!tle->resjunk)
103 			{
104 				cleanMap[cleanResno] = tle->resno;
105 				cleanResno++;
106 			}
107 		}
108 		Assert(cleanResno == cleanLength);
109 	}
110 	else
111 		cleanMap = NULL;
112 
113 	/*
114 	 * Finally create and initialize the JunkFilter struct.
115 	 */
116 	junkfilter = makeNode(JunkFilter);
117 
118 	junkfilter->jf_targetList = targetList;
119 	junkfilter->jf_cleanTupType = cleanTupType;
120 	junkfilter->jf_cleanMap = cleanMap;
121 	junkfilter->jf_resultSlot = slot;
122 
123 	return junkfilter;
124 }
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 *
ExecInitJunkFilterConversion(List * targetList,TupleDesc cleanTupType,TupleTableSlot * slot)137 ExecInitJunkFilterConversion(List *targetList,
138 							 TupleDesc cleanTupType,
139 							 TupleTableSlot *slot)
140 {
141 	JunkFilter *junkfilter;
142 	int			cleanLength;
143 	AttrNumber *cleanMap;
144 	ListCell   *t;
145 	int			i;
146 
147 	/*
148 	 * Use the given slot, or make a new slot if we weren't given one.
149 	 */
150 	if (slot)
151 		ExecSetSlotDescriptor(slot, cleanTupType);
152 	else
153 		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 	cleanLength = cleanTupType->natts;
166 	if (cleanLength > 0)
167 	{
168 		cleanMap = (AttrNumber *) palloc0(cleanLength * sizeof(AttrNumber));
169 		t = list_head(targetList);
170 		for (i = 0; i < cleanLength; i++)
171 		{
172 			if (TupleDescAttr(cleanTupType, i)->attisdropped)
173 				continue;		/* map entry is already zero */
174 			for (;;)
175 			{
176 				TargetEntry *tle = lfirst(t);
177 
178 				t = lnext(targetList, t);
179 				if (!tle->resjunk)
180 				{
181 					cleanMap[i] = tle->resno;
182 					break;
183 				}
184 			}
185 		}
186 	}
187 	else
188 		cleanMap = NULL;
189 
190 	/*
191 	 * Finally create and initialize the JunkFilter struct.
192 	 */
193 	junkfilter = makeNode(JunkFilter);
194 
195 	junkfilter->jf_targetList = targetList;
196 	junkfilter->jf_cleanTupType = cleanTupType;
197 	junkfilter->jf_cleanMap = cleanMap;
198 	junkfilter->jf_resultSlot = slot;
199 
200 	return junkfilter;
201 }
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
ExecFindJunkAttribute(JunkFilter * junkfilter,const char * attrName)210 ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
211 {
212 	return ExecFindJunkAttributeInTlist(junkfilter->jf_targetList, attrName);
213 }
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
ExecFindJunkAttributeInTlist(List * targetlist,const char * attrName)222 ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
223 {
224 	ListCell   *t;
225 
226 	foreach(t, targetlist)
227 	{
228 		TargetEntry *tle = lfirst(t);
229 
230 		if (tle->resjunk && tle->resname &&
231 			(strcmp(tle->resname, attrName) == 0))
232 		{
233 			/* We found it ! */
234 			return tle->resno;
235 		}
236 	}
237 
238 	return InvalidAttrNumber;
239 }
240 
241 /*
242  * ExecFilterJunk
243  *
244  * Construct and return a slot with all the junk attributes removed.
245  */
246 TupleTableSlot *
ExecFilterJunk(JunkFilter * junkfilter,TupleTableSlot * slot)247 ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
248 {
249 	TupleTableSlot *resultSlot;
250 	AttrNumber *cleanMap;
251 	TupleDesc	cleanTupType;
252 	int			cleanLength;
253 	int			i;
254 	Datum	   *values;
255 	bool	   *isnull;
256 	Datum	   *old_values;
257 	bool	   *old_isnull;
258 
259 	/*
260 	 * Extract all the values of the old tuple.
261 	 */
262 	slot_getallattrs(slot);
263 	old_values = slot->tts_values;
264 	old_isnull = slot->tts_isnull;
265 
266 	/*
267 	 * get info from the junk filter
268 	 */
269 	cleanTupType = junkfilter->jf_cleanTupType;
270 	cleanLength = cleanTupType->natts;
271 	cleanMap = junkfilter->jf_cleanMap;
272 	resultSlot = junkfilter->jf_resultSlot;
273 
274 	/*
275 	 * Prepare to build a virtual result tuple.
276 	 */
277 	ExecClearTuple(resultSlot);
278 	values = resultSlot->tts_values;
279 	isnull = resultSlot->tts_isnull;
280 
281 	/*
282 	 * Transpose data into proper fields of the new tuple.
283 	 */
284 	for (i = 0; i < cleanLength; i++)
285 	{
286 		int			j = cleanMap[i];
287 
288 		if (j == 0)
289 		{
290 			values[i] = (Datum) 0;
291 			isnull[i] = true;
292 		}
293 		else
294 		{
295 			values[i] = old_values[j - 1];
296 			isnull[i] = old_isnull[j - 1];
297 		}
298 	}
299 
300 	/*
301 	 * And return the virtual tuple.
302 	 */
303 	return ExecStoreVirtualTuple(resultSlot);
304 }
305