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