diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 22f06bb61a..a782203cd9 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * 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, 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; /* - * Keep a map of which tlist items we have transferred to new list. +1 - * here keeps palloc from complaining if old_tlist_len=0. + * Keep a map of which tlist items we have transferred to new list. + * + * +1 here just keeps palloc from complaining if old_tlist_len==0. */ tlistentry_used = (bool *) palloc((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. + * Junk attributes are not candidates to be matched. */ old_tlist_index = 0; foreach(temp, tlist) @@ -149,26 +154,11 @@ expand_targetlist(List *tlist, int command_type, Resdom *resdom = old_tle->resdom; if (!tlistentry_used[old_tlist_index] && - strcmp(resdom->resname, attrname) == 0 && - !resdom->resjunk) + !resdom->resjunk && + strcmp(resdom->resname, attrname) == 0) { - - /* - * 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); - } + new_tle = process_matched_tle(old_tle, new_tle, attrno); tlistentry_used[old_tlist_index] = true; - break; } old_tlist_index++; } @@ -192,22 +182,15 @@ expand_targetlist(List *tlist, int command_type, { case CMD_INSERT: { -#ifdef _DROP_COLUMN_HACK__ - Datum typedefault; - -#else Datum typedefault = get_typdefault(atttype); - -#endif /* _DROP_COLUMN_HACK__ */ int typlen; Const *temp_const; #ifdef _DROP_COLUMN_HACK__ if (COLUMN_IS_DROPPED(att_tup)) typedefault = PointerGetDatum(NULL); - else - typedefault = get_typdefault(atttype); #endif /* _DROP_COLUMN_HACK__ */ + if (typedefault == PointerGetDatum(NULL)) typlen = 0; else @@ -247,11 +230,9 @@ expand_targetlist(List *tlist, int command_type, Var *temp_var; #ifdef _DROP_COLUMN_HACK__ - Node *temp_node = (Node *) NULL; - if (COLUMN_IS_DROPPED(att_tup)) { - temp_node = (Node *) makeConst(atttype, 0, + temp_var = (Var *) makeConst(atttype, 0, PointerGetDatum(NULL), true, false, @@ -260,25 +241,20 @@ expand_targetlist(List *tlist, int command_type, } else #endif /* _DROP_COLUMN_HACK__ */ - temp_var = makeVar(result_relation, attrno, atttype, - atttypmod, 0); -#ifdef _DROP_COLUMN_HACK__ - if (!temp_node) - temp_node = (Node *) temp_var; -#endif /* _DROP_COLUMN_HACK__ */ + temp_var = makeVar(result_relation, + attrno, + atttype, + atttypmod, + 0); new_tle = makeTargetEntry(makeResdom(attrno, atttype, atttypmod, - pstrdup(attrname), + pstrdup(attrname), 0, (Oid) 0, false), -#ifdef _DROP_COLUMN_HACK__ - temp_node); -#else (Node *) temp_var); -#endif /* _DROP_COLUMN_HACK__ */ break; } default: @@ -304,13 +280,20 @@ expand_targetlist(List *tlist, int command_type, if (!tlistentry_used[old_tlist_index]) { - Resdom *resdom; + Resdom *resdom = old_tle->resdom; - resdom = (Resdom *) copyObject((Node *) old_tle->resdom); - resdom->resno = attrno++; - resdom->resjunk = true; - new_tlist = lappend(new_tlist, - makeTargetEntry(resdom, old_tle->expr)); + if (! resdom->resjunk) + elog(ERROR, "Unexpected assignment to attribute \"%s\"", + resdom->resname); + /* Get the resno right, but don't copy unnecessarily */ + 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++; } @@ -321,3 +304,72 @@ expand_targetlist(List *tlist, int command_type, 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); +}