mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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.
This commit is contained in:
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.36 2000/04/12 17:15:23 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.37 2000/07/22 06:19:04 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -32,6 +32,9 @@
|
|||||||
|
|
||||||
static List *expand_targetlist(List *tlist, int command_type,
|
static List *expand_targetlist(List *tlist, int command_type,
|
||||||
Index result_relation, List *range_table);
|
Index result_relation, List *range_table);
|
||||||
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
||||||
|
TargetEntry *prior_tle,
|
||||||
|
int attrno);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,8 +122,9 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
List *temp;
|
List *temp;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep a map of which tlist items we have transferred to new list. +1
|
* Keep a map of which tlist items we have transferred to new list.
|
||||||
* here keeps palloc from complaining if old_tlist_len=0.
|
*
|
||||||
|
* +1 here just keeps palloc from complaining if old_tlist_len==0.
|
||||||
*/
|
*/
|
||||||
tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
|
tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
|
||||||
memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool));
|
memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool));
|
||||||
@ -141,6 +145,7 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We match targetlist entries to attributes using the resname.
|
* We match targetlist entries to attributes using the resname.
|
||||||
|
* Junk attributes are not candidates to be matched.
|
||||||
*/
|
*/
|
||||||
old_tlist_index = 0;
|
old_tlist_index = 0;
|
||||||
foreach(temp, tlist)
|
foreach(temp, tlist)
|
||||||
@ -149,26 +154,11 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
Resdom *resdom = old_tle->resdom;
|
Resdom *resdom = old_tle->resdom;
|
||||||
|
|
||||||
if (!tlistentry_used[old_tlist_index] &&
|
if (!tlistentry_used[old_tlist_index] &&
|
||||||
strcmp(resdom->resname, attrname) == 0 &&
|
!resdom->resjunk &&
|
||||||
!resdom->resjunk)
|
strcmp(resdom->resname, attrname) == 0)
|
||||||
{
|
{
|
||||||
|
new_tle = process_matched_tle(old_tle, new_tle, attrno);
|
||||||
/*
|
|
||||||
* We can recycle the old TLE+resdom if right resno; else
|
|
||||||
* make a new one to avoid modifying the old tlist
|
|
||||||
* structure. (Is preserving old tlist actually
|
|
||||||
* necessary?)
|
|
||||||
*/
|
|
||||||
if (resdom->resno == attrno)
|
|
||||||
new_tle = old_tle;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resdom = (Resdom *) copyObject((Node *) resdom);
|
|
||||||
resdom->resno = attrno;
|
|
||||||
new_tle = makeTargetEntry(resdom, old_tle->expr);
|
|
||||||
}
|
|
||||||
tlistentry_used[old_tlist_index] = true;
|
tlistentry_used[old_tlist_index] = true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
old_tlist_index++;
|
old_tlist_index++;
|
||||||
}
|
}
|
||||||
@ -192,22 +182,15 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
{
|
{
|
||||||
case CMD_INSERT:
|
case CMD_INSERT:
|
||||||
{
|
{
|
||||||
#ifdef _DROP_COLUMN_HACK__
|
|
||||||
Datum typedefault;
|
|
||||||
|
|
||||||
#else
|
|
||||||
Datum typedefault = get_typdefault(atttype);
|
Datum typedefault = get_typdefault(atttype);
|
||||||
|
|
||||||
#endif /* _DROP_COLUMN_HACK__ */
|
|
||||||
int typlen;
|
int typlen;
|
||||||
Const *temp_const;
|
Const *temp_const;
|
||||||
|
|
||||||
#ifdef _DROP_COLUMN_HACK__
|
#ifdef _DROP_COLUMN_HACK__
|
||||||
if (COLUMN_IS_DROPPED(att_tup))
|
if (COLUMN_IS_DROPPED(att_tup))
|
||||||
typedefault = PointerGetDatum(NULL);
|
typedefault = PointerGetDatum(NULL);
|
||||||
else
|
|
||||||
typedefault = get_typdefault(atttype);
|
|
||||||
#endif /* _DROP_COLUMN_HACK__ */
|
#endif /* _DROP_COLUMN_HACK__ */
|
||||||
|
|
||||||
if (typedefault == PointerGetDatum(NULL))
|
if (typedefault == PointerGetDatum(NULL))
|
||||||
typlen = 0;
|
typlen = 0;
|
||||||
else
|
else
|
||||||
@ -247,11 +230,9 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
Var *temp_var;
|
Var *temp_var;
|
||||||
|
|
||||||
#ifdef _DROP_COLUMN_HACK__
|
#ifdef _DROP_COLUMN_HACK__
|
||||||
Node *temp_node = (Node *) NULL;
|
|
||||||
|
|
||||||
if (COLUMN_IS_DROPPED(att_tup))
|
if (COLUMN_IS_DROPPED(att_tup))
|
||||||
{
|
{
|
||||||
temp_node = (Node *) makeConst(atttype, 0,
|
temp_var = (Var *) makeConst(atttype, 0,
|
||||||
PointerGetDatum(NULL),
|
PointerGetDatum(NULL),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@ -260,25 +241,20 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif /* _DROP_COLUMN_HACK__ */
|
#endif /* _DROP_COLUMN_HACK__ */
|
||||||
temp_var = makeVar(result_relation, attrno, atttype,
|
temp_var = makeVar(result_relation,
|
||||||
atttypmod, 0);
|
attrno,
|
||||||
#ifdef _DROP_COLUMN_HACK__
|
atttype,
|
||||||
if (!temp_node)
|
atttypmod,
|
||||||
temp_node = (Node *) temp_var;
|
0);
|
||||||
#endif /* _DROP_COLUMN_HACK__ */
|
|
||||||
|
|
||||||
new_tle = makeTargetEntry(makeResdom(attrno,
|
new_tle = makeTargetEntry(makeResdom(attrno,
|
||||||
atttype,
|
atttype,
|
||||||
atttypmod,
|
atttypmod,
|
||||||
pstrdup(attrname),
|
pstrdup(attrname),
|
||||||
0,
|
0,
|
||||||
(Oid) 0,
|
(Oid) 0,
|
||||||
false),
|
false),
|
||||||
#ifdef _DROP_COLUMN_HACK__
|
|
||||||
temp_node);
|
|
||||||
#else
|
|
||||||
(Node *) temp_var);
|
(Node *) temp_var);
|
||||||
#endif /* _DROP_COLUMN_HACK__ */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -304,13 +280,20 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
|
|
||||||
if (!tlistentry_used[old_tlist_index])
|
if (!tlistentry_used[old_tlist_index])
|
||||||
{
|
{
|
||||||
Resdom *resdom;
|
Resdom *resdom = old_tle->resdom;
|
||||||
|
|
||||||
resdom = (Resdom *) copyObject((Node *) old_tle->resdom);
|
if (! resdom->resjunk)
|
||||||
resdom->resno = attrno++;
|
elog(ERROR, "Unexpected assignment to attribute \"%s\"",
|
||||||
resdom->resjunk = true;
|
resdom->resname);
|
||||||
new_tlist = lappend(new_tlist,
|
/* Get the resno right, but don't copy unnecessarily */
|
||||||
makeTargetEntry(resdom, old_tle->expr));
|
if (resdom->resno != attrno)
|
||||||
|
{
|
||||||
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
||||||
|
resdom->resno = attrno;
|
||||||
|
old_tle = makeTargetEntry(resdom, old_tle->expr);
|
||||||
|
}
|
||||||
|
new_tlist = lappend(new_tlist, old_tle);
|
||||||
|
attrno++;
|
||||||
}
|
}
|
||||||
old_tlist_index++;
|
old_tlist_index++;
|
||||||
}
|
}
|
||||||
@ -321,3 +304,72 @@ expand_targetlist(List *tlist, int command_type,
|
|||||||
|
|
||||||
return new_tlist;
|
return new_tlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a matched TLE from the original tlist into a correct new TLE.
|
||||||
|
*
|
||||||
|
* This routine checks for multiple assignments to the same target attribute,
|
||||||
|
* such as "UPDATE table SET foo = 42, foo = 43". This is OK only if they
|
||||||
|
* are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43".
|
||||||
|
* If so, we need to merge the operations into a single assignment op.
|
||||||
|
* Essentially, the expression we want to produce in this case is like
|
||||||
|
* foo = array_set(array_set(foo, 2, 42), 4, 43)
|
||||||
|
*/
|
||||||
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
||||||
|
TargetEntry *prior_tle,
|
||||||
|
int attrno)
|
||||||
|
{
|
||||||
|
Resdom *resdom = src_tle->resdom;
|
||||||
|
Node *priorbottom;
|
||||||
|
ArrayRef *newexpr;
|
||||||
|
|
||||||
|
if (prior_tle == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Normal case where this is the first assignment to the attribute.
|
||||||
|
*
|
||||||
|
* We can recycle the old TLE+resdom if right resno; else make a
|
||||||
|
* new one to avoid modifying the old tlist structure. (Is preserving
|
||||||
|
* old tlist actually necessary? Not sure, be safe.)
|
||||||
|
*/
|
||||||
|
if (resdom->resno == attrno)
|
||||||
|
return src_tle;
|
||||||
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
||||||
|
resdom->resno = attrno;
|
||||||
|
return makeTargetEntry(resdom, src_tle->expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiple assignments to same attribute. Allow only if all are
|
||||||
|
* array-assign operators with same bottom array object.
|
||||||
|
*/
|
||||||
|
if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
|
||||||
|
((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
|
||||||
|
prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
|
||||||
|
((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
|
||||||
|
((ArrayRef *) src_tle->expr)->refelemtype !=
|
||||||
|
((ArrayRef *) prior_tle->expr)->refelemtype)
|
||||||
|
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
|
||||||
|
resdom->resname);
|
||||||
|
/*
|
||||||
|
* Prior TLE could be a nest of ArrayRefs if we do this more than once.
|
||||||
|
*/
|
||||||
|
priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
|
||||||
|
while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
|
||||||
|
((ArrayRef *) priorbottom)->refassgnexpr != NULL)
|
||||||
|
priorbottom = ((ArrayRef *) priorbottom)->refexpr;
|
||||||
|
if (! equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
|
||||||
|
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
|
||||||
|
resdom->resname);
|
||||||
|
/*
|
||||||
|
* Looks OK to nest 'em.
|
||||||
|
*/
|
||||||
|
newexpr = makeNode(ArrayRef);
|
||||||
|
memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
|
||||||
|
newexpr->refexpr = prior_tle->expr;
|
||||||
|
|
||||||
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
||||||
|
resdom->resno = attrno;
|
||||||
|
return makeTargetEntry(resdom, (Node *) newexpr);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user