1515 * Portions Copyright (c) 1994, Regents of the University of California
1616 *
1717 * 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 $
1919 *
2020 *-------------------------------------------------------------------------
2121 */
3232
3333static List * expand_targetlist (List * tlist ,int command_type ,
3434Index result_relation ,List * range_table );
35+ static TargetEntry * process_matched_tle (TargetEntry * src_tle ,
36+ TargetEntry * prior_tle ,
37+ int attrno );
3538
3639
3740/*
@@ -119,8 +122,9 @@ expand_targetlist(List *tlist, int command_type,
119122List * temp ;
120123
121124/*
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.
124128 */
125129tlistentry_used = (bool * )palloc ((old_tlist_len + 1 )* sizeof (bool ));
126130memset (tlistentry_used ,0 , (old_tlist_len + 1 )* sizeof (bool ));
@@ -141,6 +145,7 @@ expand_targetlist(List *tlist, int command_type,
141145
142146/*
143147 * We match targetlist entries to attributes using the resname.
148+ * Junk attributes are not candidates to be matched.
144149 */
145150old_tlist_index = 0 ;
146151foreach (temp ,tlist )
@@ -149,26 +154,11 @@ expand_targetlist(List *tlist, int command_type,
149154Resdom * resdom = old_tle -> resdom ;
150155
151156if (!tlistentry_used [old_tlist_index ]&&
152- strcmp ( resdom -> resname , attrname ) == 0 &&
153- ! resdom -> resjunk )
157+ ! resdom -> resjunk &&
158+ strcmp ( resdom -> resname , attrname ) == 0 )
154159{
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 );
170161tlistentry_used [old_tlist_index ]= true;
171- break ;
172162}
173163old_tlist_index ++ ;
174164}
@@ -192,22 +182,15 @@ expand_targetlist(List *tlist, int command_type,
192182{
193183case CMD_INSERT :
194184{
195- #ifdef _DROP_COLUMN_HACK__
196- Datum typedefault ;
197-
198- #else
199185Datum typedefault = get_typdefault (atttype );
200-
201- #endif /* _DROP_COLUMN_HACK__ */
202186int typlen ;
203187Const * temp_const ;
204188
205189#ifdef _DROP_COLUMN_HACK__
206190if (COLUMN_IS_DROPPED (att_tup ))
207191typedefault = PointerGetDatum (NULL );
208- else
209- typedefault = get_typdefault (atttype );
210192#endif /* _DROP_COLUMN_HACK__ */
193+
211194if (typedefault == PointerGetDatum (NULL ))
212195typlen = 0 ;
213196else
@@ -247,11 +230,9 @@ expand_targetlist(List *tlist, int command_type,
247230Var * temp_var ;
248231
249232#ifdef _DROP_COLUMN_HACK__
250- Node * temp_node = (Node * )NULL ;
251-
252233if (COLUMN_IS_DROPPED (att_tup ))
253234{
254- temp_node = (Node * )makeConst (atttype ,0 ,
235+ temp_var = (Var * )makeConst (atttype ,0 ,
255236PointerGetDatum (NULL ),
256237 true,
257238 false,
@@ -260,25 +241,20 @@ expand_targetlist(List *tlist, int command_type,
260241}
261242else
262243#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 );
269249
270250new_tle = makeTargetEntry (makeResdom (attrno ,
271251atttype ,
272252atttypmod ,
273- pstrdup (attrname ),
253+ pstrdup (attrname ),
2742540 ,
275255 (Oid )0 ,
276256 false),
277- #ifdef _DROP_COLUMN_HACK__
278- temp_node );
279- #else
280257 (Node * )temp_var );
281- #endif /* _DROP_COLUMN_HACK__ */
282258break ;
283259}
284260default :
@@ -304,13 +280,20 @@ expand_targetlist(List *tlist, int command_type,
304280
305281if (!tlistentry_used [old_tlist_index ])
306282{
307- Resdom * resdom ;
283+ Resdom * resdom = old_tle -> resdom ;
308284
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 ++ ;
314297}
315298old_tlist_index ++ ;
316299}
@@ -321,3 +304,72 @@ expand_targetlist(List *tlist, int command_type,
321304
322305return new_tlist ;
323306}
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+ }