]> granicus.if.org Git - postgresql/commitdiff
Make update lists like 'UPDATE tab SET foo[1] = bar, foo[3] = baz'
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 22 Jul 2000 06:19:04 +0000 (06:19 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 22 Jul 2000 06:19:04 +0000 (06:19 +0000)
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.

src/backend/optimizer/prep/preptlist.c

index 22f06bb61a94f4ddbbcd2985af50cda9aa7334f4..a782203cd9b95595f4913c5fe30a7eb5de0072f4 100644 (file)
@@ -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);
+}