]> granicus.if.org Git - postgresql/commitdiff
Support assignment to subfields of composite columns in UPDATE and INSERT.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Jun 2004 19:08:20 +0000 (19:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Jun 2004 19:08:20 +0000 (19:08 +0000)
As a side effect, cause subscripts in INSERT targetlists to do something
more or less sensible; previously we evaluated such subscripts and then
effectively ignored them.  Another side effect is that UPDATE-ing an
element or slice of an array value that is NULL now produces a non-null
result, namely an array containing just the assigned-to positions.

27 files changed:
doc/src/sgml/ref/insert.sgml
doc/src/sgml/ref/update.sgml
doc/src/sgml/rowtypes.sgml
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/parser/parse_target.c
src/backend/rewrite/rewriteHandler.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/include/parser/parse_node.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/arrays.out
src/test/regress/expected/rowtypes.out
src/test/regress/sql/arrays.sql
src/test/regress/sql/rowtypes.sql

index f73c7fafc5eefa847d4420c1a5256b40b421c2c0..a77428d33e0ec8b27025778b18c9cebe39d579f8 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.26 2003/11/29 19:51:39 pgsql Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.27 2004/06/09 19:08:13 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -73,6 +73,9 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
     <listitem>
      <para>
       The name of a column in <replaceable class="PARAMETER">table</replaceable>.
+      The column name can be qualified with a subfield name or array
+      subscript, if needed.  (Inserting into only some fields of a
+      composite column leaves the other fields null.)
      </para>
     </listitem>
    </varlistentry>
@@ -184,13 +187,11 @@ INSERT INTO films SELECT * FROM tmp;
 
 <programlisting>
 -- Create an empty 3x3 gameboard for noughts-and-crosses
--- (all of these commands create the same board)
+-- (these commands create the same board)
 INSERT INTO tictactoe (game, board[1:3][1:3])
-    VALUES (1,'{{"","",""},{},{"",""}}');
-INSERT INTO tictactoe (game, board[3][3])
-    VALUES (2,'{}');
+    VALUES (1,'{{"","",""},{"","",""},{"","",""}}');
 INSERT INTO tictactoe (game, board)
-    VALUES (3,'{{,,},{,,},{,,}}');
+    VALUES (2,'{{,,},{,,},{,,}}');
 </programlisting>
   </para>
  </refsect1>
index 6a6cf99137314ec811c37306b94867610c3656a6..5695df1584bbbce2847af6ab28333273a31d4037 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.28 2004/03/03 22:22:24 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.29 2004/06/09 19:08:13 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -77,7 +77,10 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
     <term><replaceable class="PARAMETER">column</replaceable></term>
     <listitem>
      <para>
-      The name of a column in <replaceable class="PARAMETER">table</replaceable>.
+      The name of a column in <replaceable
+      class="PARAMETER">table</replaceable>.
+      The column name can be qualified with a subfield name or array
+      subscript, if needed.
      </para>
     </listitem>
    </varlistentry>
index 4a5d013a0acf14819e1be76a8ab9353f14cc8022..264b4c59a0c6907f749bf2c657b4ca63bd52c6d1 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/rowtypes.sgml,v 2.1 2004/06/07 04:04:47 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/rowtypes.sgml,v 2.2 2004/06/09 19:08:14 tgl Exp $ -->
 
 <sect1 id="rowtypes">
  <title>Composite Types</title>
@@ -66,6 +66,27 @@ SELECT price_extension(item, 10) FROM on_hand;
 </programlisting>
 
  </para>
+
+ <para>
+  Whenever you create a table, a composite type is also automatically
+  created, with the same name as the table, to represent the table's
+  row type.  For example, had we said
+<programlisting>
+CREATE TABLE inventory_item (
+    name            text,
+    supplier_id     integer REFERENCES suppliers,
+    price           numeric CHECK (price > 0)
+);
+</programlisting>
+  then the same <literal>inventory_item</> composite type shown above would
+  come into being as a 
+  byproduct, and could be used just as above.  Note however an important
+  restriction of the current implementation: since no constraints are
+  associated with a composite type, the constraints shown in the table
+  definition <emphasis>do not apply</> to values of the composite type
+  outside the table.  (A partial workaround is to use domain
+  types as members of composite types.)
+ </para>
  </sect2>
 
  <sect2>
@@ -178,6 +199,49 @@ SELECT (my_func(...)).field FROM ...
  </para>
  </sect2>
 
+ <sect2>
+  <title>Modifying Composite Types</title>
+
+ <para>
+  Here are some examples of the proper syntax for inserting and updating
+  composite columns.
+  First, inserting or updating a whole column:
+
+<programlisting>
+INSERT INTO mytab (complex_col) VALUES((1.1,2.2));
+
+UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;
+</programlisting>
+
+  The first example omits <literal>ROW</>, the second uses it; we
+  could have done it either way.
+ </para>
+
+ <para>
+  We can update an individual subfield of a composite column:
+
+<programlisting>
+UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;
+</programlisting>
+
+  Notice here that we don't need to (and indeed cannot)
+  put parentheses around the column name appearing just after
+  <literal>SET</>, but we do need parentheses when referencing the same
+  column in the expression to the right of the equal sign.
+ </para>
+
+ <para>
+  And we can specify subfields as targets for <command>INSERT</>, too:
+
+<programlisting>
+INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);
+</programlisting>
+
+  Had we not supplied values for all the subfields of the column, the
+  remaining subfields would have been filled with NULLs.
+ </para>
+ </sect2>
+
  <sect2>
   <title>Composite Type Input and Output Syntax</title>
 
index 8ada5e6faf254573b91eafdf8728df6380336182..e4362c38f58437e90aa13057d528b119fccfc967 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.163 2004/06/05 19:48:08 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.164 2004/06/09 19:08:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -118,6 +118,9 @@ static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
 static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalFieldStore(FieldStoreState *fstate,
+                                       ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
@@ -217,6 +220,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
        ArrayType  *array_source;
        ArrayType  *resultArray;
        bool            isAssignment = (arrayRef->refassgnexpr != NULL);
+       bool            eisnull;
        ListCell   *l;
        int                     i = 0,
                                j = 0;
@@ -224,39 +228,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                lower;
        int                *lIndex;
 
-       /* Set default values for result flags: non-null, not a set result */
-       *isNull = false;
-       if (isDone)
-               *isDone = ExprSingleResult;
+       array_source = (ArrayType *)
+               DatumGetPointer(ExecEvalExpr(astate->refexpr,
+                                                                        econtext,
+                                                                        isNull,
+                                                                        isDone));
 
-       if (arrayRef->refexpr != NULL)
+       /*
+        * If refexpr yields NULL, and it's a fetch, then result is NULL.
+        * In the assignment case, we'll cons up something below.
+        */
+       if (*isNull)
        {
-               array_source = (ArrayType *)
-                       DatumGetPointer(ExecEvalExpr(astate->refexpr,
-                                                                                econtext,
-                                                                                isNull,
-                                                                                isDone));
-
-               /*
-                * If refexpr yields NULL, result is always NULL, for now anyway.
-                * (This means you cannot assign to an element or slice of an
-                * array that's NULL; it'll just stay NULL.)
-                */
-               if (*isNull)
+               if (isDone && *isDone == ExprEndResult)
+                       return (Datum) NULL; /* end of set result */
+               if (!isAssignment)
                        return (Datum) NULL;
        }
-       else
-       {
-               /*
-                * Empty refexpr indicates we are doing an INSERT into an array
-                * column. For now, we just take the refassgnexpr (which the
-                * parser will have ensured is an array value) and return it
-                * as-is, ignoring any subscripts that may have been supplied in
-                * the INSERT column list. This is a kluge, but it's not real
-                * clear what the semantics ought to be...
-                */
-               array_source = NULL;
-       }
 
        foreach(l, astate->refupperindexpr)
        {
@@ -270,14 +258,16 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
                upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
                                                                                                         econtext,
-                                                                                                        isNull,
+                                                                                                        &eisnull,
                                                                                                         NULL));
                /* If any index expr yields NULL, result is NULL or source array */
-               if (*isNull)
+               if (eisnull)
                {
-                       if (!isAssignment || array_source == NULL)
+                       if (!isAssignment)
+                       {
+                               *isNull = true;
                                return (Datum) NULL;
-                       *isNull = false;
+                       }
                        return PointerGetDatum(array_source);
                }
        }
@@ -296,18 +286,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
                        lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
                                                                                                                 econtext,
-                                                                                                                isNull,
+                                                                                                                &eisnull,
                                                                                                                 NULL));
 
                        /*
                         * If any index expr yields NULL, result is NULL or source
                         * array
                         */
-                       if (*isNull)
+                       if (eisnull)
                        {
-                               if (!isAssignment || array_source == NULL)
+                               if (!isAssignment)
+                               {
+                                       *isNull = true;
                                        return (Datum) NULL;
-                               *isNull = false;
+                               }
                                return PointerGetDatum(array_source);
                        }
                }
@@ -321,26 +313,50 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
        if (isAssignment)
        {
-               Datum           sourceData = ExecEvalExpr(astate->refassgnexpr,
-                                                                                         econtext,
-                                                                                         isNull,
-                                                                                         NULL);
+               Datum           sourceData;
+
+               /*
+                * Evaluate the value to be assigned into the array.
+                *
+                * XXX At some point we'll need to look into making the old value of
+                * the array element available via CaseTestExpr, as is done by
+                * ExecEvalFieldStore.  This is not needed now but will be needed
+                * to support arrays of composite types; in an assignment to a field
+                * of an array member, the parser would generate a FieldStore that
+                * expects to fetch its input tuple via CaseTestExpr.
+                */
+               sourceData = ExecEvalExpr(astate->refassgnexpr,
+                                                                 econtext,
+                                                                 &eisnull,
+                                                                 NULL);
 
                /*
                 * For now, can't cope with inserting NULL into an array, so make
                 * it a no-op per discussion above...
                 */
+               if (eisnull)
+                       return PointerGetDatum(array_source);
+
+               /*
+                * For an assignment, if all the subscripts and the input expression
+                * are non-null but the original array is null, then substitute an
+                * empty (zero-dimensional) array and proceed with the assignment.
+                * This only works for varlena arrays, though; for fixed-length
+                * array types we punt and return the null input array.
+                */
                if (*isNull)
                {
-                       if (array_source == NULL)
-                               return (Datum) NULL;
+                       if (astate->refattrlength > 0) /* fixed-length array? */
+                               return PointerGetDatum(array_source);
+
+                       array_source = construct_md_array(NULL, 0, NULL, NULL,
+                                                                                         arrayRef->refelemtype,
+                                                                                         astate->refelemlength,
+                                                                                         astate->refelembyval,
+                                                                                         astate->refelemalign);
                        *isNull = false;
-                       return PointerGetDatum(array_source);
                }
 
-               if (array_source == NULL)
-                       return sourceData;      /* XXX do something else? */
-
                if (lIndex == NULL)
                        resultArray = array_set(array_source, i,
                                                                        upper.indx,
@@ -2538,6 +2554,120 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        return result;
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalFieldStore
+ *
+ *             Evaluate a FieldStore node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalFieldStore(FieldStoreState *fstate,
+                                  ExprContext *econtext,
+                                  bool *isNull,
+                                  ExprDoneCond *isDone)
+{
+       FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
+       HeapTuple       tuple;
+       Datum           tupDatum;
+       TupleDesc       tupDesc;
+       Datum      *values;
+       char       *nulls;
+       Datum           save_datum;
+       bool            save_isNull;
+       ListCell   *l1,
+                          *l2;
+
+       tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return tupDatum;
+
+       /* Lookup tupdesc if first time through or if type changes */
+       tupDesc = fstate->argdesc;
+       if (tupDesc == NULL ||
+               fstore->resulttype != tupDesc->tdtypeid)
+       {
+               MemoryContext oldcontext;
+
+               tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
+               /* Copy the tupdesc into query storage for safety */
+               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+               tupDesc = CreateTupleDescCopy(tupDesc);
+               if (fstate->argdesc)
+                       FreeTupleDesc(fstate->argdesc);
+               fstate->argdesc = tupDesc;
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /* Allocate workspace */
+       values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
+       nulls = (char *) palloc(tupDesc->natts * sizeof(char));
+
+       if (!*isNull)
+       {
+               /*
+                * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
+                * We set all the fields in the struct just in case.
+                */
+               HeapTupleHeader tuphdr;
+               HeapTupleData tmptup;
+
+               tuphdr = DatumGetHeapTupleHeader(tupDatum);
+               tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
+               ItemPointerSetInvalid(&(tmptup.t_self));
+               tmptup.t_tableOid = InvalidOid;
+               tmptup.t_data = tuphdr;
+
+               heap_deformtuple(&tmptup, tupDesc, values, nulls);
+       }
+       else
+       {
+               /* Convert null input tuple into an all-nulls row */
+               memset(nulls, 'n', tupDesc->natts * sizeof(char));
+       }
+
+       /* Result is never null */
+       *isNull = false;
+
+       save_datum = econtext->caseValue_datum;
+       save_isNull = econtext->caseValue_isNull;
+
+       forboth(l1, fstate->newvals, l2, fstore->fieldnums)
+       {
+               ExprState *newval = (ExprState *) lfirst(l1);
+               AttrNumber fieldnum = lfirst_int(l2);
+               bool            eisnull;
+
+               Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
+
+               /*
+                * Use the CaseTestExpr mechanism to pass down the old value of the
+                * field being replaced; this is useful in case we have a nested field
+                * update situation.  It's safe to reuse the CASE mechanism because
+                * there cannot be a CASE between here and where the value would be
+                * needed.
+                */
+               econtext->caseValue_datum = values[fieldnum - 1];
+               econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n');
+
+               values[fieldnum - 1] = ExecEvalExpr(newval,
+                                                                                       econtext,
+                                                                                       &eisnull,
+                                                                                       NULL);
+               nulls[fieldnum - 1] = eisnull ? 'n' : ' ';
+       }
+
+       econtext->caseValue_datum = save_datum;
+       econtext->caseValue_isNull = save_isNull;
+
+       tuple = heap_formtuple(tupDesc, values, nulls);
+
+       pfree(values);
+       pfree(nulls);
+
+       return HeapTupleGetDatum(tuple);
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalRelabelType
  *
@@ -2810,6 +2940,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) fstate;
                        }
                        break;
+               case T_FieldStore:
+                       {
+                               FieldStore *fstore = (FieldStore *) node;
+                               FieldStoreState *fstate = makeNode(FieldStoreState);
+
+                               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
+                               fstate->arg = ExecInitExpr(fstore->arg, parent);
+                               fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
+                               fstate->argdesc = NULL;
+                               state = (ExprState *) fstate;
+                       }
+                       break;
                case T_RelabelType:
                        {
                                RelabelType *relabel = (RelabelType *) node;
index f3086c84b16eeadea5a0d7cd7300c206c15823b4..bfcb82447d8e8c48ea0cbe6f2327b490d9a71c1e 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.284 2004/05/30 23:40:27 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.285 2004/06/09 19:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -844,6 +844,22 @@ _copyFieldSelect(FieldSelect *from)
        return newnode;
 }
 
+/*
+ * _copyFieldStore
+ */
+static FieldStore *
+_copyFieldStore(FieldStore *from)
+{
+       FieldStore *newnode = makeNode(FieldStore);
+
+       COPY_NODE_FIELD(arg);
+       COPY_NODE_FIELD(newvals);
+       COPY_NODE_FIELD(fieldnums);
+       COPY_SCALAR_FIELD(resulttype);
+
+       return newnode;
+}
+
 /*
  * _copyRelabelType
  */
@@ -1275,7 +1291,6 @@ _copyColumnRef(ColumnRef *from)
        ColumnRef  *newnode = makeNode(ColumnRef);
 
        COPY_NODE_FIELD(fields);
-       COPY_NODE_FIELD(indirection);
 
        return newnode;
 }
@@ -1286,8 +1301,6 @@ _copyParamRef(ParamRef *from)
        ParamRef   *newnode = makeNode(ParamRef);
 
        COPY_SCALAR_FIELD(number);
-       COPY_NODE_FIELD(fields);
-       COPY_NODE_FIELD(indirection);
 
        return newnode;
 }
@@ -1347,13 +1360,12 @@ _copyAIndices(A_Indices *from)
        return newnode;
 }
 
-static ExprFieldSelect *
-_copyExprFieldSelect(ExprFieldSelect *from)
+static A_Indirection *
+_copyA_Indirection(A_Indirection *from)
 {
-       ExprFieldSelect *newnode = makeNode(ExprFieldSelect);
+       A_Indirection *newnode = makeNode(A_Indirection);
 
        COPY_NODE_FIELD(arg);
-       COPY_NODE_FIELD(fields);
        COPY_NODE_FIELD(indirection);
 
        return newnode;
@@ -2648,6 +2660,9 @@ copyObject(void *from)
                case T_FieldSelect:
                        retval = _copyFieldSelect(from);
                        break;
+               case T_FieldStore:
+                       retval = _copyFieldStore(from);
+                       break;
                case T_RelabelType:
                        retval = _copyRelabelType(from);
                        break;
@@ -2984,8 +2999,8 @@ copyObject(void *from)
                case T_A_Indices:
                        retval = _copyAIndices(from);
                        break;
-               case T_ExprFieldSelect:
-                       retval = _copyExprFieldSelect(from);
+               case T_A_Indirection:
+                       retval = _copyA_Indirection(from);
                        break;
                case T_ResTarget:
                        retval = _copyResTarget(from);
index ca2511ea09537d7c631040aba5c2663821932af2..fec550836b30f9afc3359a56b7975f482da3d01e 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.223 2004/05/30 23:40:27 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.224 2004/06/09 19:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -350,6 +350,17 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b)
        return true;
 }
 
+static bool
+_equalFieldStore(FieldStore *a, FieldStore *b)
+{
+       COMPARE_NODE_FIELD(arg);
+       COMPARE_NODE_FIELD(newvals);
+       COMPARE_NODE_FIELD(fieldnums);
+       COMPARE_SCALAR_FIELD(resulttype);
+
+       return true;
+}
+
 static bool
 _equalRelabelType(RelabelType *a, RelabelType *b)
 {
@@ -1428,7 +1439,6 @@ static bool
 _equalColumnRef(ColumnRef *a, ColumnRef *b)
 {
        COMPARE_NODE_FIELD(fields);
-       COMPARE_NODE_FIELD(indirection);
 
        return true;
 }
@@ -1437,8 +1447,6 @@ static bool
 _equalParamRef(ParamRef *a, ParamRef *b)
 {
        COMPARE_SCALAR_FIELD(number);
-       COMPARE_NODE_FIELD(fields);
-       COMPARE_NODE_FIELD(indirection);
 
        return true;
 }
@@ -1474,10 +1482,9 @@ _equalAIndices(A_Indices *a, A_Indices *b)
 }
 
 static bool
-_equalExprFieldSelect(ExprFieldSelect *a, ExprFieldSelect *b)
+_equalA_Indirection(A_Indirection *a, A_Indirection *b)
 {
        COMPARE_NODE_FIELD(arg);
-       COMPARE_NODE_FIELD(fields);
        COMPARE_NODE_FIELD(indirection);
 
        return true;
@@ -1805,6 +1812,9 @@ equal(void *a, void *b)
                case T_FieldSelect:
                        retval = _equalFieldSelect(a, b);
                        break;
+               case T_FieldStore:
+                       retval = _equalFieldStore(a, b);
+                       break;
                case T_RelabelType:
                        retval = _equalRelabelType(a, b);
                        break;
@@ -2127,8 +2137,8 @@ equal(void *a, void *b)
                case T_A_Indices:
                        retval = _equalAIndices(a, b);
                        break;
-               case T_ExprFieldSelect:
-                       retval = _equalExprFieldSelect(a, b);
+               case T_A_Indirection:
+                       retval = _equalA_Indirection(a, b);
                        break;
                case T_ResTarget:
                        retval = _equalResTarget(a, b);
index cb6964c2d88d4a6f406c5c068fd2badedc79211d..1984ced756ffdcd11b147b161808fa3d99a89a67 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.238 2004/05/30 23:40:27 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.239 2004/06/09 19:08:15 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -745,6 +745,17 @@ _outFieldSelect(StringInfo str, FieldSelect *node)
        WRITE_INT_FIELD(resulttypmod);
 }
 
+static void
+_outFieldStore(StringInfo str, FieldStore *node)
+{
+       WRITE_NODE_TYPE("FIELDSTORE");
+
+       WRITE_NODE_FIELD(arg);
+       WRITE_NODE_FIELD(newvals);
+       WRITE_NODE_FIELD(fieldnums);
+       WRITE_OID_FIELD(resulttype);
+}
+
 static void
 _outRelabelType(StringInfo str, RelabelType *node)
 {
@@ -1166,8 +1177,24 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
 {
        WRITE_NODE_TYPE("SELECT");
 
-       /* XXX this is pretty durn incomplete */
        WRITE_NODE_FIELD(whereClause);
+       WRITE_NODE_FIELD(distinctClause);
+       WRITE_NODE_FIELD(into);
+       WRITE_NODE_FIELD(intoColNames);
+       WRITE_ENUM_FIELD(intoHasOids, ContainsOids);
+       WRITE_NODE_FIELD(targetList);
+       WRITE_NODE_FIELD(fromClause);
+       WRITE_NODE_FIELD(whereClause);
+       WRITE_NODE_FIELD(groupClause);
+       WRITE_NODE_FIELD(havingClause);
+       WRITE_NODE_FIELD(sortClause);
+       WRITE_NODE_FIELD(limitOffset);
+       WRITE_NODE_FIELD(limitCount);
+       WRITE_NODE_FIELD(forUpdate);
+       WRITE_ENUM_FIELD(op, SetOperation);
+       WRITE_BOOL_FIELD(all);
+       WRITE_NODE_FIELD(larg);
+       WRITE_NODE_FIELD(rarg);
 }
 
 static void
@@ -1181,6 +1208,15 @@ _outFuncCall(StringInfo str, FuncCall *node)
        WRITE_BOOL_FIELD(agg_distinct);
 }
 
+static void
+_outDefElem(StringInfo str, DefElem *node)
+{
+       WRITE_NODE_TYPE("DEFELEM");
+
+       WRITE_STRING_FIELD(defname);
+       WRITE_NODE_FIELD(arg);
+}
+
 static void
 _outColumnDef(StringInfo str, ColumnDef *node)
 {
@@ -1439,7 +1475,6 @@ _outColumnRef(StringInfo str, ColumnRef *node)
        WRITE_NODE_TYPE("COLUMNREF");
 
        WRITE_NODE_FIELD(fields);
-       WRITE_NODE_FIELD(indirection);
 }
 
 static void
@@ -1448,29 +1483,45 @@ _outParamRef(StringInfo str, ParamRef *node)
        WRITE_NODE_TYPE("PARAMREF");
 
        WRITE_INT_FIELD(number);
-       WRITE_NODE_FIELD(fields);
-       WRITE_NODE_FIELD(indirection);
 }
 
 static void
 _outAConst(StringInfo str, A_Const *node)
 {
-       WRITE_NODE_TYPE("CONST ");
+       WRITE_NODE_TYPE("A_CONST");
 
        _outValue(str, &(node->val));
        WRITE_NODE_FIELD(typename);
 }
 
 static void
-_outExprFieldSelect(StringInfo str, ExprFieldSelect *node)
+_outA_Indices(StringInfo str, A_Indices *node)
 {
-       WRITE_NODE_TYPE("EXPRFIELDSELECT");
+       WRITE_NODE_TYPE("A_INDICES");
+
+       WRITE_NODE_FIELD(lidx);
+       WRITE_NODE_FIELD(uidx);
+}
+
+static void
+_outA_Indirection(StringInfo str, A_Indirection *node)
+{
+       WRITE_NODE_TYPE("A_INDIRECTION");
 
        WRITE_NODE_FIELD(arg);
-       WRITE_NODE_FIELD(fields);
        WRITE_NODE_FIELD(indirection);
 }
 
+static void
+_outResTarget(StringInfo str, ResTarget *node)
+{
+       WRITE_NODE_TYPE("RESTARGET");
+
+       WRITE_STRING_FIELD(name);
+       WRITE_NODE_FIELD(indirection);
+       WRITE_NODE_FIELD(val);
+}
+
 static void
 _outConstraint(StringInfo str, Constraint *node)
 {
@@ -1666,6 +1717,9 @@ _outNode(StringInfo str, void *obj)
                        case T_FieldSelect:
                                _outFieldSelect(str, obj);
                                break;
+                       case T_FieldStore:
+                               _outFieldStore(str, obj);
+                               break;
                        case T_RelabelType:
                                _outRelabelType(str, obj);
                                break;
@@ -1815,8 +1869,14 @@ _outNode(StringInfo str, void *obj)
                        case T_A_Const:
                                _outAConst(str, obj);
                                break;
-                       case T_ExprFieldSelect:
-                               _outExprFieldSelect(str, obj);
+                       case T_A_Indices:
+                               _outA_Indices(str, obj);
+                               break;
+                       case T_A_Indirection:
+                               _outA_Indirection(str, obj);
+                               break;
+                       case T_ResTarget:
+                               _outResTarget(str, obj);
                                break;
                        case T_Constraint:
                                _outConstraint(str, obj);
@@ -1827,6 +1887,9 @@ _outNode(StringInfo str, void *obj)
                        case T_FuncCall:
                                _outFuncCall(str, obj);
                                break;
+                       case T_DefElem:
+                               _outDefElem(str, obj);
+                               break;
 
                        default:
 
index 225bf6241fa0d4a23bf985c644a8a17904823bc9..e3fa983cea0750da8e72cdc08d60d170a5a1dc51 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.171 2004/05/30 23:40:27 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.172 2004/06/09 19:08:15 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -543,6 +543,22 @@ _readFieldSelect(void)
        READ_DONE();
 }
 
+/*
+ * _readFieldStore
+ */
+static FieldStore *
+_readFieldStore(void)
+{
+       READ_LOCALS(FieldStore);
+
+       READ_NODE_FIELD(arg);
+       READ_NODE_FIELD(newvals);
+       READ_NODE_FIELD(fieldnums);
+       READ_OID_FIELD(resulttype);
+
+       READ_DONE();
+}
+
 /*
  * _readRelabelType
  */
@@ -814,17 +830,6 @@ _readFromExpr(void)
  *     Stuff from parsenodes.h.
  */
 
-static ColumnRef *
-_readColumnRef(void)
-{
-       READ_LOCALS(ColumnRef);
-
-       READ_NODE_FIELD(fields);
-       READ_NODE_FIELD(indirection);
-
-       READ_DONE();
-}
-
 static ColumnDef *
 _readColumnDef(void)
 {
@@ -859,18 +864,6 @@ _readTypeName(void)
        READ_DONE();
 }
 
-static ExprFieldSelect *
-_readExprFieldSelect(void)
-{
-       READ_LOCALS(ExprFieldSelect);
-
-       READ_NODE_FIELD(arg);
-       READ_NODE_FIELD(fields);
-       READ_NODE_FIELD(indirection);
-
-       READ_DONE();
-}
-
 /*
  * _readRangeTblEntry
  */
@@ -974,6 +967,8 @@ parseNodeString(void)
                return_value = _readSubLink();
        else if (MATCH("FIELDSELECT", 11))
                return_value = _readFieldSelect();
+       else if (MATCH("FIELDSTORE", 10))
+               return_value = _readFieldStore();
        else if (MATCH("RELABELTYPE", 11))
                return_value = _readRelabelType();
        else if (MATCH("CASE", 4))
@@ -1008,14 +1003,10 @@ parseNodeString(void)
                return_value = _readJoinExpr();
        else if (MATCH("FROMEXPR", 8))
                return_value = _readFromExpr();
-       else if (MATCH("COLUMNREF", 9))
-               return_value = _readColumnRef();
        else if (MATCH("COLUMNDEF", 9))
                return_value = _readColumnDef();
        else if (MATCH("TYPENAME", 8))
                return_value = _readTypeName();
-       else if (MATCH("EXPRFIELDSELECT", 15))
-               return_value = _readExprFieldSelect();
        else if (MATCH("RTE", 3))
                return_value = _readRangeTblEntry();
        else if (MATCH("NOTIFY", 6))
index b22360ac2b128f2b209873d349b4bac2f3ad2751..a3a2ddfbd830a3a858d93e263a484ced584e91d4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.174 2004/06/05 19:48:08 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.175 2004/06/09 19:08:16 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -724,6 +724,13 @@ contain_nonstrict_functions_walker(Node *node, void *context)
                /* an aggregate could return non-null with null input */
                return true;
        }
+       if (IsA(node, ArrayRef))
+       {
+               /* array assignment is nonstrict */
+               if (((ArrayRef *) node)->refassgnexpr != NULL)
+                       return true;
+               /* else fall through to check args */
+       }
        if (IsA(node, FuncExpr))
        {
                FuncExpr   *expr = (FuncExpr *) node;
@@ -771,6 +778,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, SubPlan))
                return true;
+       if (IsA(node, FieldStore))
+               return true;
        if (IsA(node, CaseExpr))
                return true;
        if (IsA(node, CaseWhen))
@@ -2450,6 +2459,16 @@ expression_tree_walker(Node *node,
                        break;
                case T_FieldSelect:
                        return walker(((FieldSelect *) node)->arg, context);
+               case T_FieldStore:
+                       {
+                               FieldStore   *fstore = (FieldStore *) node;
+
+                               if (walker(fstore->arg, context))
+                                       return true;
+                               if (walker(fstore->newvals, context))
+                                       return true;
+                       }
+                       break;
                case T_RelabelType:
                        return walker(((RelabelType *) node)->arg, context);
                case T_CaseExpr:
@@ -2840,6 +2859,18 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_FieldStore:
+                       {
+                               FieldStore *fstore = (FieldStore *) node;
+                               FieldStore *newnode;
+
+                               FLATCOPY(newnode, fstore, FieldStore);
+                               MUTATE(newnode->arg, fstore->arg, Expr *);
+                               MUTATE(newnode->newvals, fstore->newvals, List *);
+                               newnode->fieldnums = list_copy(fstore->fieldnums);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_RelabelType:
                        {
                                RelabelType *relabel = (RelabelType *) node;
index 104716bf4557b8253593eb0ee6f19b647cf48d23..f6cdf96e296f77834f3766674108c2546972ff39 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.303 2004/06/04 03:24:04 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.304 2004/06/09 19:08:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2461,6 +2461,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                if (origTargetList == NULL)
                        elog(ERROR, "UPDATE target count mismatch --- internal error");
                origTarget = (ResTarget *) lfirst(origTargetList);
+               Assert(IsA(origTarget, ResTarget));
 
                updateTargetListEntry(pstate, tle, origTarget->name,
                                                          attnameAttNum(pstate->p_target_relation,
index 61a3bd477e65f57c61dc479e782229f567fb39a5..07a882f3db50906fadf17cd2e9255b30226a6ab1 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.460 2004/06/02 21:01:09 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.461 2004/06/09 19:08:17 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -75,6 +75,7 @@ static bool QueryIsRule = FALSE;
  */
 /*#define __YYSCLASS*/
 
+static Node *makeColumnRef(char *relname, List *indirection);
 static Node *makeTypeCast(Node *arg, TypeName *typename);
 static Node *makeStringConst(char *str, TypeName *typename);
 static Node *makeIntConst(int val);
@@ -84,6 +85,7 @@ static Node *makeRowNullTest(NullTestType test, RowExpr *row);
 static DefElem *makeDefElem(char *name, Node *arg);
 static A_Const *makeBoolAConst(bool state);
 static FuncCall *makeOverlaps(List *largs, List *rargs);
+static List *check_func_name(List *names);
 static List *extractArgTypes(List *parameters);
 static SelectStmt *findLeftmostSelect(SelectStmt *node);
 static void insertSelectOptions(SelectStmt *stmt,
@@ -110,7 +112,6 @@ static void doNegateFloat(Value *v);
        List                            *list;
        Node                            *node;
        Value                           *value;
-       ColumnRef                       *columnref;
        ObjectType                      objtype;
 
        TypeName                        *typnam;
@@ -221,9 +222,9 @@ static void doNegateFloat(Value *v);
                                sort_clause opt_sort_clause sortby_list index_params
                                name_list from_clause from_list opt_array_bounds
                                qualified_name_list any_name any_name_list
-                               any_operator expr_list dotted_name attrs
+                               any_operator expr_list attrs
                                target_list update_target_list insert_column_list
-                               insert_target_list def_list opt_indirection
+                               insert_target_list def_list indirection opt_indirection
                                group_clause TriggerFuncArgs select_limit
                                opt_select_limit opclass_item_list transaction_mode_list
                                transaction_mode_list_or_empty
@@ -274,8 +275,8 @@ static void doNegateFloat(Value *v);
 %type <node>   TableElement ConstraintElem TableFuncElement
 %type <node>   columnDef
 %type <defelt> def_elem
-%type <node>   def_arg columnElem where_clause insert_column_item
-                               a_expr b_expr c_expr AexprConst
+%type <node>   def_arg columnElem where_clause
+                               a_expr b_expr c_expr AexprConst indirection_el columnref
                                in_expr having_clause func_table array_expr
 %type <list>   row type_list array_expr_list
 %type <node>   case_expr case_arg when_clause case_default
@@ -284,14 +285,13 @@ static void doNegateFloat(Value *v);
 %type <list>   OptCreateAs CreateAsList
 %type <node>   CreateAsElement
 %type <value>  NumericOnly FloatOnly IntegerOnly
-%type <columnref>      columnref
 %type <alias>  alias_clause
 %type <sortby> sortby
 %type <ielem>  index_elem
 %type <node>   table_ref
 %type <jexpr>  joined_table
 %type <range>  relation_expr
-%type <target> target_el insert_target_el update_target_el
+%type <target> target_el insert_target_el update_target_el insert_column_item
 
 %type <typnam> Typename SimpleTypename ConstTypename
                                GenericType Numeric opt_float
@@ -2102,12 +2102,11 @@ opt_trusted:
 
 /* This ought to be just func_name, but that causes reduce/reduce conflicts
  * (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
- * Work around by using name and dotted_name separately.
+ * Work around by using simple names, instead.
  */
 handler_name:
-                       name
-                                                       { $$ = list_make1(makeString($1)); }
-                       | dotted_name                                                   { $$ = $1; }
+                       name                                            { $$ = list_make1(makeString($1)); }
+                       | name attrs                            { $$ = lcons(makeString($1), $2); }
                ;
 
 opt_lancompiler:
@@ -2578,9 +2577,20 @@ any_name_list:
                ;
 
 any_name:      ColId                                           { $$ = list_make1(makeString($1)); }
-                       | dotted_name                           { $$ = $1; }
+                       | ColId attrs                           { $$ = lcons(makeString($1), $2); }
                ;
 
+/*
+ * The slightly convoluted way of writing this production avoids reduce/reduce
+ * errors against indirection_el.
+ */
+attrs:         '.' attr_name
+                                       { $$ = list_make1(makeString($2)); }
+                       | '.' attr_name attrs
+                                       { $$ = lcons(makeString($2), $3); }
+               ;
+
+
 /*****************************************************************************
  *
  *             QUERY:
@@ -4387,7 +4397,8 @@ insert_rest:
                ;
 
 insert_column_list:
-                       insert_column_item                                              { $$ = list_make1($1); }
+                       insert_column_item
+                                       { $$ = list_make1($1); }
                        | insert_column_list ',' insert_column_item
                                        { $$ = lappend($1, $3); }
                ;
@@ -4395,11 +4406,10 @@ insert_column_list:
 insert_column_item:
                        ColId opt_indirection
                                {
-                                       ResTarget *n = makeNode(ResTarget);
-                                       n->name = $1;
-                                       n->indirection = $2;
-                                       n->val = NULL;
-                                       $$ = (Node *)n;
+                                       $$ = makeNode(ResTarget);
+                                       $$->name = $1;
+                                       $$->indirection = $2;
+                                       $$->val = NULL;
                                }
                ;
 
@@ -6203,35 +6213,28 @@ b_expr:         c_expr
  * inside parentheses, such as function arguments; that cannot introduce
  * ambiguity to the b_expr syntax.
  */
-c_expr:                columnref                                                               { $$ = (Node *) $1; }
+c_expr:                columnref                                                               { $$ = $1; }
                        | AexprConst                                                    { $$ = $1; }
-                       | PARAM attrs opt_indirection
-                               {
-                                       /*
-                                        * PARAM without field names is considered a constant,
-                                        * but with 'em, it is not.  Not very consistent ...
-                                        */
-                                       ParamRef *n = makeNode(ParamRef);
-                                       n->number = $1;
-                                       n->fields = $2;
-                                       n->indirection = $3;
-                                       $$ = (Node *)n;
-                               }
-                       | '(' a_expr ')' attrs opt_indirection
+                       | PARAM opt_indirection
                                {
-                                       ExprFieldSelect *n = makeNode(ExprFieldSelect);
-                                       n->arg = $2;
-                                       n->fields = $4;
-                                       n->indirection = $5;
-                                       $$ = (Node *)n;
+                                       ParamRef *p = makeNode(ParamRef);
+                                       p->number = $1;
+                                       if ($2)
+                                       {
+                                               A_Indirection *n = makeNode(A_Indirection);
+                                               n->arg = (Node *) p;
+                                               n->indirection = $2;
+                                               $$ = (Node *) n;
+                                       }
+                                       else
+                                               $$ = (Node *) p;
                                }
                        | '(' a_expr ')' opt_indirection
                                {
                                        if ($4)
                                        {
-                                               ExprFieldSelect *n = makeNode(ExprFieldSelect);
+                                               A_Indirection *n = makeNode(A_Indirection);
                                                n->arg = $2;
-                                               n->fields = NIL;
                                                n->indirection = $4;
                                                $$ = (Node *)n;
                                        }
@@ -6806,25 +6809,6 @@ subquery_Op:
  */
                        ;
 
-opt_indirection:
-                       opt_indirection '[' a_expr ']'
-                               {
-                                       A_Indices *ai = makeNode(A_Indices);
-                                       ai->lidx = NULL;
-                                       ai->uidx = $3;
-                                       $$ = lappend($1, ai);
-                               }
-                       | opt_indirection '[' a_expr ':' a_expr ']'
-                               {
-                                       A_Indices *ai = makeNode(A_Indices);
-                                       ai->lidx = $3;
-                                       ai->uidx = $5;
-                                       $$ = lappend($1, ai);
-                               }
-                       | /*EMPTY*/
-                               { $$ = NIL; }
-               ;
-
 expr_list:     a_expr
                                {
                                        $$ = list_make1($1);
@@ -7050,42 +7034,58 @@ case_arg:       a_expr                                                                  { $$ = $1; }
  * references can be accepted. Note that when there are more than two
  * dotted names, the first name is not actually a relation name...
  */
-columnref:     relation_name opt_indirection
+columnref:     relation_name
                                {
-                                       $$ = makeNode(ColumnRef);
-                                       $$->fields = list_make1(makeString($1));
-                                       $$->indirection = $2;
+                                       $$ = makeColumnRef($1, NIL);
                                }
-                       | dotted_name opt_indirection
+                       | relation_name indirection
                                {
-                                       $$ = makeNode(ColumnRef);
-                                       $$->fields = $1;
-                                       $$->indirection = $2;
+                                       $$ = makeColumnRef($1, $2);
                                }
                ;
 
-dotted_name:
-                       relation_name attrs
-                                       { $$ = lcons(makeString($1), $2); }
+indirection_el:
+                       '.' attr_name
+                               {
+                                       $$ = (Node *) makeString($2);
+                               }
+                       | '.' '*'
+                               {
+                                       $$ = (Node *) makeString("*");
+                               }
+                       | '[' a_expr ']'
+                               {
+                                       A_Indices *ai = makeNode(A_Indices);
+                                       ai->lidx = NULL;
+                                       ai->uidx = $2;
+                                       $$ = (Node *) ai;
+                               }
+                       | '[' a_expr ':' a_expr ']'
+                               {
+                                       A_Indices *ai = makeNode(A_Indices);
+                                       ai->lidx = $2;
+                                       ai->uidx = $4;
+                                       $$ = (Node *) ai;
+                               }
                ;
 
-attrs:         '.' attr_name
-                                       { $$ = list_make1(makeString($2)); }
-                       | '.' '*'
-                                       { $$ = list_make1(makeString("*")); }
-                       | '.' attr_name attrs
-                                       { $$ = lcons(makeString($2), $3); }
+indirection:
+                       indirection_el                                                  { $$ = list_make1($1); }
+                       | indirection indirection_el                    { $$ = lappend($1, $2); }
+               ;
+
+opt_indirection:
+                       /*EMPTY*/                                                               { $$ = NIL; }
+                       | opt_indirection indirection_el                { $$ = lappend($1, $2); }
                ;
 
 
 /*****************************************************************************
  *
- *     target lists
+ *     target lists for SELECT, UPDATE, INSERT
  *
  *****************************************************************************/
 
-/* Target lists as found in SELECT ... and INSERT VALUES ( ... ) */
-
 target_list:
                        target_el                                                               { $$ = list_make1($1); }
                        | target_list ',' target_el                             { $$ = lappend($1, $3); }
@@ -7110,7 +7110,7 @@ target_el:        a_expr AS ColLabel
                                {
                                        ColumnRef *n = makeNode(ColumnRef);
                                        n->fields = list_make1(makeString("*"));
-                                       n->indirection = NIL;
+
                                        $$ = makeNode(ResTarget);
                                        $$->name = NULL;
                                        $$->indirection = NIL;
@@ -7118,12 +7118,6 @@ target_el:       a_expr AS ColLabel
                                }
                ;
 
-/* Target list as found in UPDATE table SET ...
-| '(' row_ ')' = '(' row_ ')'
-{
-       $$ = NULL;
-}
- */
 update_target_list:
                        update_target_el                                                { $$ = list_make1($1); }
                        | update_target_list ',' update_target_el { $$ = lappend($1,$3); }
@@ -7153,7 +7147,13 @@ insert_target_list:
                ;
 
 insert_target_el:
-                       target_el                                                               { $$ = $1; }
+                       a_expr
+                               {
+                                       $$ = makeNode(ResTarget);
+                                       $$->name = NULL;
+                                       $$->indirection = NIL;
+                                       $$->val = (Node *)$1;
+                               }
                        | DEFAULT
                                {
                                        $$ = makeNode(ResTarget);
@@ -7188,26 +7188,26 @@ qualified_name:
                                        $$->schemaname = NULL;
                                        $$->relname = $1;
                                }
-                       | dotted_name
+                       | relation_name attrs
                                {
                                        $$ = makeNode(RangeVar);
-                                       switch (list_length($1))
+                                       switch (list_length($2))
                                        {
-                                               case 2:
+                                               case 1:
                                                        $$->catalogname = NULL;
-                                                       $$->schemaname = strVal(linitial($1));
-                                                       $$->relname = strVal(lsecond($1));
+                                                       $$->schemaname = $1;
+                                                       $$->relname = strVal(linitial($2));
                                                        break;
-                                               case 3:
-                                                       $$->catalogname = strVal(linitial($1));
-                                                       $$->schemaname = strVal(lsecond($1));
-                                                       $$->relname = strVal(lthird($1));
+                                               case 2:
+                                                       $$->catalogname = $1;
+                                                       $$->schemaname = strVal(linitial($2));
+                                                       $$->relname = strVal(lsecond($2));
                                                        break;
                                                default:
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                                                         errmsg("improper qualified name (too many dotted names): %s",
-                                                                                       NameListToString($1))));
+                                                                                       NameListToString(lcons(makeString($1), $2)))));
                                                        break;
                                        }
                                }
@@ -7234,9 +7234,18 @@ index_name: ColId                                                                        { $$ = $1; };
 
 file_name:     Sconst                                                                  { $$ = $1; };
 
+/*
+ * The production for a qualified func_name has to exactly match the
+ * production for a qualified columnref, because we cannot tell which we
+ * are parsing until we see what comes after it ('(' for a func_name,
+ * anything else for a columnref).  Therefore we allow 'indirection' which
+ * may contain subscripts, and reject that case in the C code.  (If we
+ * ever implement SQL99-like methods, such syntax may actually become legal!)
+ */
 func_name:     function_name
                                        { $$ = list_make1(makeString($1)); }
-                       | dotted_name                                                   { $$ = $1; }
+                       | relation_name indirection
+                                       { $$ = check_func_name(lcons(makeString($1), $2)); }
                ;
 
 
@@ -7325,14 +7334,6 @@ AexprConst: Iconst
                                        n->typename->typmod = INTERVAL_TYPMOD($3, $6);
                                        $$ = (Node *)n;
                                }
-                       | PARAM opt_indirection
-                               {
-                                       ParamRef *n = makeNode(ParamRef);
-                                       n->number = $1;
-                                       n->fields = NIL;
-                                       n->indirection = $2;
-                                       $$ = (Node *)n;
-                               }
                        | TRUE_P
                                {
                                        $$ = (Node *)makeBoolAConst(TRUE);
@@ -7781,6 +7782,48 @@ SpecialRuleRelation:
 
 %%
 
+static Node *
+makeColumnRef(char *relname, List *indirection)
+{
+       /*
+        * Generate a ColumnRef node, with an A_Indirection node added if there
+        * is any subscripting in the specified indirection list.  However,
+        * any field selection at the start of the indirection list must be
+        * transposed into the "fields" part of the ColumnRef node.
+        */
+       ColumnRef  *c = makeNode(ColumnRef);
+       int             nfields = 0;
+       ListCell *l;
+
+       foreach(l, indirection)
+       {
+               if (IsA(lfirst(l), A_Indices))
+               {
+                       A_Indirection *i = makeNode(A_Indirection);
+
+                       if (nfields == 0)
+                       {
+                               /* easy case - all indirection goes to A_Indirection */
+                               c->fields = list_make1(makeString(relname));
+                               i->indirection = indirection;
+                       }
+                       else
+                       {
+                               /* got to split the list in two */
+                               i->indirection = list_copy_tail(indirection, nfields);
+                               indirection = list_truncate(indirection, nfields);
+                               c->fields = lcons(makeString(relname), indirection);
+                       }
+                       i->arg = (Node *) c;
+                       return (Node *) i;
+               }
+               nfields++;
+       }
+       /* No subscripting, so all indirection gets added to field list */
+       c->fields = lcons(makeString(relname), indirection);
+       return (Node *) c;
+}
+
 static Node *
 makeTypeCast(Node *arg, TypeName *typename)
 {
@@ -7945,6 +7988,26 @@ makeOverlaps(List *largs, List *rargs)
        return n;
 }
 
+/* check_func_name --- check the result of func_name production
+ *
+ * It's easiest to let the grammar production for func_name allow subscripts
+ * and '*', which we then must reject here.
+ */
+static List *
+check_func_name(List *names)
+{
+       ListCell   *i;
+
+       foreach(i, names)
+       {
+               if (!IsA(lfirst(i), String))
+                       yyerror("syntax error");
+               else if (strcmp(strVal(lfirst(i)), "*") == 0)
+                       yyerror("syntax error");
+       }
+       return names;
+}
+
 /* extractArgTypes()
  * Given a list of FunctionParameter nodes, extract a list of just the
  * argument types (TypeNames).  Most of the productions using func_args
index a0901662b8326e4d5fb0ac4d526e5dc042761c6b..a2cd7dccc15b7cc5de196ecf2c28a65f1e4c7c80 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.131 2004/05/30 23:40:34 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1122,8 +1122,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
         *----------
         */
        if (IsA(node, ColumnRef) &&
-               list_length(((ColumnRef *) node)->fields) == 1 &&
-               ((ColumnRef *) node)->indirection == NIL)
+               list_length(((ColumnRef *) node)->fields) == 1)
        {
                char       *name = strVal(linitial(((ColumnRef *) node)->fields));
 
index 5dbac6338bcb064324bd4b2fa13228a84943e06d..3b4ad7cf8a06ce0636b24ed6c81e478d09550569 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.172 2004/05/30 23:40:35 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.173 2004/06/09 19:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,7 +100,6 @@ transformExpr(ParseState *pstate, Node *expr)
                                int                     paramno = pref->number;
                                ParseState *toppstate;
                                Param      *param;
-                               ListCell   *fields;
 
                                /*
                                 * Find topmost ParseState, which is where paramtype info
@@ -148,18 +147,6 @@ transformExpr(ParseState *pstate, Node *expr)
                                param->paramid = (AttrNumber) paramno;
                                param->paramtype = toppstate->p_paramtypes[paramno - 1];
                                result = (Node *) param;
-
-                               /* handle qualification, if any */
-                               foreach(fields, pref->fields)
-                               {
-                                       result = ParseFuncOrColumn(pstate,
-                                                                                          list_make1(lfirst(fields)),
-                                                                                          list_make1(result),
-                                                                                          false, false, true);
-                               }
-                               /* handle subscripts, if any */
-                               result = transformIndirection(pstate, result,
-                                                                                         pref->indirection);
                                break;
                        }
                case T_A_Const:
@@ -173,23 +160,13 @@ transformExpr(ParseState *pstate, Node *expr)
                                                                                                 con->typename);
                                break;
                        }
-               case T_ExprFieldSelect:
+               case T_A_Indirection:
                        {
-                               ExprFieldSelect *efs = (ExprFieldSelect *) expr;
-                               ListCell            *fields;
+                               A_Indirection   *ind = (A_Indirection *) expr;
 
-                               result = transformExpr(pstate, efs->arg);
-                               /* handle qualification, if any */
-                               foreach(fields, efs->fields)
-                               {
-                                       result = ParseFuncOrColumn(pstate,
-                                                                                          list_make1(lfirst(fields)),
-                                                                                          list_make1(result),
-                                                                                          false, false, true);
-                               }
-                               /* handle subscripts, if any */
+                               result = transformExpr(pstate, ind->arg);
                                result = transformIndirection(pstate, result,
-                                                                                         efs->indirection);
+                                                                                         ind->indirection);
                                break;
                        }
                case T_TypeCast:
@@ -961,6 +938,7 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_NullIfExpr:
                case T_BoolExpr:
                case T_FieldSelect:
+               case T_FieldStore:
                case T_RelabelType:
                case T_CaseTestExpr:
                case T_CoerceToDomain:
@@ -983,15 +961,55 @@ transformExpr(ParseState *pstate, Node *expr)
 static Node *
 transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
 {
-       if (indirection == NIL)
-               return basenode;
-       return (Node *) transformArraySubscripts(pstate,
-                                                                                        basenode,
-                                                                                        exprType(basenode),
-                                                                                        exprTypmod(basenode),
-                                                                                        indirection,
-                                                                                        false,
-                                                                                        NULL);
+       Node       *result = basenode;
+       List       *subscripts = NIL;
+       ListCell   *i;
+
+       /*
+        * We have to split any field-selection operations apart from
+        * subscripting.  Adjacent A_Indices nodes have to be treated
+        * as a single multidimensional subscript operation.
+        */
+       foreach(i, indirection)
+       {
+               Node    *n = lfirst(i);
+
+               if (IsA(n, A_Indices))
+               {
+                       subscripts = lappend(subscripts, n);
+               }
+               else
+               {
+                       Assert(IsA(n, String));
+
+                       /* process subscripts before this field selection */
+                       if (subscripts)
+                               result = (Node *) transformArraySubscripts(pstate,
+                                                                                                                  result,
+                                                                                                                  exprType(result),
+                                                                                                                  InvalidOid,
+                                                                                                                  -1,
+                                                                                                                  subscripts,
+                                                                                                                  NULL);
+                       subscripts = NIL;
+
+                       result = ParseFuncOrColumn(pstate,
+                                                                          list_make1(n),
+                                                                          list_make1(result),
+                                                                          false, false, true);
+               }
+       }
+       /* process trailing subscripts, if any */
+       if (subscripts)
+               result = (Node *) transformArraySubscripts(pstate,
+                                                                                                  result,
+                                                                                                  exprType(result),
+                                                                                                  InvalidOid,
+                                                                                                  -1,
+                                                                                                  subscripts,
+                                                                                                  NULL);
+
+       return result;
 }
 
 static Node *
@@ -1051,17 +1069,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                        }
 
                                        /*
-                                        * Try to find the name as a relation ... but not if
-                                        * subscripts appear.  Note also that only relations
-                                        * already entered into the rangetable will be
+                                        * Try to find the name as a relation.  Note that only
+                                        * relations already entered into the rangetable will be
                                         * recognized.
                                         *
                                         * This is a hack for backwards compatibility with
                                         * PostQUEL-inspired syntax.  The preferred form now
                                         * is "rel.*".
                                         */
-                                       if (cref->indirection == NIL &&
-                                               refnameRangeTblEntry(pstate, NULL, name,
+                                       if (refnameRangeTblEntry(pstate, NULL, name,
                                                                                         &levels_up) != NULL)
                                                node = transformWholeRowRef(pstate, NULL, name);
                                        else
@@ -1172,7 +1188,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                        break;
        }
 
-       return transformIndirection(pstate, node, cref->indirection);
+       return node;
 }
 
 /*
@@ -1385,6 +1401,9 @@ exprType(Node *expr)
                case T_FieldSelect:
                        type = ((FieldSelect *) expr)->resulttype;
                        break;
+               case T_FieldStore:
+                       type = ((FieldStore *) expr)->resulttype;
+                       break;
                case T_RelabelType:
                        type = ((RelabelType *) expr)->resulttype;
                        break;
index c46c27481a549533478a02049626492841c74b2c..c95fe6650dacb79956e37b92bb7fd780e80b907c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.83 2004/05/26 04:41:30 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.84 2004/06/09 19:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,6 +67,39 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
        return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
 }
 
+/*
+ * transformArrayType()
+ *             Get the element type of an array type in preparation for subscripting
+ */
+Oid
+transformArrayType(Oid arrayType)
+{
+       Oid                     elementType;
+       HeapTuple       type_tuple_array;
+       Form_pg_type type_struct_array;
+
+       /* Get the type tuple for the array */
+       type_tuple_array = SearchSysCache(TYPEOID,
+                                                                         ObjectIdGetDatum(arrayType),
+                                                                         0, 0, 0);
+       if (!HeapTupleIsValid(type_tuple_array))
+               elog(ERROR, "cache lookup failed for type %u", arrayType);
+       type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
+
+       /* needn't check typisdefined since this will fail anyway */
+
+       elementType = type_struct_array->typelem;
+       if (elementType == InvalidOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                       errmsg("cannot subscript type %s because it is not an array",
+                                  format_type_be(arrayType))));
+
+       ReleaseSysCache(type_tuple_array);
+
+       return elementType;
+}
+
 /*
  * transformArraySubscripts()
  *             Transform array subscripting.  This is used for both
@@ -83,68 +116,49 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
  *
  * pstate              Parse state
  * arrayBase   Already-transformed expression for the array as a whole
- *                             (may be NULL if we are handling an INSERT)
- * arrayType   OID of array's datatype
- * arrayTypMod typmod to be applied to array elements
+ * arrayType   OID of array's datatype (should match type of arrayBase)
+ * elementType OID of array's element type (fetch with transformArrayType,
+ *                             or pass InvalidOid to do it here)
+ * elementTypMod typmod to be applied to array elements (if storing)
  * indirection Untransformed list of subscripts (must not be NIL)
- * forceSlice  If true, treat subscript as array slice in all cases
  * assignFrom  NULL for array fetch, else transformed expression for source.
  */
 ArrayRef *
 transformArraySubscripts(ParseState *pstate,
                                                 Node *arrayBase,
                                                 Oid arrayType,
-                                                int32 arrayTypMod,
+                                                Oid elementType,
+                                                int32 elementTypMod,
                                                 List *indirection,
-                                                bool forceSlice,
                                                 Node *assignFrom)
 {
-       Oid                     elementType,
-                               resultType;
-       HeapTuple       type_tuple_array;
-       Form_pg_type type_struct_array;
-       bool            isSlice = forceSlice;
+       Oid                     resultType;
+       bool            isSlice = false;
        List       *upperIndexpr = NIL;
        List       *lowerIndexpr = NIL;
        ListCell   *idx;
        ArrayRef   *aref;
 
-       /* Get the type tuple for the array */
-       type_tuple_array = SearchSysCache(TYPEOID,
-                                                                         ObjectIdGetDatum(arrayType),
-                                                                         0, 0, 0);
-       if (!HeapTupleIsValid(type_tuple_array))
-               elog(ERROR, "cache lookup failed for type %u", arrayType);
-       type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
-
-       elementType = type_struct_array->typelem;
-       if (elementType == InvalidOid)
-               ereport(ERROR,
-                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                       errmsg("cannot subscript type %s because it is not an array",
-                                  format_type_be(arrayType))));
+       /* Caller may or may not have bothered to determine elementType */
+       if (!OidIsValid(elementType))
+               elementType = transformArrayType(arrayType);
 
        /*
         * A list containing only single subscripts refers to a single array
         * element.  If any of the items are double subscripts (lower:upper),
         * then the subscript expression means an array slice operation. In
         * this case, we supply a default lower bound of 1 for any items that
-        * contain only a single subscript. The forceSlice parameter forces us
-        * to treat the operation as a slice, even if no lower bounds are
-        * mentioned.  Otherwise, we have to prescan the indirection list to
-        * see if there are any double subscripts.
+        * contain only a single subscript.  We have to prescan the indirection
+        * list to see if there are any double subscripts.
         */
-       if (!isSlice)
+       foreach(idx, indirection)
        {
-               foreach(idx, indirection)
-               {
-                       A_Indices  *ai = (A_Indices *) lfirst(idx);
+               A_Indices  *ai = (A_Indices *) lfirst(idx);
 
-                       if (ai->lidx != NULL)
-                       {
-                               isSlice = true;
-                               break;
-                       }
+               if (ai->lidx != NULL)
+               {
+                       isSlice = true;
+                       break;
                }
        }
 
@@ -166,6 +180,7 @@ transformArraySubscripts(ParseState *pstate,
                A_Indices  *ai = (A_Indices *) lfirst(idx);
                Node       *subexpr;
 
+               Assert(IsA(ai, A_Indices));
                if (isSlice)
                {
                        if (ai->lidx)
@@ -209,28 +224,26 @@ transformArraySubscripts(ParseState *pstate,
 
        /*
         * If doing an array store, coerce the source value to the right type.
+        * (This should agree with the coercion done by updateTargetListEntry.)
         */
        if (assignFrom != NULL)
        {
                Oid                     typesource = exprType(assignFrom);
                Oid                     typeneeded = isSlice ? arrayType : elementType;
 
-               if (typesource != InvalidOid)
-               {
-                       assignFrom = coerce_to_target_type(pstate,
-                                                                                          assignFrom, typesource,
-                                                                                          typeneeded, arrayTypMod,
-                                                                                          COERCION_ASSIGNMENT,
-                                                                                          COERCE_IMPLICIT_CAST);
-                       if (assignFrom == NULL)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("array assignment requires type %s"
-                                                               " but expression is of type %s",
-                                                               format_type_be(typeneeded),
-                                                               format_type_be(typesource)),
-                                                errhint("You will need to rewrite or cast the expression.")));
-               }
+               assignFrom = coerce_to_target_type(pstate,
+                                                                                  assignFrom, typesource,
+                                                                                  typeneeded, elementTypMod,
+                                                                                  COERCION_ASSIGNMENT,
+                                                                                  COERCE_IMPLICIT_CAST);
+               if (assignFrom == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("array assignment requires type %s"
+                                                       " but expression is of type %s",
+                                                       format_type_be(typeneeded),
+                                                       format_type_be(typesource)),
+                                        errhint("You will need to rewrite or cast the expression.")));
        }
 
        /*
@@ -245,8 +258,6 @@ transformArraySubscripts(ParseState *pstate,
        aref->refexpr = (Expr *) arrayBase;
        aref->refassgnexpr = (Expr *) assignFrom;
 
-       ReleaseSysCache(type_tuple_array);
-
        return aref;
 }
 
index 3856005fab64bf426ba278444373bc5ac59eeb55..e0f0e6c930e757b31dd5869183f36dab7e3a7fc4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.120 2004/06/01 03:28:48 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parse_target.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 
 
 static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
+static Node *transformAssignmentIndirection(ParseState *pstate,
+                                                          Node *basenode,
+                                                          const char *targetName,
+                                                          bool targetIsArray,
+                                                          Oid targetTypeId,
+                                                          int32 targetTypMod,
+                                                          ListCell *indirection,
+                                                          Node *rhs);
 static List *ExpandAllTables(ParseState *pstate);
 static char *FigureColname(Node *node);
 static int     FigureColnameInternal(Node *node, char **name);
@@ -87,7 +96,7 @@ transformTargetEntry(ParseState *pstate,
  * Turns a list of ResTarget's into a list of TargetEntry's.
  *
  * At this point, we don't care whether we are doing SELECT, INSERT,
- * or UPDATE; we just transform the given expressions.
+ * or UPDATE; we just transform the given expressions (the "val" fields).
  */
 List *
 transformTargetList(ParseState *pstate, List *targetlist)
@@ -284,14 +293,14 @@ markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
  *     This is used in INSERT and UPDATE statements only.      It prepares a
  *     TargetEntry for assignment to a column of the target table.
  *     This includes coercing the given value to the target column's type
- *     (if necessary), and dealing with any subscripts attached to the target
- *     column itself.
+ *     (if necessary), and dealing with any subfield names or subscripts
+ *     attached to the target column itself.
  *
  * pstate              parse state
  * tle                 target list entry to be modified
  * colname             target column name (ie, name of attribute to be assigned to)
  * attrno              target attribute number
- * indirection subscripts for target column, if any
+ * indirection subscripts/field names for target column, if any
  */
 void
 updateTargetListEntry(ParseState *pstate,
@@ -320,8 +329,8 @@ updateTargetListEntry(ParseState *pstate,
         * type/typmod into it so that exprType will report the right things.
         * (We expect that the eventually substituted default expression will
         * in fact have this type and typmod.)  Also, reject trying to update
-        * an array element with DEFAULT, since there can't be any default for
-        * individual elements of a column.
+        * a subfield or array element with DEFAULT, since there can't be any
+        * default for portions of a column.
         */
        if (tle->expr && IsA(tle->expr, SetToDefault))
        {
@@ -330,82 +339,81 @@ updateTargetListEntry(ParseState *pstate,
                def->typeId = attrtype;
                def->typeMod = attrtypmod;
                if (indirection)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot set an array element to DEFAULT")));
+               {
+                       if (IsA(linitial(indirection), A_Indices))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot set an array element to DEFAULT")));
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot set a subfield to DEFAULT")));
+               }
        }
 
        /* Now we can use exprType() safely. */
        type_id = exprType((Node *) tle->expr);
 
        /*
-        * If there are subscripts on the target column, prepare an array
-        * assignment expression.  This will generate an array value that the
-        * source value has been inserted into, which can then be placed in
-        * the new tuple constructed by INSERT or UPDATE. Note that
-        * transformArraySubscripts takes care of type coercion.
+        * If there is indirection on the target column, prepare an array or
+        * subfield assignment expression.  This will generate a new column value
+        * that the source value has been inserted into, which can then be placed
+        * in the new tuple constructed by INSERT or UPDATE.
         */
        if (indirection)
        {
-               Node       *arrayBase;
-               ArrayRef   *aref;
+               Node       *colVar;
 
                if (pstate->p_is_insert)
                {
                        /*
-                        * The command is INSERT INTO table (arraycol[subscripts]) ...
-                        * so there is not really a source array value to work with.
-                        * Let the executor do something reasonable, if it can. Notice
-                        * that we force transformArraySubscripts to treat the
-                        * subscripting op as an array-slice op below, so the source
-                        * data will have been coerced to the array type.
+                        * The command is INSERT INTO table (col.something) ...
+                        * so there is not really a source value to work with.
+                        * Insert a NULL constant as the source value.
                         */
-                       arrayBase = NULL;       /* signal there is no source array */
+                       colVar = (Node *) makeNullConst(attrtype);
                }
                else
                {
                        /*
-                        * Build a Var for the array to be updated.
+                        * Build a Var for the column to be updated.
                         */
-                       arrayBase = (Node *) make_var(pstate,
-                                                                                 pstate->p_target_rangetblentry,
-                                                                                 attrno);
+                       colVar = (Node *) make_var(pstate,
+                                                                          pstate->p_target_rangetblentry,
+                                                                          attrno);
                }
 
-               aref = transformArraySubscripts(pstate,
-                                                                               arrayBase,
-                                                                               attrtype,
-                                                                               attrtypmod,
-                                                                               indirection,
-                                                                               pstate->p_is_insert,
-                                                                               (Node *) tle->expr);
-               tle->expr = (Expr *) aref;
+               tle->expr = (Expr *)
+                       transformAssignmentIndirection(pstate,
+                                                                                  colVar,
+                                                                                  colname,
+                                                                                  false,
+                                                                                  attrtype,
+                                                                                  attrtypmod,
+                                                                                  list_head(indirection),
+                                                                                  (Node *) tle->expr);
        }
        else
        {
                /*
-                * For normal non-subscripted target column, do type checking and
-                * coercion.  But accept InvalidOid, which indicates the source is
-                * a NULL constant.  (XXX is that still true?)
+                * For normal non-qualified target column, do type checking and
+                * coercion.
                 */
-               if (type_id != InvalidOid)
-               {
-                       tle->expr = (Expr *)
-                               coerce_to_target_type(pstate,
-                                                                         (Node *) tle->expr, type_id,
-                                                                         attrtype, attrtypmod,
-                                                                         COERCION_ASSIGNMENT,
-                                                                         COERCE_IMPLICIT_CAST);
-                       if (tle->expr == NULL)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("column \"%s\" is of type %s"
-                                                               " but expression is of type %s",
-                                                               colname,
-                                                               format_type_be(attrtype),
-                                                               format_type_be(type_id)),
-                                                errhint("You will need to rewrite or cast the expression.")));
-               }
+               tle->expr = (Expr *)
+                       coerce_to_target_type(pstate,
+                                                                 (Node *) tle->expr, type_id,
+                                                                 attrtype, attrtypmod,
+                                                                 COERCION_ASSIGNMENT,
+                                                                 COERCE_IMPLICIT_CAST);
+               if (tle->expr == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("column \"%s\" is of type %s"
+                                                       " but expression is of type %s",
+                                                       colname,
+                                                       format_type_be(attrtype),
+                                                       format_type_be(type_id)),
+                                        errhint("You will need to rewrite or cast the expression.")));
        }
 
        /*
@@ -425,6 +433,208 @@ updateTargetListEntry(ParseState *pstate,
        resnode->resname = colname;
 }
 
+/*
+ * Process indirection (field selection or subscripting) of the target
+ * column in INSERT/UPDATE.  This routine recurses for multiple levels
+ * of indirection --- but note that several adjacent A_Indices nodes in
+ * the indirection list are treated as a single multidimensional subscript
+ * operation.
+ *
+ * In the initial call, basenode is a Var for the target column in UPDATE,
+ * or a null Const of the target's type in INSERT.  In recursive calls,
+ * basenode is NULL, indicating that a substitute node should be consed up if
+ * needed.
+ *
+ * targetName is the name of the field or subfield we're assigning to, and
+ * targetIsArray is true if we're subscripting it.  These are just for
+ * error reporting.
+ *
+ * targetTypeId and targetTypMod indicate the datatype of the object to
+ * be assigned to (initially the target column, later some subobject).
+ *
+ * indirection is the sublist remaining to process.  When it's NULL, we're
+ * done recursing and can just coerce and return the RHS.
+ *
+ * rhs is the already-transformed value to be assigned; note it has not been
+ * coerced to any particular type.
+ */
+static Node *
+transformAssignmentIndirection(ParseState *pstate,
+                                                          Node *basenode,
+                                                          const char *targetName,
+                                                          bool targetIsArray,
+                                                          Oid targetTypeId,
+                                                          int32 targetTypMod,
+                                                          ListCell *indirection,
+                                                          Node *rhs)
+{
+       Node       *result;
+       List       *subscripts = NIL;
+       bool            isSlice = false;
+       ListCell   *i;
+
+       if (indirection && !basenode)
+       {
+               /* Set up a substitution.  We reuse CaseTestExpr for this. */
+               CaseTestExpr *ctest = makeNode(CaseTestExpr);
+
+               ctest->typeId = targetTypeId;
+               ctest->typeMod = targetTypMod;
+               basenode = (Node *) ctest;
+       }
+
+       /*
+        * We have to split any field-selection operations apart from
+        * subscripting.  Adjacent A_Indices nodes have to be treated
+        * as a single multidimensional subscript operation.
+        */
+       for_each_cell(i, indirection)
+       {
+               Node    *n = lfirst(i);
+
+               if (IsA(n, A_Indices))
+               {
+                       subscripts = lappend(subscripts, n);
+                       if (((A_Indices *) n)->lidx != NULL)
+                               isSlice = true;
+               }
+               else
+               {
+                       FieldStore *fstore;
+                       Oid             typrelid;
+                       AttrNumber attnum;
+                       Oid     fieldTypeId;
+                       int32 fieldTypMod;
+
+                       Assert(IsA(n, String));
+
+                       /* process subscripts before this field selection */
+                       if (subscripts)
+                       {
+                               Oid elementTypeId = transformArrayType(targetTypeId);
+                               Oid     typeNeeded = isSlice ? targetTypeId : elementTypeId;
+
+                               /* recurse to create appropriate RHS for array assign */
+                               rhs = transformAssignmentIndirection(pstate,
+                                                                                                        NULL,
+                                                                                                        targetName,
+                                                                                                        true,
+                                                                                                        typeNeeded,
+                                                                                                        targetTypMod,
+                                                                                                        i,
+                                                                                                        rhs);
+                               /* process subscripts */
+                               return (Node *) transformArraySubscripts(pstate,
+                                                                                                                basenode,
+                                                                                                                targetTypeId,
+                                                                                                                elementTypeId,
+                                                                                                                targetTypMod,
+                                                                                                                subscripts,
+                                                                                                                rhs);
+                       }
+
+                       /* No subscripts, so can process field selection here */
+
+                       typrelid = typeidTypeRelid(targetTypeId);
+                       if (!typrelid)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("cannot assign to a column of type %s because it is not a composite type",
+                                                               format_type_be(targetTypeId))));
+
+                       attnum = get_attnum(typrelid, strVal(n));
+                       if (attnum == InvalidAttrNumber)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                errmsg("column \"%s\" not found in data type %s",
+                                                               strVal(n), format_type_be(targetTypeId))));
+                       if (attnum < 0)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                errmsg("cannot assign to system column \"%s\"",
+                                                               strVal(n))));
+
+                       get_atttypetypmod(typrelid, attnum,
+                                                         &fieldTypeId, &fieldTypMod);
+
+                       /* recurse to create appropriate RHS for field assign */
+                       rhs = transformAssignmentIndirection(pstate,
+                                                                                                NULL,
+                                                                                                strVal(n),
+                                                                                                false,
+                                                                                                fieldTypeId,
+                                                                                                fieldTypMod,
+                                                                                                lnext(i),
+                                                                                                rhs);
+
+                       /* and build a FieldStore node */
+                       fstore = makeNode(FieldStore);
+                       fstore->arg = (Expr *) basenode;
+                       fstore->newvals = list_make1(rhs);
+                       fstore->fieldnums = list_make1_int(attnum);
+                       fstore->resulttype = targetTypeId;
+
+                       return (Node *) fstore;
+               }
+       }
+
+       /* process trailing subscripts, if any */
+       if (subscripts)
+       {
+               Oid elementTypeId = transformArrayType(targetTypeId);
+               Oid     typeNeeded = isSlice ? targetTypeId : elementTypeId;
+
+               /* recurse to create appropriate RHS for array assign */
+               rhs = transformAssignmentIndirection(pstate,
+                                                                                        NULL,
+                                                                                        targetName,
+                                                                                        true,
+                                                                                        typeNeeded,
+                                                                                        targetTypMod,
+                                                                                        NULL,
+                                                                                        rhs);
+               /* process subscripts */
+               return (Node *) transformArraySubscripts(pstate,
+                                                                                                basenode,
+                                                                                                targetTypeId,
+                                                                                                elementTypeId,
+                                                                                                targetTypMod,
+                                                                                                subscripts,
+                                                                                                rhs);
+       }
+
+       /* base case: just coerce RHS to match target type ID */
+
+       result = coerce_to_target_type(pstate,
+                                                                  rhs, exprType(rhs),
+                                                                  targetTypeId, targetTypMod,
+                                                                  COERCION_ASSIGNMENT,
+                                                                  COERCE_IMPLICIT_CAST);
+       if (result == NULL)
+       {
+               if (targetIsArray)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("array assignment to \"%s\" requires type %s"
+                                                       " but expression is of type %s",
+                                                       targetName,
+                                                       format_type_be(targetTypeId),
+                                                       format_type_be(exprType(rhs))),
+                                        errhint("You will need to rewrite or cast the expression.")));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("subfield \"%s\" is of type %s"
+                                                       " but expression is of type %s",
+                                                       targetName,
+                                                       format_type_be(targetTypeId),
+                                                       format_type_be(exprType(rhs))),
+                                        errhint("You will need to rewrite or cast the expression.")));
+       }
+
+       return result;
+}
+
 
 /*
  * checkInsertTargets -
@@ -466,21 +676,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
                /*
                 * Do initial validation of user-supplied INSERT column list.
                 */
+               List       *wholecols = NIL;
                ListCell   *tl;
 
                foreach(tl, cols)
                {
-                       char       *name = ((ResTarget *) lfirst(tl))->name;
+                       ResTarget  *col = (ResTarget *) lfirst(tl);
+                       char       *name = col->name;
                        int                     attrno;
 
                        /* Lookup column name, ereport on failure */
                        attrno = attnameAttNum(pstate->p_target_relation, name, false);
-                       /* Check for duplicates */
-                       if (list_member_int(*attrnos, attrno))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                         errmsg("column \"%s\" specified more than once",
-                                                        name)));
+
+                       /*
+                        * Check for duplicates, but only of whole columns --- we
+                        * allow  INSERT INTO foo (col.subcol1, col.subcol2)
+                        */
+                       if (col->indirection == NIL)
+                       {
+                               /* whole column; must not have any other assignment */
+                               if (list_member_int(*attrnos, attrno))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                                        errmsg("column \"%s\" specified more than once",
+                                                                       name)));
+                               wholecols = lappend_int(wholecols, attrno);
+                       }
+                       else
+                       {
+                               /* partial column; must not have any whole assignment */
+                               if (list_member_int(wholecols, attrno))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                                        errmsg("column \"%s\" specified more than once",
+                                                                       name)));
+                       }
+
                        *attrnos = lappend_int(*attrnos, attrno);
                }
        }
@@ -572,30 +803,45 @@ FigureColnameInternal(Node *node, char **name)
        {
                case T_ColumnRef:
                        {
-                               char       *cname = strVal(llast(((ColumnRef *) node)->fields));
+                               char       *fname = NULL;
+                               ListCell   *l;
 
-                               if (strcmp(cname, "*") != 0)
+                               /* find last field name, if any, ignoring "*" */
+                               foreach(l, ((ColumnRef *) node)->fields)
                                {
-                                       *name = cname;
+                                       Node   *i = lfirst(l);
+
+                                       if (strcmp(strVal(i), "*") != 0)
+                                               fname = strVal(i);
+                               }
+                               if (fname)
+                               {
+                                       *name = fname;
                                        return 2;
                                }
                        }
                        break;
-               case T_ExprFieldSelect:
+               case T_A_Indirection:
                        {
-                               ExprFieldSelect *efs = (ExprFieldSelect *) node;
+                               A_Indirection *ind = (A_Indirection *) node;
+                               char       *fname = NULL;
+                               ListCell   *l;
 
-                               if (efs->fields)
+                               /* find last field name, if any, ignoring "*" */
+                               foreach(l, ind->indirection)
                                {
-                                       char       *fname = strVal(llast(efs->fields));
+                                       Node   *i = lfirst(l);
 
-                                       if (strcmp(fname, "*") != 0)
-                                       {
-                                               *name = fname;
-                                               return 2;
-                                       }
+                                       if (IsA(i, String) &&
+                                               strcmp(strVal(i), "*") != 0)
+                                               fname = strVal(i);
+                               }
+                               if (fname)
+                               {
+                                       *name = fname;
+                                       return 2;
                                }
-                               return FigureColnameInternal(efs->arg, name);
+                               return FigureColnameInternal(ind->arg, name);
                        }
                        break;
                case T_FuncCall:
index cf6281d7cc9d2992373b612ce98e5c234dd49778..768f8788b7ec6a629ea27d8401186808a8e93431 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.138 2004/05/30 23:40:35 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.139 2004/06/09 19:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,6 +50,7 @@ static void rewriteTargetList(Query *parsetree, Relation target_relation);
 static TargetEntry *process_matched_tle(TargetEntry *src_tle,
                                                                                TargetEntry *prior_tle,
                                                                                const char *attrName);
+static Node *get_assignment_input(Node *node);
 static void markQueryForUpdate(Query *qry, bool skipOldNew);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
                   int varno, Query *parsetree);
@@ -273,8 +274,9 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
  * expressions.
  *
  * 2. Merge multiple entries for the same target attribute, or declare error
- * if we can't.  Presently, multiple entries are only allowed for UPDATE of
- * an array field, for example "UPDATE table SET foo[2] = 42, foo[4] = 43".
+ * if we can't.  Multiple entries are only allowed for INSERT/UPDATE of
+ * portions of an array or record field, for example
+ *                     UPDATE table SET foo[2] = 42, foo[4] = 43;
  * We can merge such 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)
@@ -431,8 +433,12 @@ process_matched_tle(TargetEntry *src_tle,
                                        const char *attrName)
 {
        Resdom     *resdom = src_tle->resdom;
+       Node       *src_expr;
+       Node       *prior_expr;
+       Node       *src_input;
+       Node       *prior_input;
        Node       *priorbottom;
-       ArrayRef   *newexpr;
+       Node       *newexpr;
 
        if (prior_tle == NULL)
        {
@@ -443,30 +449,55 @@ process_matched_tle(TargetEntry *src_tle,
                return src_tle;
        }
 
-       /*
+       /*----------
         * Multiple assignments to same attribute.      Allow only if all are
-        * array-assign operators with same bottom array object.
+        * FieldStore or ArrayRef assignment operations.  This is a bit
+        * tricky because what we may actually be looking at is a nest of
+        * such nodes; consider
+        *              UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
+        * The two expressions produced by the parser will look like
+        *              FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
+        *              FieldStore(col, fld2, FieldStore(placeholder, subfld2, x))
+        * However, we can ignore the substructure and just consider the top
+        * FieldStore or ArrayRef from each assignment, because it works to
+        * combine these as
+        *              FieldStore(FieldStore(col, fld1,
+        *                                                        FieldStore(placeholder, subfld1, x)),
+        *                                 fld2, FieldStore(placeholder, subfld2, x))
+        * Note the leftmost expression goes on the inside so that the
+        * assignments appear to occur left-to-right.
+        *
+        * For FieldStore, instead of nesting we can generate a single
+        * FieldStore with multiple target fields.  We must nest when
+        * ArrayRefs are involved though.
+        *----------
         */
-       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)->refrestype !=
-               ((ArrayRef *) prior_tle->expr)->refrestype)
+       src_expr = (Node *) src_tle->expr;
+       prior_expr = (Node *) prior_tle->expr;
+       src_input = get_assignment_input(src_expr);
+       prior_input = get_assignment_input(prior_expr);
+       if (src_input == NULL ||
+               prior_input == NULL ||
+               exprType(src_expr) != exprType(prior_expr))
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("multiple assignments to same column \"%s\"",
                                                attrName)));
 
        /*
-        * Prior TLE could be a nest of ArrayRefs if we do this more than
+        * Prior TLE could be a nest of assignments if we do this more than
         * once.
         */
-       priorbottom = (Node *) ((ArrayRef *) prior_tle->expr)->refexpr;
-       while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
-                  ((ArrayRef *) priorbottom)->refassgnexpr != NULL)
-               priorbottom = (Node *) ((ArrayRef *) priorbottom)->refexpr;
-       if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
+       priorbottom = prior_input;
+       for (;;)
+       {
+               Node    *newbottom = get_assignment_input(priorbottom);
+
+               if (newbottom == NULL)
+                       break;                          /* found the original Var reference */
+               priorbottom = newbottom;
+       }
+       if (!equal(priorbottom, src_input))
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("multiple assignments to same column \"%s\"",
@@ -475,13 +506,70 @@ process_matched_tle(TargetEntry *src_tle,
        /*
         * Looks OK to nest 'em.
         */
-       newexpr = makeNode(ArrayRef);
-       memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
-       newexpr->refexpr = prior_tle->expr;
+       if (IsA(src_expr, FieldStore))
+       {
+               FieldStore   *fstore = makeNode(FieldStore);
+
+               if (IsA(prior_expr, FieldStore))
+               {
+                       /* combine the two */
+                       memcpy(fstore, prior_expr, sizeof(FieldStore));
+                       fstore->newvals =
+                               list_concat(list_copy(((FieldStore *) prior_expr)->newvals),
+                                                       list_copy(((FieldStore *) src_expr)->newvals));
+                       fstore->fieldnums =
+                               list_concat(list_copy(((FieldStore *) prior_expr)->fieldnums),
+                                                       list_copy(((FieldStore *) src_expr)->fieldnums));
+               }
+               else
+               {
+                       /* general case, just nest 'em */
+                       memcpy(fstore, src_expr, sizeof(FieldStore));
+                       fstore->arg = (Expr *) prior_expr;
+               }
+               newexpr = (Node *) fstore;
+       }
+       else if (IsA(src_expr, ArrayRef))
+       {
+               ArrayRef   *aref = makeNode(ArrayRef);
+
+               memcpy(aref, src_expr, sizeof(ArrayRef));
+               aref->refexpr = (Expr *) prior_expr;
+               newexpr = (Node *) aref;
+       }
+       else
+       {
+               elog(ERROR, "can't happen");
+               newexpr = NULL;
+       }
 
        return makeTargetEntry(resdom, (Expr *) newexpr);
 }
 
+/*
+ * If node is an assignment node, return its input; else return NULL
+ */
+static Node *
+get_assignment_input(Node *node)
+{
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, FieldStore))
+       {
+               FieldStore *fstore = (FieldStore *) node;
+
+               return (Node *) fstore->arg;
+       }
+       else if (IsA(node, ArrayRef))
+       {
+               ArrayRef   *aref = (ArrayRef *) node;
+
+               if (aref->refassgnexpr == NULL)
+                       return NULL;
+               return (Node *) aref->refexpr;
+       }
+       return NULL;
+}
 
 /*
  * Make an expression tree for the default value for a column.
index ef58e99dac9e135ab2fbc175eb8ed89d41a1c11c..589db025d29942c85ea39b625f2a5c54500b6593 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.170 2004/06/06 00:41:27 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.171 2004/06/09 19:08:18 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -204,7 +204,8 @@ static void get_from_clause_coldeflist(List *coldeflist,
                                                   deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 StringInfo buf);
-static bool tleIsArrayAssign(TargetEntry *tle);
+static Node *processIndirection(Node *node, deparse_context *context);
+static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *generate_relation_name(Oid relid);
 static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
@@ -2061,6 +2062,7 @@ get_insert_query_def(Query *query, deparse_context *context)
        RangeTblEntry *rte;
        char       *sep;
        ListCell   *l;
+       List       *strippedexprs;
 
        /*
         * If it's an INSERT ... SELECT there will be a single subquery RTE
@@ -2087,11 +2089,15 @@ get_insert_query_def(Query *query, deparse_context *context)
                context->indentLevel += PRETTYINDENT_STD;
                appendStringInfoChar(buf, ' ');
        }
-       appendStringInfo(buf, "INSERT INTO %s",
+       appendStringInfo(buf, "INSERT INTO %s (",
                                         generate_relation_name(rte->relid));
 
-       /* Add the insert-column-names list */
-       sep = " (";
+       /*
+        * Add the insert-column-names list, and make a list of the actual
+        * assignment source expressions.
+        */
+       strippedexprs = NIL;
+       sep = "";
        foreach(l, query->targetList)
        {
                TargetEntry *tle = (TargetEntry *) lfirst(l);
@@ -2101,9 +2107,22 @@ get_insert_query_def(Query *query, deparse_context *context)
 
                appendStringInfo(buf, sep);
                sep = ", ";
+
+               /*
+                * Put out name of target column; look in the catalogs, not at
+                * tle->resname, since resname will fail to track RENAME.
+                */
                appendStringInfoString(buf,
                                                           quote_identifier(get_relid_attribute_name(rte->relid,
                                                                                                                                                 tle->resdom->resno)));
+
+               /*
+                * Print any indirection needed (subfields or subscripts), and strip
+                * off the top-level nodes representing the indirection assignments.
+                */
+               strippedexprs = lappend(strippedexprs,
+                                                               processIndirection((Node *) tle->expr,
+                                                                                                  context));
        }
        appendStringInfo(buf, ") ");
 
@@ -2113,16 +2132,13 @@ get_insert_query_def(Query *query, deparse_context *context)
                appendContextKeyword(context, "VALUES (",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
                sep = "";
-               foreach(l, query->targetList)
+               foreach(l, strippedexprs)
                {
-                       TargetEntry *tle = (TargetEntry *) lfirst(l);
-
-                       if (tle->resdom->resjunk)
-                               continue;               /* ignore junk entries */
+                       Node   *expr = lfirst(l);
 
                        appendStringInfo(buf, sep);
                        sep = ", ";
-                       get_rule_expr((Node *) tle->expr, context, false);
+                       get_rule_expr(expr, context, false);
                }
                appendStringInfoChar(buf, ')');
        }
@@ -2163,6 +2179,7 @@ get_update_query_def(Query *query, deparse_context *context)
        foreach(l, query->targetList)
        {
                TargetEntry *tle = (TargetEntry *) lfirst(l);
+               Node    *expr;
 
                if (tle->resdom->resjunk)
                        continue;                       /* ignore junk entries */
@@ -2171,15 +2188,22 @@ get_update_query_def(Query *query, deparse_context *context)
                sep = ", ";
 
                /*
-                * If the update expression is an array assignment, we mustn't put
-                * out "attname =" here; it will come out of the display of the
-                * ArrayRef node instead.
+                * Put out name of target column; look in the catalogs, not at
+                * tle->resname, since resname will fail to track RENAME.
                 */
-               if (!tleIsArrayAssign(tle))
-                       appendStringInfo(buf, "%s = ",
+               appendStringInfoString(buf,
                                                quote_identifier(get_relid_attribute_name(rte->relid,
                                                                                                                tle->resdom->resno)));
-               get_rule_expr((Node *) tle->expr, context, false);
+
+               /*
+                * Print any indirection needed (subfields or subscripts), and strip
+                * off the top-level nodes representing the indirection assignments.
+                */
+               expr = processIndirection((Node *) tle->expr, context);
+
+               appendStringInfo(buf, " = ");
+
+               get_rule_expr(expr, context, false);
        }
 
        /* Add the FROM clause if needed */
@@ -2452,6 +2476,13 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                         */
                        return (IsA(parentNode, FieldSelect) ? false : true);
 
+               case T_FieldStore:
+
+                       /*
+                        * treat like FieldSelect (probably doesn't matter)
+                        */
+                       return (IsA(parentNode, FieldStore) ? false : true);
+
                case T_CoerceToDomain:
                        /* maybe simple, check args */
                        return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
@@ -2757,53 +2788,27 @@ get_rule_expr(Node *node, deparse_context *context,
                case T_ArrayRef:
                        {
                                ArrayRef   *aref = (ArrayRef *) node;
-                               bool            savevarprefix = context->varprefix;
                                bool            need_parens;
-                               ListCell   *lowlist_item;
-                               ListCell   *uplist_item;
 
                                /*
-                                * If we are doing UPDATE array[n] = expr, we need to
-                                * suppress any prefix on the array name.  Currently, that
-                                * is the only context in which we will see a non-null
-                                * refassgnexpr --- but someday a smarter test may be
-                                * needed.
+                                * Parenthesize the argument unless it's a simple Var or
+                                * a FieldSelect.  (In particular, if it's another ArrayRef,
+                                * we *must* parenthesize to avoid confusion.)
                                 */
-                               if (aref->refassgnexpr)
-                                       context->varprefix = false;
-
-                               /*
-                                * Parenthesize the argument unless it's a simple Var.
-                                */
-                               need_parens = (aref->refassgnexpr == NULL) &&
-                                       !IsA(aref->refexpr, Var);
+                               need_parens = !IsA(aref->refexpr, Var) &&
+                                       !IsA(aref->refexpr, FieldSelect);
                                if (need_parens)
                                        appendStringInfoChar(buf, '(');
                                get_rule_expr((Node *) aref->refexpr, context, showimplicit);
                                if (need_parens)
                                        appendStringInfoChar(buf, ')');
-                               context->varprefix = savevarprefix;
-                               lowlist_item = list_head(aref->reflowerindexpr);
-                               foreach(uplist_item, aref->refupperindexpr)
-                               {
-                                       appendStringInfo(buf, "[");
-                                       if (lowlist_item)
-                                       {
-                                               get_rule_expr((Node *) lfirst(lowlist_item), context,
-                                                                         false);
-                                               appendStringInfo(buf, ":");
-                                               lowlist_item = lnext(lowlist_item);
-                                       }
-                                       get_rule_expr((Node *) lfirst(uplist_item),
-                                                                 context, false);
-                                       appendStringInfo(buf, "]");
-                               }
+                               printSubscripts(aref, context);
+                               /*
+                                * Array assignment nodes should have been handled in
+                                * processIndirection().
+                                */
                                if (aref->refassgnexpr)
-                               {
-                                       appendStringInfo(buf, " = ");
-                                       get_rule_expr((Node *) aref->refassgnexpr, context,
-                                                                 showimplicit);
-                               }
+                                       elog(ERROR, "unexpected refassgnexpr");
                        }
                        break;
 
@@ -2935,6 +2940,7 @@ get_rule_expr(Node *node, deparse_context *context,
                                Oid                     argType = exprType((Node *) fselect->arg);
                                Oid                     typrelid;
                                char       *fieldname;
+                               bool            need_parens;
 
                                /* lookup arg type and get the field name */
                                typrelid = get_typ_typrelid(argType);
@@ -2943,19 +2949,31 @@ get_rule_expr(Node *node, deparse_context *context,
                                                 format_type_be(argType));
                                fieldname = get_relid_attribute_name(typrelid,
                                                                                                         fselect->fieldnum);
-
                                /*
-                                * If the argument is simple enough, we could emit
-                                * arg.fieldname, but most cases where FieldSelect is used
-                                * are *not* simple.  So, always use parenthesized syntax.
+                                * Parenthesize the argument unless it's an ArrayRef or
+                                * another FieldSelect.  Note in particular that it would be
+                                * WRONG to not parenthesize a Var argument; simplicity is not
+                                * the issue here, having the right number of names is.
                                 */
-                               appendStringInfoChar(buf, '(');
-                               get_rule_expr_paren((Node *) fselect->arg, context, true, node);
-                               appendStringInfoChar(buf, ')');
+                               need_parens = !IsA(fselect->arg, ArrayRef) &&
+                                       !IsA(fselect->arg, FieldSelect);
+                               if (need_parens)
+                                       appendStringInfoChar(buf, '(');
+                               get_rule_expr((Node *) fselect->arg, context, true);
+                               if (need_parens)
+                                       appendStringInfoChar(buf, ')');
                                appendStringInfo(buf, ".%s", quote_identifier(fieldname));
                        }
                        break;
 
+               case T_FieldStore:
+                       /*
+                        * We shouldn't see FieldStore here; it should have been
+                        * stripped off by processIndirection().
+                        */
+                       elog(ERROR, "unexpected FieldStore");
+                       break;
+
                case T_RelabelType:
                        {
                                RelabelType *relabel = (RelabelType *) node;
@@ -4043,29 +4061,89 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 }
 
 /*
- * tleIsArrayAssign                    - check for array assignment
+ * processIndirection - take care of array and subfield assignment
+ *
+ * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * appear in the input, printing out the appropriate decoration for the
+ * base column name (that the caller just printed).  We return the
+ * subexpression that's to be assigned.
  */
-static bool
-tleIsArrayAssign(TargetEntry *tle)
+static Node *
+processIndirection(Node *node, deparse_context *context)
 {
-       ArrayRef   *aref;
+       StringInfo      buf = context->buf;
 
-       if (tle->expr == NULL || !IsA(tle->expr, ArrayRef))
-               return false;
-       aref = (ArrayRef *) tle->expr;
-       if (aref->refassgnexpr == NULL)
-               return false;
+       for (;;)
+       {
+               if (node == NULL)
+                       break;
+               if (IsA(node, FieldStore))
+               {
+                       FieldStore *fstore = (FieldStore *) node;
+                       Oid                     typrelid;
+                       char       *fieldname;
+
+                       /* lookup tuple type */
+                       typrelid = get_typ_typrelid(fstore->resulttype);
+                       if (!OidIsValid(typrelid))
+                               elog(ERROR, "argument type %s of FieldStore is not a tuple type",
+                                        format_type_be(fstore->resulttype));
+                       /*
+                        * Get the field name.  Note we assume here that there's only
+                        * one field being assigned to.  This is okay in stored rules
+                        * but could be wrong in executable target lists.  Presently no
+                        * problem since explain.c doesn't print plan targetlists, but
+                        * someday may have to think of something ...
+                        */
+                       fieldname = get_relid_attribute_name(typrelid,
+                                                                                                linitial_int(fstore->fieldnums));
+                       appendStringInfo(buf, ".%s", quote_identifier(fieldname));
+                       /*
+                        * We ignore arg since it should be an uninteresting reference
+                        * to the target column or subcolumn.
+                        */
+                       node = (Node *) linitial(fstore->newvals);
+               }
+               else if (IsA(node, ArrayRef))
+               {
+                       ArrayRef   *aref = (ArrayRef *) node;
 
-       /*
-        * Currently, it should only be possible to see non-null refassgnexpr
-        * if we are indeed looking at an "UPDATE array[n] = expr" situation.
-        * So aref->refexpr ought to match the tle's target.
-        */
-       if (aref->refexpr == NULL || !IsA(aref->refexpr, Var) ||
-               ((Var *) aref->refexpr)->varattno != tle->resdom->resno)
-               elog(ERROR, "unrecognized situation in array assignment");
+                       if (aref->refassgnexpr == NULL)
+                               break;
+                       printSubscripts(aref, context);
+                       /*
+                        * We ignore refexpr since it should be an uninteresting reference
+                        * to the target column or subcolumn.
+                        */
+                       node = (Node *) aref->refassgnexpr;
+               }
+               else
+                       break;
+       }
 
-       return true;
+       return node;
+}
+
+static void
+printSubscripts(ArrayRef *aref, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       ListCell   *lowlist_item;
+       ListCell   *uplist_item;
+
+       lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
+       foreach(uplist_item, aref->refupperindexpr)
+       {
+               appendStringInfoChar(buf, '[');
+               if (lowlist_item)
+               {
+                       get_rule_expr((Node *) lfirst(lowlist_item), context, false);
+                       appendStringInfoChar(buf, ':');
+                       lowlist_item = lnext(lowlist_item);
+               }
+               get_rule_expr((Node *) lfirst(uplist_item), context, false);
+               appendStringInfoChar(buf, ']');
+       }
 }
 
 /*
index 325bf8768002185c6f90a9e79f8f381d36f9cfb4..a32378c7f84ad943016335fe9fb716bdc0e63e49 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.116 2004/05/10 22:44:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.117 2004/06/09 19:08:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -574,6 +574,18 @@ typedef struct FieldSelectState
        TupleDesc       argdesc;                /* tupdesc for most recent input */
 } FieldSelectState;
 
+/* ----------------
+ *             FieldStoreState node
+ * ----------------
+ */
+typedef struct FieldStoreState
+{
+       ExprState       xprstate;
+       ExprState  *arg;                        /* input tuple value */
+       List       *newvals;            /* new value(s) for field(s) */
+       TupleDesc       argdesc;                /* tupdesc for most recent input */
+} FieldStoreState;
+
 /* ----------------
  *             CaseExprState node
  * ----------------
index 6feedf6762c9895fe75986355b1bcffe86f53379..b719747e7621d0834e3bb9773ba2fe5ab284f03d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.156 2004/05/26 13:57:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.157 2004/06/09 19:08:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,6 +110,7 @@ typedef enum NodeTag
        T_SubLink,
        T_SubPlan,
        T_FieldSelect,
+       T_FieldStore,
        T_RelabelType,
        T_CaseExpr,
        T_CaseWhen,
@@ -143,6 +144,7 @@ typedef enum NodeTag
        T_BoolExprState,
        T_SubPlanState,
        T_FieldSelectState,
+       T_FieldStoreState,
        T_CaseExprState,
        T_CaseWhenState,
        T_ArrayExprState,
@@ -274,7 +276,7 @@ typedef enum NodeTag
        T_A_Const,
        T_FuncCall,
        T_A_Indices,
-       T_ExprFieldSelect,
+       T_A_Indirection,
        T_ResTarget,
        T_TypeCast,
        T_SortBy,
index 8f6cc25e0aad2e28edd34996e2ed4908a5a511f5..0da9b3790776b7901c5808e49cbf8936f3f27b52 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.257 2004/06/02 21:01:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.258 2004/06/09 19:08:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -166,26 +166,26 @@ typedef struct TypeName
  * ColumnRef - specifies a reference to a column, or possibly a whole tuple
  *
  *             The "fields" list must be nonempty; its last component may be "*"
- *             instead of a field name.  Subscripts are optional.
+ *             instead of a regular field name.
+ *
+ * Note: any array subscripting or selection of fields from composite columns
+ * is represented by an A_Indirection node above the ColumnRef.  However,
+ * for simplicity in the normal case, initial field selection from a table
+ * name is represented within ColumnRef and not by adding A_Indirection.
  */
 typedef struct ColumnRef
 {
        NodeTag         type;
        List       *fields;                     /* field names (list of Value strings) */
-       List       *indirection;        /* subscripts (list of A_Indices) */
 } ColumnRef;
 
 /*
- * ParamRef - specifies a parameter reference
- *
- *             The parameter could be qualified with field names and/or subscripts
+ * ParamRef - specifies a $n parameter reference
  */
 typedef struct ParamRef
 {
        NodeTag         type;
        int                     number;                 /* the number of the parameter */
-       List       *fields;                     /* field names (list of Value strings) */
-       List       *indirection;        /* subscripts (list of A_Indices) */
 } ParamRef;
 
 /*
@@ -267,40 +267,50 @@ typedef struct A_Indices
 } A_Indices;
 
 /*
- * ExprFieldSelect - select a field and/or array element from an expression
+ * A_Indirection - select a field and/or array element from an expression
  *
- *             This is used in the raw parsetree to represent selection from an
- *             arbitrary expression (not a column or param reference).  Either
- *             fields or indirection may be NIL if not used.
+ * The indirection list can contain both A_Indices nodes (representing
+ * subscripting) and string Value nodes (representing field selection
+ * --- the string value is the name of the field to select).  For example,
+ * a complex selection operation like
+ *                             (foo).field1[42][7].field2
+ * would be represented with a single A_Indirection node having a 4-element
+ * indirection list.
+ *
+ * Note: as of Postgres 7.5, we don't support arrays of composite values,
+ * so cases in which a field select follows a subscript aren't actually
+ * semantically legal.  However the parser is prepared to handle such.
  */
-typedef struct ExprFieldSelect
+typedef struct A_Indirection
 {
        NodeTag         type;
        Node       *arg;                        /* the thing being selected from */
-       List       *fields;                     /* field names (list of Value strings) */
-       List       *indirection;        /* subscripts (list of A_Indices) */
-} ExprFieldSelect;
+       List       *indirection;        /* subscripts and/or field names */
+} A_Indirection;
 
 /*
  * ResTarget -
- *       result target (used in target list of pre-transformed Parse trees)
+ *       result target (used in target list of pre-transformed parse trees)
  *
- * In a SELECT or INSERT target list, 'name' is either NULL or
- * the column name assigned to the value.  (If there is an 'AS ColumnLabel'
- * clause, the grammar sets 'name' from it; otherwise 'name' is initially NULL
- * and is filled in during the parse analysis phase.)
- * The 'indirection' field is not used at all.
+ * In a SELECT or INSERT target list, 'name' is the column label from an
+ * 'AS ColumnLabel' clause, or NULL if there was none, and 'val' is the
+ * value expression itself.  The 'indirection' field is not used.
  *
- * In an UPDATE target list, 'name' is the name of the destination column,
+ * INSERT has a second ResTarget list which is the target-column-names list.
+ * Here, 'val' is not used, 'name' is the name of the destination column,
  * and 'indirection' stores any subscripts attached to the destination.
- * That is, our representation is UPDATE table SET name [indirection] = val.
+ *
+ * In an UPDATE target list, 'name' is the name of the destination column,
+ * 'indirection' stores any subscripts attached to the destination, and
+ * 'val' is the expression to assign.
+ *
+ * See A_Indirection for more info about what can appear in 'indirection'.
  */
 typedef struct ResTarget
 {
        NodeTag         type;
        char       *name;                       /* column name or NULL */
-       List       *indirection;        /* subscripts for destination column, or
-                                                                * NIL */
+       List       *indirection;        /* subscripts and field names, or NIL */
        Node       *val;                        /* the value expression to compute or
                                                                 * assign */
 } ResTarget;
index 5db00199cefba25a7f783ba68c172f78e1a91528..3ed4d74ee3546cb3d270650f23320805bb5e0c89 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.99 2004/05/30 23:40:39 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.100 2004/06/09 19:08:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * ordinal position (counting from 1).  However, in an INSERT or UPDATE
  * targetlist, resno represents the attribute number of the destination
  * column for the item; so there may be missing or out-of-order resnos.
- * In an UPDATE, it is even legal to have duplicated resnos; consider
+ * It is even legal to have duplicated resnos; consider
  *             UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...
  * The two meanings come together in the executor, because the planner
  * transforms INSERT/UPDATE tlists into a normalized form with exactly
  * one entry for each column of the destination table.  Before that's
  * happened, however, it is risky to assume that resno == position.
  * Generally get_tle_by_resno() should be used rather than list_nth()
- * to fetch tlist entries by resno.
+ * to fetch tlist entries by resno, and only in SELECT should you assume
+ * that resno is a unique identifier.
  *
  * resname is required to represent the correct column name in non-resjunk
  * entries of top-level SELECT targetlists, since it will be used as the
@@ -540,6 +541,31 @@ typedef struct FieldSelect
        int32           resulttypmod;   /* output typmod (usually -1) */
 } FieldSelect;
 
+/* ----------------
+ * FieldStore
+ *
+ * FieldStore represents the operation of modifying one field in a tuple
+ * value, yielding a new tuple value (the input is not touched!).  Like
+ * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * portion of a column.
+ *
+ * A single FieldStore can actually represent updates of several different
+ * fields.  The parser only generates FieldStores with single-element lists,
+ * but the planner will collapse multiple updates of the same base column
+ * into one FieldStore.
+ * ----------------
+ */
+
+typedef struct FieldStore
+{
+       Expr            xpr;
+       Expr       *arg;                        /* input tuple value */
+       List       *newvals;            /* new value(s) for field(s) */
+       List       *fieldnums;          /* integer list of field attnums */
+       Oid                     resulttype;             /* type of result (same as type of arg) */
+       /* Like RowExpr, we deliberately omit a typmod here */
+} FieldStore;
+
 /* ----------------
  * RelabelType
  *
@@ -607,6 +633,9 @@ typedef struct CaseWhen
  * Placeholder node for the test value to be processed by a CASE expression.
  * This is effectively like a Param, but can be implemented more simply
  * since we need only one replacement value at a time.
+ *
+ * We also use this in nested UPDATE expressions.
+ * See transformAssignmentIndirection().
  */
 typedef struct CaseTestExpr
 {
index 8bb595c7098e97b4819d37147e6d652272aef8fb..de7b4766559099ec980f29f9791efb327eda93a2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.38 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.39 2004/06/09 19:08:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,12 +67,13 @@ typedef struct ParseState
 
 extern ParseState *make_parsestate(ParseState *parentParseState);
 extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
+extern Oid transformArrayType(Oid arrayType);
 extern ArrayRef *transformArraySubscripts(ParseState *pstate,
                                                 Node *arrayBase,
                                                 Oid arrayType,
-                                                int32 arrayTypMod,
+                                                Oid elementType,
+                                                int32 elementTypMod,
                                                 List *indirection,
-                                                bool forceSlice,
                                                 Node *assignFrom);
 extern Const *make_const(Value *value);
 
index a8b531874db5e42c3bb95db630fab2f9b7f32f65..0c9d865d17dafd692f5edb5b698cda1b88d84fa2 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.106 2004/06/06 00:41:28 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.107 2004/06/09 19:08:19 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -3852,6 +3852,18 @@ exec_simple_check_node(Node *node)
                case T_FieldSelect:
                        return exec_simple_check_node((Node *) ((FieldSelect *) node)->arg);
 
+               case T_FieldStore:
+                       {
+                               FieldStore   *expr = (FieldStore *) node;
+
+                               if (!exec_simple_check_node((Node *) expr->arg))
+                                       return FALSE;
+                               if (!exec_simple_check_node((Node *) expr->newvals))
+                                       return FALSE;
+
+                               return TRUE;
+                       }
+
                case T_RelabelType:
                        return exec_simple_check_node((Node *) ((RelabelType *) node)->arg);
 
index 6e82a7d0a6c70d876712a6f9c979da00a81a21e0..8647eccf14c09ede192f71c512d4118755b2d40f 100644 (file)
@@ -11,26 +11,25 @@ CREATE TABLE arrtest (
        g                       varchar(5)[]
 );
 --
--- only this array as a 0-based 'e', the others are 1-based.
--- 'e' is also a large object.
+-- only the 'e' array is 0-based, the others are 1-based.
 --
-INSERT INTO arrtest (a[5], b[2][1][2], c, d, f, g)
+INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g)
    VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
 UPDATE arrtest SET e[0] = '1.1';
 UPDATE arrtest SET e[1] = '2.2';
 INSERT INTO arrtest (f)
    VALUES ('{"too long"}');
 ERROR:  value too long for type character(5)
-INSERT INTO arrtest (a, b[2][2][1], c, d, e, f, g)
+INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g)
    VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', 
            '{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
            '{"abc","abcde"}', '{"abc","abcde"}');
-INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
+INSERT INTO arrtest (a, b[1:2], c, d[1:2])
    VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
 SELECT * FROM arrtest;
       a      |        b        |     c     |       d       |     e     |        f        |      g      
 -------------+-----------------+-----------+---------------+-----------+-----------------+-------------
- {1,2,3,4,5} | {{{0,0},{1,2}}} | {}        | {}            |           | {}              | {}
+ {1,2,3,4,5} | {{{0,0},{1,2}}} | {}        | {}            | {1.1,2.2} | {}              | {}
  {11,12,23}  | {{3,4},{4,5}}   | {foobar}  | {{elt1,elt2}} | {3.4,6.7} | {"abc  ",abcde} | {abc,abcde}
  {}          | {3,4}           | {foo,bar} | {bar,foo}     |           |                 | 
 (3 rows)
@@ -41,20 +40,20 @@ SELECT arrtest.a[1],
           arrtest.d[1][1], 
           arrtest.e[0]
    FROM arrtest;
- a  | b |   c    |  d   | e 
-----+---+--------+------+---
-  1 | 0 |        |      |  
- 11 |   | foobar | elt1 |  
-    |   | foo    |      |  
+ a  | b |   c    |  d   |  e  
+----+---+--------+------+-----
+  1 | 0 |        |      | 1.1
+ 11 |   | foobar | elt1 |    
+    |   | foo    |      |    
 (3 rows)
 
 SELECT a[1], b[1][1][1], c[1], d[1][1], e[0]
    FROM arrtest;
- a  | b |   c    |  d   | e 
-----+---+--------+------+---
-  1 | 0 |        |      |  
- 11 |   | foobar | elt1 |  
-    |   | foo    |      |  
+ a  | b |   c    |  d   |  e  
+----+---+--------+------+-----
+  1 | 0 |        |      | 1.1
+ 11 |   | foobar | elt1 |    
+    |   | foo    |      |    
 (3 rows)
 
 SELECT a[1:3],
index 91eeaf0ab87fbd45bc6c683351f7f38ec2c743f7..384e5ad6d53baf50ac5f2549a2e5a637db3ca2d8 100644 (file)
@@ -87,19 +87,23 @@ select * from people;
  (Joe,Blow,) | 01-10-1984
 (1 row)
 
--- This fails at the moment, would like it to work though:
+-- test insertion/updating of subfields
 update people set fn.suffix = 'Jr';
-ERROR:  syntax error at or near "." at character 21
-LINE 1: update people set fn.suffix = 'Jr';
-                            ^
--- ugly workaround:
-update people set fn = ((fn).first, (fn).last, 'III');
 select * from people;
-       fn       |     bd     
-----------------+------------
- (Joe,Blow,III) | 01-10-1984
+      fn       |     bd     
+---------------+------------
+ (Joe,Blow,Jr) | 01-10-1984
 (1 row)
 
+insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66);
+select * from quadtable;
+ f1 |             q             
+----+---------------------------
+  1 | ("(3.3,4.4)","(5.5,6.6)")
+  2 | ("(,4.4)","(5.5,6.6)")
+ 44 | ("(55,)","(,66)")
+(3 rows)
+
 -- The object here is to ensure that toasted references inside
 -- composite values don't cause problems.  The large f1 value will
 -- be toasted inside pp, it must still work after being copied to people.
index 629ca15fb1c45221f7e5f05f4c5ecdd3743febed..97cb5bbc075f455ec597ed8d966643d6ee9a8a8a 100644 (file)
@@ -13,11 +13,10 @@ CREATE TABLE arrtest (
 );
 
 --
--- only this array as a 0-based 'e', the others are 1-based.
--- 'e' is also a large object.
+-- only the 'e' array is 0-based, the others are 1-based.
 --
 
-INSERT INTO arrtest (a[5], b[2][1][2], c, d, f, g)
+INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g)
    VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
 
 UPDATE arrtest SET e[0] = '1.1';
@@ -27,12 +26,12 @@ UPDATE arrtest SET e[1] = '2.2';
 INSERT INTO arrtest (f)
    VALUES ('{"too long"}');
 
-INSERT INTO arrtest (a, b[2][2][1], c, d, e, f, g)
+INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g)
    VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', 
            '{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
            '{"abc","abcde"}', '{"abc","abcde"}');
 
-INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
+INSERT INTO arrtest (a, b[1:2], c, d[1:2])
    VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
 
 
index 809cad8f32018b709b97a4decb063c2ae7ebb87c..e53197c05b5e3fa30530393ea6273c4e205bb4a6 100644 (file)
@@ -53,14 +53,15 @@ alter table fullname add column suffix text default null;
 
 select * from people;
 
--- This fails at the moment, would like it to work though:
+-- test insertion/updating of subfields
 update people set fn.suffix = 'Jr';
 
--- ugly workaround:
-update people set fn = ((fn).first, (fn).last, 'III');
-
 select * from people;
 
+insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66);
+
+select * from quadtable;
+
 -- The object here is to ensure that toasted references inside
 -- composite values don't cause problems.  The large f1 value will
 -- be toasted inside pp, it must still work after being copied to people.