15
15
* Portions Copyright (c) 1994, Regents of the University of California
16
16
*
17
17
* IDENTIFICATION
18
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.36 2000/04/12 17:15:23 momjian Exp $
18
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.37 2000/07/22 06:19:04 tgl Exp $
19
19
*
20
20
*-------------------------------------------------------------------------
21
21
*/
32
32
33
33
static List * expand_targetlist (List * tlist , int command_type ,
34
34
Index result_relation , List * range_table );
35
+ static TargetEntry * process_matched_tle (TargetEntry * src_tle ,
36
+ TargetEntry * prior_tle ,
37
+ int attrno );
35
38
36
39
37
40
/*
@@ -119,8 +122,9 @@ expand_targetlist(List *tlist, int command_type,
119
122
List * temp ;
120
123
121
124
/*
122
- * Keep a map of which tlist items we have transferred to new list. +1
123
- * here keeps palloc from complaining if old_tlist_len=0.
125
+ * Keep a map of which tlist items we have transferred to new list.
126
+ *
127
+ * +1 here just keeps palloc from complaining if old_tlist_len==0.
124
128
*/
125
129
tlistentry_used = (bool * ) palloc ((old_tlist_len + 1 ) * sizeof (bool ));
126
130
memset (tlistentry_used , 0 , (old_tlist_len + 1 ) * sizeof (bool ));
@@ -141,6 +145,7 @@ expand_targetlist(List *tlist, int command_type,
141
145
142
146
/*
143
147
* We match targetlist entries to attributes using the resname.
148
+ * Junk attributes are not candidates to be matched.
144
149
*/
145
150
old_tlist_index = 0 ;
146
151
foreach (temp , tlist )
@@ -149,26 +154,11 @@ expand_targetlist(List *tlist, int command_type,
149
154
Resdom * resdom = old_tle -> resdom ;
150
155
151
156
if (!tlistentry_used [old_tlist_index ] &&
152
- strcmp ( resdom -> resname , attrname ) == 0 &&
153
- ! resdom -> resjunk )
157
+ ! resdom -> resjunk &&
158
+ strcmp ( resdom -> resname , attrname ) == 0 )
154
159
{
155
-
156
- /*
157
- * We can recycle the old TLE+resdom if right resno; else
158
- * make a new one to avoid modifying the old tlist
159
- * structure. (Is preserving old tlist actually
160
- * necessary?)
161
- */
162
- if (resdom -> resno == attrno )
163
- new_tle = old_tle ;
164
- else
165
- {
166
- resdom = (Resdom * ) copyObject ((Node * ) resdom );
167
- resdom -> resno = attrno ;
168
- new_tle = makeTargetEntry (resdom , old_tle -> expr );
169
- }
160
+ new_tle = process_matched_tle (old_tle , new_tle , attrno );
170
161
tlistentry_used [old_tlist_index ] = true;
171
- break ;
172
162
}
173
163
old_tlist_index ++ ;
174
164
}
@@ -192,22 +182,15 @@ expand_targetlist(List *tlist, int command_type,
192
182
{
193
183
case CMD_INSERT :
194
184
{
195
- #ifdef _DROP_COLUMN_HACK__
196
- Datum typedefault ;
197
-
198
- #else
199
185
Datum typedefault = get_typdefault (atttype );
200
-
201
- #endif /* _DROP_COLUMN_HACK__ */
202
186
int typlen ;
203
187
Const * temp_const ;
204
188
205
189
#ifdef _DROP_COLUMN_HACK__
206
190
if (COLUMN_IS_DROPPED (att_tup ))
207
191
typedefault = PointerGetDatum (NULL );
208
- else
209
- typedefault = get_typdefault (atttype );
210
192
#endif /* _DROP_COLUMN_HACK__ */
193
+
211
194
if (typedefault == PointerGetDatum (NULL ))
212
195
typlen = 0 ;
213
196
else
@@ -247,11 +230,9 @@ expand_targetlist(List *tlist, int command_type,
247
230
Var * temp_var ;
248
231
249
232
#ifdef _DROP_COLUMN_HACK__
250
- Node * temp_node = (Node * ) NULL ;
251
-
252
233
if (COLUMN_IS_DROPPED (att_tup ))
253
234
{
254
- temp_node = (Node * ) makeConst (atttype , 0 ,
235
+ temp_var = (Var * ) makeConst (atttype , 0 ,
255
236
PointerGetDatum (NULL ),
256
237
true,
257
238
false,
@@ -260,25 +241,20 @@ expand_targetlist(List *tlist, int command_type,
260
241
}
261
242
else
262
243
#endif /* _DROP_COLUMN_HACK__ */
263
- temp_var = makeVar (result_relation , attrno , atttype ,
264
- atttypmod , 0 );
265
- #ifdef _DROP_COLUMN_HACK__
266
- if (!temp_node )
267
- temp_node = (Node * ) temp_var ;
268
- #endif /* _DROP_COLUMN_HACK__ */
244
+ temp_var = makeVar (result_relation ,
245
+ attrno ,
246
+ atttype ,
247
+ atttypmod ,
248
+ 0 );
269
249
270
250
new_tle = makeTargetEntry (makeResdom (attrno ,
271
251
atttype ,
272
252
atttypmod ,
273
- pstrdup (attrname ),
253
+ pstrdup (attrname ),
274
254
0 ,
275
255
(Oid ) 0 ,
276
256
false),
277
- #ifdef _DROP_COLUMN_HACK__
278
- temp_node );
279
- #else
280
257
(Node * ) temp_var );
281
- #endif /* _DROP_COLUMN_HACK__ */
282
258
break ;
283
259
}
284
260
default :
@@ -304,13 +280,20 @@ expand_targetlist(List *tlist, int command_type,
304
280
305
281
if (!tlistentry_used [old_tlist_index ])
306
282
{
307
- Resdom * resdom ;
283
+ Resdom * resdom = old_tle -> resdom ;
308
284
309
- resdom = (Resdom * ) copyObject ((Node * ) old_tle -> resdom );
310
- resdom -> resno = attrno ++ ;
311
- resdom -> resjunk = true;
312
- new_tlist = lappend (new_tlist ,
313
- makeTargetEntry (resdom , old_tle -> expr ));
285
+ if (! resdom -> resjunk )
286
+ elog (ERROR , "Unexpected assignment to attribute \"%s\"" ,
287
+ resdom -> resname );
288
+ /* Get the resno right, but don't copy unnecessarily */
289
+ if (resdom -> resno != attrno )
290
+ {
291
+ resdom = (Resdom * ) copyObject ((Node * ) resdom );
292
+ resdom -> resno = attrno ;
293
+ old_tle = makeTargetEntry (resdom , old_tle -> expr );
294
+ }
295
+ new_tlist = lappend (new_tlist , old_tle );
296
+ attrno ++ ;
314
297
}
315
298
old_tlist_index ++ ;
316
299
}
@@ -321,3 +304,72 @@ expand_targetlist(List *tlist, int command_type,
321
304
322
305
return new_tlist ;
323
306
}
307
+
308
+
309
+ /*
310
+ * Convert a matched TLE from the original tlist into a correct new TLE.
311
+ *
312
+ * This routine checks for multiple assignments to the same target attribute,
313
+ * such as "UPDATE table SET foo = 42, foo = 43". This is OK only if they
314
+ * are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43".
315
+ * If so, we need to merge the operations into a single assignment op.
316
+ * Essentially, the expression we want to produce in this case is like
317
+ * foo = array_set(array_set(foo, 2, 42), 4, 43)
318
+ */
319
+ static TargetEntry * process_matched_tle (TargetEntry * src_tle ,
320
+ TargetEntry * prior_tle ,
321
+ int attrno )
322
+ {
323
+ Resdom * resdom = src_tle -> resdom ;
324
+ Node * priorbottom ;
325
+ ArrayRef * newexpr ;
326
+
327
+ if (prior_tle == NULL )
328
+ {
329
+ /*
330
+ * Normal case where this is the first assignment to the attribute.
331
+ *
332
+ * We can recycle the old TLE+resdom if right resno; else make a
333
+ * new one to avoid modifying the old tlist structure. (Is preserving
334
+ * old tlist actually necessary? Not sure, be safe.)
335
+ */
336
+ if (resdom -> resno == attrno )
337
+ return src_tle ;
338
+ resdom = (Resdom * ) copyObject ((Node * ) resdom );
339
+ resdom -> resno = attrno ;
340
+ return makeTargetEntry (resdom , src_tle -> expr );
341
+ }
342
+
343
+ /*
344
+ * Multiple assignments to same attribute. Allow only if all are
345
+ * array-assign operators with same bottom array object.
346
+ */
347
+ if (src_tle -> expr == NULL || !IsA (src_tle -> expr , ArrayRef ) ||
348
+ ((ArrayRef * ) src_tle -> expr )-> refassgnexpr == NULL ||
349
+ prior_tle -> expr == NULL || !IsA (prior_tle -> expr , ArrayRef ) ||
350
+ ((ArrayRef * ) prior_tle -> expr )-> refassgnexpr == NULL ||
351
+ ((ArrayRef * ) src_tle -> expr )-> refelemtype !=
352
+ ((ArrayRef * ) prior_tle -> expr )-> refelemtype )
353
+ elog (ERROR , "Multiple assignments to same attribute \"%s\"" ,
354
+ resdom -> resname );
355
+ /*
356
+ * Prior TLE could be a nest of ArrayRefs if we do this more than once.
357
+ */
358
+ priorbottom = ((ArrayRef * ) prior_tle -> expr )-> refexpr ;
359
+ while (priorbottom != NULL && IsA (priorbottom , ArrayRef ) &&
360
+ ((ArrayRef * ) priorbottom )-> refassgnexpr != NULL )
361
+ priorbottom = ((ArrayRef * ) priorbottom )-> refexpr ;
362
+ if (! equal (priorbottom , ((ArrayRef * ) src_tle -> expr )-> refexpr ))
363
+ elog (ERROR , "Multiple assignments to same attribute \"%s\"" ,
364
+ resdom -> resname );
365
+ /*
366
+ * Looks OK to nest 'em.
367
+ */
368
+ newexpr = makeNode (ArrayRef );
369
+ memcpy (newexpr , src_tle -> expr , sizeof (ArrayRef ));
370
+ newexpr -> refexpr = prior_tle -> expr ;
371
+
372
+ resdom = (Resdom * ) copyObject ((Node * ) resdom );
373
+ resdom -> resno = attrno ;
374
+ return makeTargetEntry (resdom , (Node * ) newexpr );
375
+ }
0 commit comments