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 | */ |
59 | JunkFilter * |
60 | ExecInitJunkFilter(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 | */ |
134 | JunkFilter * |
135 | ExecInitJunkFilterConversion(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 | */ |
207 | AttrNumber |
208 | ExecFindJunkAttribute(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 | */ |
219 | AttrNumber |
220 | ExecFindJunkAttributeInTlist(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 | */ |
246 | Datum |
247 | ExecGetJunkAttribute(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 | */ |
260 | TupleTableSlot * |
261 | ExecFilterJunk(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 | |