Skip to content

Commit a5a1288

Browse files
committed
Make update lists like 'UPDATE tab SET foo[1] = bar, foo[3] = baz'
work as expected. THe underlying implementation is essentially 'SET foo = array_set(foo, 1, bar)', so we have to turn the items into nested invocations of array_set() to make it work correctly. Side effect: we now complain about 'UPDATE tab SET foo = bar, foo = baz' which is illegal per SQL92 but we didn't detect it before.
1 parent 2019e24 commit a5a1288

File tree

1 file changed

+101
-49
lines changed

1 file changed

+101
-49
lines changed

src/backend/optimizer/prep/preptlist.c

Lines changed: 101 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
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
*/
@@ -32,6 +32,9 @@
3232

3333
static List *expand_targetlist(List *tlist, int command_type,
3434
Index 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,
119122
List *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
*/
125129
tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
126130
memset(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
*/
145150
old_tlist_index = 0;
146151
foreach(temp, tlist)
@@ -149,26 +154,11 @@ expand_targetlist(List *tlist, int command_type,
149154
Resdom *resdom = old_tle->resdom;
150155

151156
if (!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);
170161
tlistentry_used[old_tlist_index] = true;
171-
break;
172162
}
173163
old_tlist_index++;
174164
}
@@ -192,22 +182,15 @@ expand_targetlist(List *tlist, int command_type,
192182
{
193183
case CMD_INSERT:
194184
{
195-
#ifdef _DROP_COLUMN_HACK__
196-
Datum typedefault;
197-
198-
#else
199185
Datum typedefault = get_typdefault(atttype);
200-
201-
#endif /* _DROP_COLUMN_HACK__ */
202186
int typlen;
203187
Const *temp_const;
204188

205189
#ifdef _DROP_COLUMN_HACK__
206190
if (COLUMN_IS_DROPPED(att_tup))
207191
typedefault = PointerGetDatum(NULL);
208-
else
209-
typedefault = get_typdefault(atttype);
210192
#endif /* _DROP_COLUMN_HACK__ */
193+
211194
if (typedefault == PointerGetDatum(NULL))
212195
typlen = 0;
213196
else
@@ -247,11 +230,9 @@ expand_targetlist(List *tlist, int command_type,
247230
Var *temp_var;
248231

249232
#ifdef _DROP_COLUMN_HACK__
250-
Node *temp_node = (Node *) NULL;
251-
252233
if (COLUMN_IS_DROPPED(att_tup))
253234
{
254-
temp_node = (Node *) makeConst(atttype, 0,
235+
temp_var = (Var *) makeConst(atttype, 0,
255236
PointerGetDatum(NULL),
256237
true,
257238
false,
@@ -260,25 +241,20 @@ expand_targetlist(List *tlist, int command_type,
260241
}
261242
else
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

270250
new_tle = makeTargetEntry(makeResdom(attrno,
271251
atttype,
272252
atttypmod,
273-
pstrdup(attrname),
253+
pstrdup(attrname),
274254
0,
275255
(Oid) 0,
276256
false),
277-
#ifdef _DROP_COLUMN_HACK__
278-
temp_node);
279-
#else
280257
(Node *) temp_var);
281-
#endif /* _DROP_COLUMN_HACK__ */
282258
break;
283259
}
284260
default:
@@ -304,13 +280,20 @@ expand_targetlist(List *tlist, int command_type,
304280

305281
if (!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
}
315298
old_tlist_index++;
316299
}
@@ -321,3 +304,72 @@ expand_targetlist(List *tlist, int command_type,
321304

322305
return 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+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy