Rewrite parser's handling of INSERT ... SELECT so that processing
authorTom Lane
Mon, 19 Jul 1999 00:26:20 +0000 (00:26 +0000)
committerTom Lane
Mon, 19 Jul 1999 00:26:20 +0000 (00:26 +0000)
of the SELECT part of the statement is just like a plain SELECT.  All
INSERT-specific processing happens after the SELECT parsing is done.
This eliminates many problems, e.g. INSERT ... SELECT ... GROUP BY using
the wrong column labels.  Ensure that DEFAULT clauses are coerced to
the target column type, whether or not stored clause produces the right
type.  Substantial cleanup of parser's array support.

12 files changed:
src/backend/executor/execQual.c
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/include/parser/parse_clause.h
src/include/parser/parse_expr.h
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/parser/parse_target.h

index 649c189ea97104f8804ba4a2e530cc56c400282d..a3eb943ab9614348509503c57357f2235b47d3f3 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.57 1999/07/17 20:16:57 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.58 1999/07/19 00:26:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,31 +86,44 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                 bool *isNull,
                 bool *isDone)
 {
-   bool        dummy;
-   int         i = 0,
-               j = 0;
    ArrayType  *array_scanner;
-   List       *upperIndexpr,
-              *lowerIndexpr;
-   Node       *assgnexpr;
    List       *elt;
+   int         i = 0,
+               j = 0;
    IntArray    upper,
                lower;
    int        *lIndex;
-   char       *dataPtr;
+   bool        dummy;
 
    *isNull = false;
-   array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
-                                              econtext,
-                                              isNull,
-                                              isDone);
-   if (*isNull)
-       return (Datum) NULL;
 
-   upperIndexpr = arrayRef->refupperindexpr;
+   if (arrayRef->refexpr != NULL)
+   {
+       array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
+                                                  econtext,
+                                                  isNull,
+                                                  isDone);
+       if (*isNull)
+           return (Datum) NULL;
+   }
+   else
+   {
+       /* Null 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_scanner = NULL;
+   }
 
-   foreach(elt, upperIndexpr)
+   foreach(elt, arrayRef->refupperindexpr)
    {
+       if (i >= MAXDIM)
+           elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
+                MAXDIM);
+
        upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
                                               econtext,
                                               isNull,
@@ -119,12 +132,14 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
            return (Datum) NULL;
    }
 
-   lowerIndexpr = arrayRef->reflowerindexpr;
-   lIndex = NULL;
-   if (lowerIndexpr != NIL)
+   if (arrayRef->reflowerindexpr != NIL)
    {
-       foreach(elt, lowerIndexpr)
+       foreach(elt, arrayRef->reflowerindexpr)
        {
+           if (j >= MAXDIM)
+               elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
+                    MAXDIM);
+
            lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
                                                   econtext,
                                                   isNull,
@@ -137,30 +152,42 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                 "ExecEvalArrayRef: upper and lower indices mismatch");
        lIndex = lower.indx;
    }
+   else
+   {
+       lIndex = NULL;
+   }
 
-   assgnexpr = arrayRef->refassgnexpr;
-   if (assgnexpr != NULL)
+   if (arrayRef->refassgnexpr != NULL)
    {
-       dataPtr = (char *) ExecEvalExpr((Node *)
-                                       assgnexpr, econtext,
-                                       isNull, &dummy);
+       Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
+                                       econtext,
+                                       isNull,
+                                       &dummy);
        if (*isNull)
            return (Datum) NULL;
+
        execConstByVal = arrayRef->refelembyval;
        execConstLen = arrayRef->refelemlength;
+
+       if (array_scanner == NULL)
+           return sourceData;  /* XXX do something else? */
+
        if (lIndex == NULL)
-           return (Datum) array_set(array_scanner, i, upper.indx, dataPtr,
+           return (Datum) array_set(array_scanner, i, upper.indx,
+                                    (char *) sourceData,
                                     arrayRef->refelembyval,
                                     arrayRef->refelemlength,
                                     arrayRef->refattrlength, isNull);
        return (Datum) array_assgn(array_scanner, i, upper.indx,
                                   lower.indx,
-                                  (ArrayType *) dataPtr,
+                                  (ArrayType *) sourceData,
                                   arrayRef->refelembyval,
                                   arrayRef->refelemlength, isNull);
    }
+
    execConstByVal = arrayRef->refelembyval;
    execConstLen = arrayRef->refelemlength;
+
    if (lIndex == NULL)
        return (Datum) array_ref(array_scanner, i, upper.indx,
                                 arrayRef->refelembyval,
index 40a66b487389e4a6582d9796e1c67154acc686ff..d4a1ef57ef8dddecd50f85995ed63d744896e9d3 100644 (file)
@@ -5,12 +5,11 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: analyze.c,v 1.115 1999/07/17 20:17:19 momjian Exp $
+ * $Id: analyze.c,v 1.116 1999/07/19 00:26:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
-
 #include "postgres.h"
 
 #include "access/heapam.h"
@@ -38,8 +37,10 @@ static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
 static void transformForUpdate(Query *qry, List *forUpdate);
 void       CheckSelectForUpdate(Query *qry);
 
-List      *extras_before = NIL;
-List      *extras_after = NIL;
+/* kluge to return extra info from transformCreateStmt() */
+static List       *extras_before;
+static List       *extras_after;
+
 
 /*
  * parse_analyze -
@@ -58,17 +59,23 @@ parse_analyze(List *pl, ParseState *parentParseState)
 
    while (pl != NIL)
    {
+       extras_before = extras_after = NIL;
        pstate = make_parsestate(parentParseState);
+
        parsetree = transformStmt(pstate, lfirst(pl));
        if (pstate->p_target_relation != NULL)
            heap_close(pstate->p_target_relation);
+       pstate->p_target_relation = NULL;
+       pstate->p_target_rangetblentry = NULL;
 
        while (extras_before != NIL)
        {
            result = lappend(result,
-                          transformStmt(pstate, lfirst(extras_before)));
+                            transformStmt(pstate, lfirst(extras_before)));
            if (pstate->p_target_relation != NULL)
                heap_close(pstate->p_target_relation);
+           pstate->p_target_relation = NULL;
+           pstate->p_target_rangetblentry = NULL;
            extras_before = lnext(extras_before);
        }
 
@@ -80,11 +87,13 @@ parse_analyze(List *pl, ParseState *parentParseState)
                             transformStmt(pstate, lfirst(extras_after)));
            if (pstate->p_target_relation != NULL)
                heap_close(pstate->p_target_relation);
+           pstate->p_target_relation = NULL;
+           pstate->p_target_rangetblentry = NULL;
            extras_after = lnext(extras_after);
        }
 
-       pl = lnext(pl);
        pfree(pstate);
+       pl = lnext(pl);
    }
 
    return result;
@@ -148,9 +157,9 @@ transformStmt(ParseState *pstate, Node *parseTree)
                result->commandType = CMD_UTILITY;
                result->utilityStmt = (Node *) parseTree;
                MemoryContextSwitchTo(oldcontext);
-               break;
-
            }
+           break;
+
        case T_ExplainStmt:
            {
                ExplainStmt *n = (ExplainStmt *) parseTree;
@@ -215,7 +224,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
    qry->commandType = CMD_DELETE;
 
    /* set up a range table */
-   makeRangeTable(pstate, stmt->relname, NULL, NULL);
+   makeRangeTable(pstate, NULL, NULL);
+   setTargetTable(pstate, stmt->relname);
 
    qry->uniqueFlag = NULL;
 
@@ -240,135 +250,70 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 static Query *
 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
-   Query      *qry = makeNode(Query);  /* make a new query tree */
+   Query      *qry = makeNode(Query);
+   Node       *fromQual;
    List       *icolumns;
+   List       *tl;
+   TupleDesc   rd_att;
 
    qry->commandType = CMD_INSERT;
    pstate->p_is_insert = true;
 
-   /* set up a range table */
-   makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+   /*----------
+    * Initial processing steps are just like SELECT, which should not
+    * be surprising, since we may be handling an INSERT ... SELECT.
+    * It is important that we finish processing all the SELECT subclauses
+    * before we start doing any INSERT-specific processing; otherwise
+    * the behavior of SELECT within INSERT might be different from a
+    * stand-alone SELECT.  (Indeed, Postgres up through 6.5 had bugs of
+    * just that nature...)
+    *----------
+    */
 
-   qry->uniqueFlag = stmt->unique;
+   /* set up a range table --- note INSERT target is not in it yet */
+   makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
-   /* fix the target list */
-   icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
+   qry->uniqueFlag = stmt->unique;
 
    qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-   /* DEFAULT handling */
-   if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
-       pstate->p_target_relation->rd_att->constr &&
-       pstate->p_target_relation->rd_att->constr->num_defval > 0)
-   {
-       Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs;
-       AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
-       int         ndef = pstate->p_target_relation->rd_att->constr->num_defval;
-
-       /*
-        * if stmt->cols == NIL then makeTargetNames returns list of all
-        * attrs. May have to shorten icolumns list...
-        */
-       if (stmt->cols == NIL)
-       {
-           List       *extrl;
-           int         i = length(qry->targetList);
-
-           foreach(extrl, icolumns)
-           {
-
-               /*
-                * decrements first, so if we started with zero items it
-                * will now be negative
-                */
-               if (--i <= 0)
-                   break;
-           }
-
-           /*
-            * this an index into the targetList, so make sure we had one
-            * to start...
-            */
-           if (i >= 0)
-           {
-               freeList(lnext(extrl));
-               lnext(extrl) = NIL;
-           }
-           else
-               icolumns = NIL;
-       }
+   qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-       while (ndef-- > 0)
-       {
-           List       *tl;
-           Ident      *id;
-           TargetEntry *te;
-
-           foreach(tl, icolumns)
-           {
-               id = (Ident *) lfirst(tl);
-               if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0)
-                   break;
-           }
-           if (tl != NIL)      /* something given for this attr */
-               continue;
-
-           /*
-            * Nothing given for this attr with DEFAULT expr, so add new
-            * TargetEntry to qry->targetList. Note, that we set resno to
-            * defval[ndef].adnum: it's what
-            * transformTargetList()->make_targetlist_expr() does for
-            * INSERT ... SELECT. But for INSERT ... VALUES
-            * pstate->p_last_resno is used. It doesn't matter for
-            * "normal" using (planner creates proper target list in
-            * preptlist.c), but may break RULEs in some way. It seems
-            * better to create proper target list here...
-            */
-           te = makeTargetEntry(makeResdom(defval[ndef].adnum,
-                                  att[defval[ndef].adnum - 1]->atttypid,
-                                 att[defval[ndef].adnum - 1]->atttypmod,
-              pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
-                                           0, 0, false),
-                             (Node *) stringToNode(defval[ndef].adbin));
-           qry->targetList = lappend(qry->targetList, te);
-       }
-   }
-
-   /* fix where clause */
-   qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
-
-   /*
-    * The havingQual has a similar meaning as "qual" in the where
-    * statement. So we can easily use the code from the "where clause"
-    * with some additional traversals done in
-    * .../optimizer/plan/planner.c
+   /* Initial processing of HAVING clause is just like WHERE clause.
+    * Additional work will be done in optimizer/plan/planner.c.
     */
    qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
 
-   qry->hasSubLinks = pstate->p_hasSubLinks;
-
-   /* now the range table will not change */
-   qry->rtable = pstate->p_rtable;
-   qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
-
    qry->groupClause = transformGroupClause(pstate,
                                            stmt->groupClause,
                                            qry->targetList);
 
-   /* fix order clause */
+   /* An InsertStmt has no sortClause, but we still call
+    * transformSortClause because it also handles uniqueFlag.
+    */
    qry->sortClause = transformSortClause(pstate,
                                          NIL,
                                          NIL,
                                          qry->targetList,
                                          qry->uniqueFlag);
 
+   qry->hasSubLinks = pstate->p_hasSubLinks;
    qry->hasAggs = pstate->p_hasAggs;
    if (pstate->p_hasAggs || qry->groupClause)
        parseCheckAggregates(pstate, qry);
 
+   /*
+    * If there is a havingQual but there are no aggregates, then there is
+    * something wrong with the query because HAVING must contain
+    * aggregates in its expressions! Otherwise the query could have been
+    * formulated using the WHERE clause.
+    */
+   if (qry->havingQual && ! qry->hasAggs)
+       elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
    /*
     * The INSERT INTO ... SELECT ... could have a UNION in child, so
-    * unionClause may be false ,
+    * unionClause may be false
     */
    qry->unionall = stmt->unionall;
 
@@ -380,15 +325,98 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
    qry->intersectClause = stmt->intersectClause;
 
    /*
-    * If there is a havingQual but there are no aggregates, then there is
-    * something wrong with the query because having must contain
-    * aggregates in its expressions! Otherwise the query could have been
-    * formulated using the where clause.
+    * Now we are done with SELECT-like processing, and can get on with
+    * transforming the target list to match the INSERT target columns.
+    *
+    * In particular, it's time to add the INSERT target to the rangetable.
+    * (We didn't want it there until now since it shouldn't be visible in
+    * the SELECT part.)
+    */
+   setTargetTable(pstate, stmt->relname);
+
+   /* now the range table will not change */
+   qry->rtable = pstate->p_rtable;
+   qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+
+   /* Prepare to assign non-conflicting resnos to resjunk attributes */
+   if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+       pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+   /* Validate stmt->cols list, or build default list if no list given */
+   icolumns = makeTargetNames(pstate, stmt->cols);
+
+   /* Prepare non-junk columns for assignment to target table */
+   foreach(tl, qry->targetList)
+   {
+       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+       Resdom     *resnode = tle->resdom;
+       Ident      *id;
+
+       if (resnode->resjunk)
+       {
+           /* Resjunk nodes need no additional processing, but be sure they
+            * have names and resnos that do not match any target columns;
+            * else rewriter or planner might get confused.
+            */
+           resnode->resname = "?resjunk?";
+           resnode->resno = (AttrNumber) pstate->p_last_resno++;
+           continue;
+       }
+       if (icolumns == NIL)
+           elog(ERROR, "INSERT has more expressions than target columns");
+       id = (Ident *) lfirst(icolumns);
+       updateTargetListEntry(pstate, tle, id->name, id->indirection);
+       icolumns = lnext(icolumns);
+   }
+
+   /*
+    * Add targetlist items to assign DEFAULT values to any columns that
+    * have defaults and were not assigned to by the user.
+    * XXX wouldn't it make more sense to do this further downstream,
+    * after the rule rewriter?
     */
-   if ((qry->hasAggs == false) && (qry->havingQual != NULL))
+   rd_att = pstate->p_target_relation->rd_att;
+   if (rd_att->constr && rd_att->constr->num_defval > 0)
    {
-       elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-       return (Query *) NIL;
+       Form_pg_attribute *att = rd_att->attrs;
+       AttrDefault *defval = rd_att->constr->defval;
+       int         ndef = rd_att->constr->num_defval;
+
+       while (ndef-- > 0)
+       {
+           Form_pg_attribute thisatt = att[defval[ndef].adnum - 1];
+           TargetEntry *te;
+
+           foreach(tl, qry->targetList)
+           {
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Resdom     *resnode = tle->resdom;
+
+               if (resnode->resjunk)
+                   continue;   /* ignore resjunk nodes */
+               if (namestrcmp(&(thisatt->attname), resnode->resname) == 0)
+                   break;
+           }
+           if (tl != NIL)      /* something given for this attr */
+               continue;
+           /*
+            * No user-supplied value, so add a targetentry with DEFAULT expr
+            * and correct data for the target column.
+            */
+           te = makeTargetEntry(
+               makeResdom(defval[ndef].adnum,
+                          thisatt->atttypid,
+                          thisatt->atttypmod,
+                          pstrdup(nameout(&(thisatt->attname))),
+                          0, 0, false),
+               stringToNode(defval[ndef].adbin));
+           qry->targetList = lappend(qry->targetList, te);
+           /*
+            * Make sure the value is coerced to the target column type
+            * (might not be right type if it's not a constant!)
+            */
+           updateTargetListEntry(pstate, te, te->resdom->resname, NIL);
+       }
    }
 
    if (stmt->forUpdate != NULL)
@@ -963,12 +991,12 @@ static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
    Query      *qry = makeNode(Query);
-   Node       *qual;
+   Node       *fromQual;
 
    qry->commandType = CMD_SELECT;
 
    /* set up a range table */
-   makeRangeTable(pstate, NULL, stmt->fromClause, &qual);
+   makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
    qry->uniqueFlag = stmt->unique;
 
@@ -978,16 +1006,16 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
    qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-   qry->qual = transformWhereClause(pstate, stmt->whereClause, qual);
+   qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-   /*
-    * The havingQual has a similar meaning as "qual" in the where
-    * statement. So we can easily use the code from the "where clause"
-    * with some additional traversals done in optimizer/plan/planner.c
+   /* Initial processing of HAVING clause is just like WHERE clause.
+    * Additional work will be done in optimizer/plan/planner.c.
     */
    qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
 
-   qry->hasSubLinks = pstate->p_hasSubLinks;
+   qry->groupClause = transformGroupClause(pstate,
+                                           stmt->groupClause,
+                                           qry->targetList);
 
    qry->sortClause = transformSortClause(pstate,
                                          stmt->sortClause,
@@ -995,15 +1023,20 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
                                          qry->targetList,
                                          qry->uniqueFlag);
 
-   qry->groupClause = transformGroupClause(pstate,
-                                           stmt->groupClause,
-                                           qry->targetList);
-   qry->rtable = pstate->p_rtable;
-
+   qry->hasSubLinks = pstate->p_hasSubLinks;
    qry->hasAggs = pstate->p_hasAggs;
    if (pstate->p_hasAggs || qry->groupClause)
        parseCheckAggregates(pstate, qry);
 
+   /*
+    * If there is a havingQual but there are no aggregates, then there is
+    * something wrong with the query because HAVING must contain
+    * aggregates in its expressions! Otherwise the query could have been
+    * formulated using the WHERE clause.
+    */
+   if (qry->havingQual && ! qry->hasAggs)
+       elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
    /*
     * The INSERT INTO ... SELECT ... could have a UNION in child, so
     * unionClause may be false
@@ -1017,17 +1050,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
    qry->unionClause = stmt->unionClause;
    qry->intersectClause = stmt->intersectClause;
 
-   /*
-    * If there is a havingQual but there are no aggregates, then there is
-    * something wrong with the query because having must contain
-    * aggregates in its expressions! Otherwise the query could have been
-    * formulated using the where clause.
-    */
-   if ((qry->hasAggs == false) && (qry->havingQual != NULL))
-   {
-       elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-       return (Query *) NIL;
-   }
+   qry->rtable = pstate->p_rtable;
 
    if (stmt->forUpdate != NULL)
        transformForUpdate(qry, stmt->forUpdate);
@@ -1044,6 +1067,8 @@ static Query *
 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
    Query      *qry = makeNode(Query);
+   List       *origTargetList;
+   List       *tl;
 
    qry->commandType = CMD_UPDATE;
    pstate->p_is_update = true;
@@ -1052,21 +1077,59 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
     * the FROM clause is non-standard SQL syntax. We used to be able to
     * do this with REPLACE in POSTQUEL so we keep the feature.
     */
-   makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+   makeRangeTable(pstate, stmt->fromClause, NULL);
+   setTargetTable(pstate, stmt->relname);
 
    qry->targetList = transformTargetList(pstate, stmt->targetList);
 
    qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
+
    qry->hasSubLinks = pstate->p_hasSubLinks;
 
    qry->rtable = pstate->p_rtable;
-
    qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
    qry->hasAggs = pstate->p_hasAggs;
    if (pstate->p_hasAggs)
        parseCheckAggregates(pstate, qry);
 
+   /*
+    * Now we are done with SELECT-like processing, and can get on with
+    * transforming the target list to match the UPDATE target columns.
+    */
+
+   /* Prepare to assign non-conflicting resnos to resjunk attributes */
+   if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+       pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+   /* Prepare non-junk columns for assignment to target table */
+   origTargetList = stmt->targetList;
+   foreach(tl, qry->targetList)
+   {
+       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+       Resdom     *resnode = tle->resdom;
+       ResTarget  *origTarget;
+
+       if (resnode->resjunk)
+       {
+           /* Resjunk nodes need no additional processing, but be sure they
+            * have names and resnos that do not match any target columns;
+            * else rewriter or planner might get confused.
+            */
+           resnode->resname = "?resjunk?";
+           resnode->resno = (AttrNumber) pstate->p_last_resno++;
+           continue;
+       }
+       if (origTargetList == NIL)
+           elog(ERROR, "UPDATE target count mismatch --- internal error");
+       origTarget = (ResTarget *) lfirst(origTargetList);
+       updateTargetListEntry(pstate, tle,
+                             origTarget->name, origTarget->indirection);
+       origTargetList = lnext(origTargetList);
+   }
+   if (origTargetList != NIL)
+       elog(ERROR, "UPDATE target count mismatch --- internal error");
+
    return (Query *) qry;
 }
 
index ce9583c174a23b9190921aa4e0442db5ca750887..f8dc7777b0609b881f2cfe428267bc3960055b06 100644 (file)
@@ -7,12 +7,13 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.41 1999/07/17 20:17:23 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.42 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
+
 #include "access/heapam.h"
 #include "nodes/relation.h"
 #include "parse.h"
 
 static char *clauseText[] = {"ORDER", "GROUP"};
 
-static TargetEntry *
-           findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
-
+static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
+                                       List *tlist, int clause);
 static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
+static char    *transformTableEntry(ParseState *pstate, RangeVar *r);
 
 #ifdef ENABLE_OUTER_JOINS
-Node      *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
-
+static Node *transformUsingClause(ParseState *pstate, List *onList,
+                                 char *lname, char *rname);
 #endif
 
-static char    *transformTableEntry(ParseState *pstate, RangeVar *r);
 
 
 /*
  * makeRangeTable -
- *   make a range table with the specified relation (optional) and the
- *   from_clause.
+ *   Build the initial range table from the FROM clause.
  */
 void
-makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
+makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
 {
-   RangeTblEntry *rte;
-   int         sublevels_up;
-
+   /* Currently, nothing to do except this: */
    parseFromClause(pstate, frmList, qual);
+}
 
-   if (relname == NULL)
-       return;
+/*
+ * setTargetTable
+ *   Add the target relation of INSERT or UPDATE to the range table,
+ *   and make the special links to it in the ParseState.
+ */
+void
+setTargetTable(ParseState *pstate, char *relname)
+{
+   RangeTblEntry *rte;
+   int         sublevels_up;
 
    if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
        || (sublevels_up != 0))
@@ -136,7 +142,7 @@ makeAttr(char *relname, char *attname)
 /* transformUsingClause()
  * Take an ON or USING clause from a join expression and expand if necessary.
  */
-Node *
+static Node *
 transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
 {
    A_Expr     *expr = NULL;
@@ -295,7 +301,8 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
                if (IsA(j->quals, List))
                    j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
 
-               Assert(qual != NULL);
+               if (qual == NULL)
+                   elog(ERROR, "JOIN/ON not supported in this context");
 
                if (*qual == NULL)
                    *qual = lfirst(j->quals);
@@ -329,145 +336,111 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
    }
 }
 
+
 /*
  * findTargetlistEntry -
- *   returns the Resdom in the target list matching the specified varname
- *   and range. If none exist one is created.
- *
- *   Rewritten for ver 6.4 to handle expressions in the GROUP/ORDER BY clauses.
- *    - [email protected]  1998-07-31
+ *   Returns the targetlist entry matching the given (untransformed) node.
+ *   If no matching entry exists, one is created and appended to the target
+ *   list as a "resjunk" node.
  *
+ * node        the ORDER BY or GROUP BY expression to be matched
+ * tlist   the existing target list (NB: this cannot be NIL, which is a
+ *         good thing since we'd be unable to append to it...)
+ * clause  identifies clause type for error messages.
  */
 static TargetEntry *
 findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
 {
-   List       *l;
-   int         rtable_pos = 0,
-               target_pos = 0,
-               targetlist_pos = 0;
    TargetEntry *target_result = NULL;
-   Value      *val = NULL;
-   char       *relname = NULL;
-   char       *name = NULL;
-   Node       *expr = NULL;
-   int         relCnt = 0;
-
-   /* Pull out some values before looping thru target list  */
-   switch (nodeTag(node))
-   {
-       case T_Attr:
-           relname = ((Attr *) node)->relname;
-           val = (Value *) lfirst(((Attr *) node)->attrs);
-           name = strVal(val);
-           rtable_pos = refnameRangeTablePosn(pstate, relname, NULL);
-           relCnt = length(pstate->p_rtable);
-           break;
-
-       case T_Ident:
-           name = ((Ident *) node)->name;
-           relCnt = length(pstate->p_rtable);
-           break;
-
-       case T_A_Const:
-           val = &((A_Const *) node)->val;
-
-           if (nodeTag(val) != T_Integer)
-               elog(ERROR, "Illegal Constant in %s BY", clauseText[clause]);
-           target_pos = intVal(val);
-           break;
-
-       case T_FuncCall:
-       case T_A_Expr:
-           expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-           break;
-
-       default:
-           elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-   }
+   List       *tl;
+   Node       *expr;
 
-   /*
-    * Loop through target entries and try to match to node
+   /*----------
+    * Handle two special cases as mandated by the SQL92 spec:
+    *
+    * 1. ORDER/GROUP BY ColumnName
+    *    For a bare identifier, we search for a matching column name
+    *    in the existing target list.  Multiple matches are an error
+    *    unless they refer to identical values; for example,
+    *    we allow  SELECT a, a FROM table ORDER BY a
+    *    but not   SELECT a AS b, b FROM table ORDER BY b
+    *    If no match is found, we fall through and treat the identifier
+    *    as an expression.
+    *
+    * 2. ORDER/GROUP BY IntegerConstant
+    *    This means to use the n'th item in the existing target list.
+    *    Note that it would make no sense to order/group by an actual
+    *    constant, so this does not create a conflict with our extension
+    *    to order/group by an expression.
+    *
+    * Note that pre-existing resjunk targets must not be used in either case.
+    *----------
     */
-   foreach(l, tlist)
+   if (IsA(node, Ident) && ((Ident *) node)->indirection == NIL)
    {
-       TargetEntry *target = (TargetEntry *) lfirst(l);
-       Resdom     *resnode = target->resdom;
-       Var        *var = (Var *) target->expr;
-       char       *resname = resnode->resname;
-       int         test_rtable_pos = var->varno;
-
-       ++targetlist_pos;
-
-       switch (nodeTag(node))
+       char       *name = ((Ident *) node)->name;
+       foreach(tl, tlist)
        {
-           case T_Attr:
-               if (strcmp(resname, name) == 0 && rtable_pos == test_rtable_pos)
-               {
-
-                   /*
-                    * Check for only 1 table & ORDER BY -ambiguity does
-                    * not matter here
-                    */
-                   if (clause == ORDER_CLAUSE && relCnt == 1)
-                       return target;
-
-                   if (target_result != NULL)
-                       elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
-                   else
-                       target_result = target;
-                   /* Stay in loop to check for ambiguity */
-               }
-               break;
-
-           case T_Ident:
-               if (strcmp(resname, name) == 0)
-               {
+           TargetEntry *tle = (TargetEntry *) lfirst(tl);
+           Resdom     *resnode = tle->resdom;
 
-                   /*
-                    * Check for only 1 table & ORDER BY  -ambiguity does
-                    * not matter here
-                    */
-                   if (clause == ORDER_CLAUSE && relCnt == 1)
-                       return target;
-
-                   if (target_result != NULL)
-                       elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
-                   else
-                       target_result = target;
-                   /* Stay in loop to check for ambiguity  */
-               }
-               break;
-
-           case T_A_Const:
-               if (target_pos == targetlist_pos)
+           if (!resnode->resjunk &&
+               strcmp(resnode->resname, name) == 0)
+           {
+               if (target_result != NULL)
                {
-                   /* Can't be ambigious and we got what we came for  */
-                   return target;
+                   if (! equal(target_result->expr, tle->expr))
+                       elog(ERROR, "%s BY '%s' is ambiguous",
+                            clauseText[clause], name);
                }
-               break;
+               else
+                   target_result = tle;
+               /* Stay in loop to check for ambiguity */
+           }
+       }
+       if (target_result != NULL)
+           return target_result; /* return the first match */
+   }
+   if (IsA(node, A_Const))
+   {
+       Value      *val = &((A_Const *) node)->val;
+       int         targetlist_pos = 0;
+       int         target_pos;
+
+       if (nodeTag(val) != T_Integer)
+           elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
+       target_pos = intVal(val);
+       foreach(tl, tlist)
+       {
+           TargetEntry *tle = (TargetEntry *) lfirst(tl);
+           Resdom     *resnode = tle->resdom;
 
-           case T_FuncCall:
-           case T_A_Expr:
-               if (equal(expr, target->expr))
-               {
+           if (!resnode->resjunk)
+           {
+               if (++targetlist_pos == target_pos)
+                   return tle; /* return the unique match */
+           }
+       }
+       elog(ERROR, "%s BY position %d is not in target list",
+            clauseText[clause], target_pos);
+   }
 
-                   /*
-                    * Check for only 1 table & ORDER BY  -ambiguity does
-                    * not matter here
-                    */
-                   if (clause == ORDER_CLAUSE)
-                       return target;
+   /*
+    * Otherwise, we have an expression (this is a Postgres extension
+    * not found in SQL92).  Convert the untransformed node to a
+    * transformed expression, and search for a match in the tlist.
+    * NOTE: it doesn't really matter whether there is more than one
+    * match.  Also, we are willing to match a resjunk target here,
+    * though the above cases must ignore resjunk targets.
+    */
+   expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
 
-                   if (target_result != NULL)
-                       elog(ERROR, "GROUP BY has ambiguous expression");
-                   else
-                       target_result = target;
-               }
-               break;
+   foreach(tl, tlist)
+   {
+       TargetEntry *tle = (TargetEntry *) lfirst(tl);
 
-           default:
-               elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-       }
+       if (equal(expr, tle->expr))
+           return tle;
    }
 
    /*
@@ -475,51 +448,13 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
     * the end of the target list.   This target is set to be  resjunk =
     * TRUE so that it will not be projected into the final tuple.
     */
-   if (target_result == NULL)
-   {
-       switch (nodeTag(node))
-       {
-           case T_Attr:
-               target_result = MakeTargetEntryIdent(pstate, node,
-                                        &((Attr *) node)->relname, NULL,
-                                        ((Attr *) node)->relname, true);
-               lappend(tlist, target_result);
-               break;
-
-           case T_Ident:
-               target_result = MakeTargetEntryIdent(pstate, node,
-                                          &((Ident *) node)->name, NULL,
-                                          ((Ident *) node)->name, true);
-               lappend(tlist, target_result);
-               break;
-
-           case T_A_Const:
-
-               /*
-                * If we got this far, then must have been an out-of-range
-                * column number
-                */
-               elog(ERROR, "%s BY position %d is not in target list", clauseText[clause], target_pos);
-               break;
-
-           case T_FuncCall:
-           case T_A_Expr:
-               target_result = MakeTargetEntryExpr(pstate, "resjunk", expr, false, true);
-               lappend(tlist, target_result);
-               break;
-
-           default:
-               elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-               break;
-       }
-   }
+   target_result = transformTargetEntry(pstate, node, expr, NULL, true);
+   lappend(tlist, target_result);
 
    return target_result;
 }
 
 
-
-
 /*
  * transformGroupClause -
  *   transform a Group By clause
@@ -529,55 +464,42 @@ List *
 transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
 {
    List       *glist = NIL,
-              *gl = NIL;
+              *gl,
+              *othergl;
+   int         nextgroupref = 1;
 
-   while (grouplist != NIL)
+   foreach(gl, grouplist)
    {
-       GroupClause *grpcl = makeNode(GroupClause);
        TargetEntry *restarget;
        Resdom     *resdom;
 
-       restarget = findTargetlistEntry(pstate, lfirst(grouplist), targetlist, GROUP_CLAUSE);
-
+       restarget = findTargetlistEntry(pstate, lfirst(gl),
+                                       targetlist, GROUP_CLAUSE);
        resdom = restarget->resdom;
-       grpcl->grpOpoid = oprid(oper("<",
-                                    resdom->restype,
-                                    resdom->restype, false));
-       if (glist == NIL)
-       {
-           int         groupref = length(glist) + 1;
 
-           restarget->resdom->resgroupref = groupref;
-           grpcl->tleGroupref = groupref;
+       /* avoid making duplicate grouplist entries */
+       foreach(othergl, glist)
+       {
+           GroupClause *gcl = (GroupClause *) lfirst(othergl);
 
-           gl = glist = lcons(grpcl, NIL);
+           if (equal(get_groupclause_expr(gcl, targetlist),
+                     restarget->expr))
+               break;
        }
-       else
-       {
-           List       *i;
 
-           foreach(i, glist)
-           {
-               GroupClause *gcl = (GroupClause *) lfirst(i);
+       if (othergl == NIL)     /* not in grouplist already */
+       {
+           GroupClause *grpcl = makeNode(GroupClause);
 
-               if (equal(get_groupclause_expr(gcl, targetlist),
-                         restarget->expr))
-                   break;
-           }
-           if (i == NIL)       /* not in grouplist already */
-           {
-               int         groupref = length(glist) + 1;
+           grpcl->tleGroupref = nextgroupref++;
+           resdom->resgroupref = grpcl->tleGroupref;
 
-               restarget->resdom->resgroupref = groupref;
-               grpcl->tleGroupref = groupref;
+           grpcl->grpOpoid = oprid(oper("<",
+                                        resdom->restype,
+                                        resdom->restype, false));
 
-               lnext(gl) = lcons(grpcl, NIL);
-               gl = lnext(gl);
-           }
-           else
-               pfree(grpcl);   /* get rid of this */
+           glist = lappend(glist, grpcl);
        }
-       grouplist = lnext(grouplist);
    }
 
    return glist;
@@ -604,7 +526,8 @@ transformSortClause(ParseState *pstate,
        TargetEntry *restarget;
        Resdom     *resdom;
 
-       restarget = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE);
+       restarget = findTargetlistEntry(pstate, sortby->node,
+                                       targetlist, ORDER_CLAUSE);
 
        sortcl->resdom = resdom = restarget->resdom;
 
index a936554ad9d221343ab937b32525541842e2085a..e9a6745334242cf082540109c71c22b90acc208f 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.54 1999/07/17 20:17:23 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.55 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,7 @@ static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod);
 static Node *transformAttr(ParseState *pstate, Attr *att, int precedence);
 static Node *transformIdent(ParseState *pstate, Ident *ident, int precedence);
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
-                                 List *indirection, int precedence);
+                                 List *indirection);
 
 /*
  * transformExpr -
@@ -81,7 +81,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                param->paramtype = (Oid) toid;
                param->param_tlist = (List *) NULL;
                result = transformIndirection(pstate, (Node *) param,
-                                             pno->indirection, precedence);
+                                             pno->indirection);
                break;
            }
        case T_A_Expr:
@@ -467,37 +467,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 }
 
 static Node *
-transformIndirection(ParseState *pstate, Node *basenode,
-                    List *indirection, int precedence)
+transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
 {
-   List       *idx;
-
    if (indirection == NIL)
        return basenode;
-   foreach (idx, indirection)
-   {
-       A_Indices  *ai = (A_Indices *) lfirst(idx);
-       Node       *lexpr = NULL,
-                  *uexpr;
-
-       /* uidx is always present, but lidx might be null */
-       if (ai->lidx != NULL)
-       {
-           lexpr = transformExpr(pstate, ai->lidx, precedence);
-           if (exprType(lexpr) != INT4OID)
-               elog(ERROR, "array index expressions must be int4's");
-       }
-       uexpr = transformExpr(pstate, ai->uidx, precedence);
-       if (exprType(uexpr) != INT4OID)
-           elog(ERROR, "array index expressions must be int4's");
-       ai->lidx = lexpr;
-       ai->uidx = uexpr;
-       /*
-        * note we reuse the list of A_Indices nodes, make sure
-        * we don't free them! Otherwise, make a new list here
-        */
-   }
-   return (Node *) make_array_ref(basenode, indirection);
+   return (Node *) transformArraySubscripts(pstate, basenode,
+                                            indirection, false, NULL);
 }
 
 static Node *
@@ -505,11 +480,9 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
 {
    Node       *basenode;
 
-   /* what if att->attrs == "*"? */
    basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
                                       precedence);
-   return transformIndirection(pstate, basenode,
-                               att->indirection, precedence);
+   return transformIndirection(pstate, basenode, att->indirection);
 }
 
 static Node *
@@ -555,7 +528,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
 Oid
 exprType(Node *expr)
 {
-   Oid         type = (Oid) 0;
+   Oid         type = (Oid) InvalidOid;
 
    if (!expr)
        return type;
@@ -622,6 +595,42 @@ exprType(Node *expr)
    return type;
 }
 
+/*
+ * exprTypmod -
+ *   returns the type-specific attrmod of the expression, if it can be
+ *   determined.  In most cases, it can't and we return -1.
+ */
+int32
+exprTypmod(Node *expr)
+{
+   if (!expr)
+       return -1;
+
+   switch (nodeTag(expr))
+   {
+       case T_Var:
+           return ((Var *) expr)->vartypmod;
+       case T_Const:
+           {
+               /* Be smart about string constants... */
+               Const  *con = (Const *) expr;
+               switch (con->consttype)
+               {
+                   case BPCHAROID:
+                       if (! con->constisnull)
+                           return VARSIZE(DatumGetPointer(con->constvalue));
+                       break;
+                   default:
+                       break;
+               }
+           }
+           break;
+       default:
+           break;
+   }
+   return -1;
+}
+
 static Node *
 parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
 {
index 53c756670c34b61390711cbc0e4674f94108fc44..80a8543d5a5addf8d9fe871bda674a4e0492a90d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.28 1999/07/17 20:17:24 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "parser/parse_node.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
+#include "parser/parse_target.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -222,164 +223,184 @@ make_var(ParseState *pstate, Oid relid, char *refname,
 }
 
 /*
- * make_array_ref() -- Make an array reference node.
+ * transformArraySubscripts()
+ *     Transform array subscripting.  This is used for both
+ *     array fetch and array assignment.
  *
- *     Array references can hang off of arbitrary nested dot (or
- *     function invocation) expressions.  This routine takes a
- *     tree generated by ParseFunc() and an array index and
- *     generates a new array reference tree.  We do some simple
- *     typechecking to be sure the dereference is valid in the
- *     type system, but we don't do any bounds checking here.
+ * In an array fetch, we are given a source array value and we produce an
+ * expression that represents the result of extracting a single array element
+ * or an array slice.
  *
- * indirection is a list of A_Indices
+ * In an array assignment, we are given a destination array value plus a
+ * source value that is to be assigned to a single element or a slice of
+ * that array.  We produce an expression that represents the new array value
+ * with the source data inserted into the right part of the array.
+ *
+ * pstate      Parse state
+ * arrayBase   Already-transformed expression for the array as a whole
+ * 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   *
-make_array_ref(Node *expr,
-              List *indirection)
+ArrayRef *
+transformArraySubscripts(ParseState *pstate,
+                        Node *arrayBase,
+                        List *indirection,
+                        bool forceSlice,
+                        Node *assignFrom)
 {
-   Oid         typearray;
+   Oid         typearray,
+               typeelement,
+               typeresult;
    HeapTuple   type_tuple;
    Form_pg_type type_struct_array,
                type_struct_element;
-   ArrayRef   *aref;
-   Oid         reftype;
+   bool        isSlice = forceSlice;
    List       *upperIndexpr = NIL;
    List       *lowerIndexpr = NIL;
+   List       *idx;
+   ArrayRef   *aref;
 
-   typearray = exprType(expr);
+   /* Get the type tuple for the array */
+   typearray = exprType(arrayBase);
 
    type_tuple = SearchSysCacheTuple(TYPOID,
                                     ObjectIdGetDatum(typearray),
                                     0, 0, 0);
-
    if (!HeapTupleIsValid(type_tuple))
-       elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
+       elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
             typearray);
-
-   /* get the array type struct from the type tuple */
    type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
 
-   if (type_struct_array->typelem == InvalidOid)
-       elog(ERROR, "make_array_ref: type %s is not an array",
+   typeelement = type_struct_array->typelem;
+   if (typeelement == InvalidOid)
+       elog(ERROR, "transformArraySubscripts: type %s is not an array",
             type_struct_array->typname);
 
-   /* get the type tuple for the element type */
+   /* Get the type tuple for the array element type */
    type_tuple = SearchSysCacheTuple(TYPOID,
-                           ObjectIdGetDatum(type_struct_array->typelem),
+                                    ObjectIdGetDatum(typeelement),
                                     0, 0, 0);
    if (!HeapTupleIsValid(type_tuple))
-       elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-            typearray);
-
+       elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
+            typeelement);
    type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
 
-   while (indirection != NIL)
+   /*
+    * 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.
+    */
+   if (! isSlice)
    {
-       A_Indices  *ind = lfirst(indirection);
-
-       if (ind->lidx)
-
-           /*
-            * XXX assumes all lower indices non null in this case
-            */
-           lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-
-       upperIndexpr = lappend(upperIndexpr, ind->uidx);
-       indirection = lnext(indirection);
+       foreach (idx, indirection)
+       {
+           A_Indices  *ai = (A_Indices *) lfirst(idx);
+           if (ai->lidx != NULL)
+           {
+               isSlice = true;
+               break;
+           }
+       }
    }
-   aref = makeNode(ArrayRef);
-   aref->refattrlength = type_struct_array->typlen;
-   aref->refelemlength = type_struct_element->typlen;
-   aref->refelemtype = type_struct_array->typelem;
-   aref->refelembyval = type_struct_element->typbyval;
-   aref->refupperindexpr = upperIndexpr;
-   aref->reflowerindexpr = lowerIndexpr;
-   aref->refexpr = expr;
-   aref->refassgnexpr = NULL;
 
-   if (lowerIndexpr == NIL)    /* accessing a single array element */
-       reftype = aref->refelemtype;
+   /* The type represented by the subscript expression is the element type
+    * if we are fetching a single element, but it is the same as the array
+    * type if we are fetching a slice or storing.
+    */
+   if (isSlice || assignFrom != NULL)
+       typeresult = typearray;
    else
-/* request to clip a part of the array, the result is another array */
-       reftype = typearray;
+       typeresult = typeelement;
 
    /*
-    * we change it to reflect the true type; since the original
-    * refelemtype doesn't seem to get used anywhere. - ay 10/94
+    * Transform the subscript expressions.
     */
-   aref->refelemtype = reftype;
-
-   return aref;
-}
-
-
-/* make_array_set()
- */
-ArrayRef   *
-make_array_set(Expr *target_expr,
-              List *upperIndexpr,
-              List *lowerIndexpr,
-              Expr *expr)
-{
-   Oid         typearray;
-   HeapTuple   type_tuple;
-   Form_pg_type type_struct_array;
-   Form_pg_type type_struct_element;
-   ArrayRef   *aref;
-   Oid         reftype;
-
-   typearray = exprType((Node *) target_expr);
-
-   type_tuple = SearchSysCacheTuple(TYPOID,
-                                    ObjectIdGetDatum(typearray),
-                                    0, 0, 0);
-
-   if (!HeapTupleIsValid(type_tuple))
-       elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-            typearray);
-
-   /* get the array type struct from the type tuple */
-   type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
+   foreach (idx, indirection)
+   {
+       A_Indices  *ai = (A_Indices *) lfirst(idx);
+       Node       *subexpr;
 
-   if (type_struct_array->typelem == InvalidOid)
-       elog(ERROR, "make_array_ref: type %s is not an array",
-            type_struct_array->typname);
-   /* get the type tuple for the element type */
-   type_tuple = SearchSysCacheTuple(TYPOID,
-                           ObjectIdGetDatum(type_struct_array->typelem),
-                                    0, 0, 0);
+       if (isSlice)
+       {
+           if (ai->lidx)
+           {
+               subexpr = transformExpr(pstate, ai->lidx, EXPR_COLUMN_FIRST);
+               /* If it's not int4 already, try to coerce */
+               subexpr = CoerceTargetExpr(pstate, subexpr,
+                                          exprType(subexpr), INT4OID);
+               if (subexpr == NULL)
+                   elog(ERROR, "array index expressions must be integers");
+           }
+           else
+           {
+               /* Make a constant 1 */
+               subexpr = (Node *) makeConst(INT4OID,
+                                            sizeof(int32),
+                                            Int32GetDatum(1),
+                                            false,
+                                            true, /* pass by value */
+                                            false,
+                                            false);
+           }
+           lowerIndexpr = lappend(lowerIndexpr, subexpr);
+       }
+       subexpr = transformExpr(pstate, ai->uidx, EXPR_COLUMN_FIRST);
+       /* If it's not int4 already, try to coerce */
+       subexpr = CoerceTargetExpr(pstate, subexpr,
+                                  exprType(subexpr), INT4OID);
+       if (subexpr == NULL)
+           elog(ERROR, "array index expressions must be integers");
+       upperIndexpr = lappend(upperIndexpr, subexpr);
+   }
 
-   if (!HeapTupleIsValid(type_tuple))
-       elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-            typearray);
+   /*
+    * If doing an array store, coerce the source value to the right type.
+    */
+   if (assignFrom != NULL)
+   {
+       Oid         typesource = exprType(assignFrom);
+       Oid         typeneeded = isSlice ? typearray : typeelement;
 
-   type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
+       if (typesource != InvalidOid)
+       {
+           if (typesource != typeneeded)
+           {
+               assignFrom = CoerceTargetExpr(pstate, assignFrom,
+                                             typesource, typeneeded);
+               if (assignFrom == NULL)
+                   elog(ERROR, "Array assignment requires type '%s'"
+                        " but expression is of type '%s'"
+                        "\n\tYou will need to rewrite or cast the expression",
+                        typeidTypeName(typeneeded),
+                        typeidTypeName(typesource));
+           }
+       }
+   }
 
+   /*
+    * Ready to build the ArrayRef node.
+    */
    aref = makeNode(ArrayRef);
    aref->refattrlength = type_struct_array->typlen;
    aref->refelemlength = type_struct_element->typlen;
-   aref->refelemtype = type_struct_array->typelem;
+   aref->refelemtype = typeresult; /* XXX should save element type too */
    aref->refelembyval = type_struct_element->typbyval;
    aref->refupperindexpr = upperIndexpr;
    aref->reflowerindexpr = lowerIndexpr;
-   aref->refexpr = (Node *) target_expr;
-   aref->refassgnexpr = (Node *) expr;
-
-   /* accessing a single array element? */
-   if (lowerIndexpr == NIL)
-       reftype = aref->refelemtype;
-
-   /* otherwise, request to set a part of the array, by another array */
-   else
-       reftype = typearray;
-
-   aref->refelemtype = reftype;
+   aref->refexpr = arrayBase;
+   aref->refassgnexpr = assignFrom;
 
    return aref;
 }
 
 /*
- *
  * make_const -
  *
  * - takes a lispvalue, (as returned to the yacc routine by the lexer)
index d3662aea8263226945fbfd1411e8db2a604e2555..79b08635f0273ada348dedccc34bfc1d798c0480 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.25 1999/07/17 20:17:25 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.26 1999/07/19 00:26:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 
-static void checkTargetTypes(ParseState *pstate, char *target_colname,
-                char *refname, char *colname);
 
-struct
+static struct
 {
    char       *field;
    int         code;
@@ -97,7 +95,6 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
    int         index;
    List       *temp;
 
-
    if (sublevels_up)
        *sublevels_up = 0;
 
@@ -175,7 +172,7 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
 /*
  * put new entry in pstate p_rtable structure, or return pointer
  * if pstate null
-*/
+ */
 RangeTblEntry *
 addRangeTableEntry(ParseState *pstate,
                   char *relname,
@@ -239,38 +236,31 @@ addRangeTableEntry(ParseState *pstate,
 List *
 expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
 {
+   List       *te_list = NIL;
+   RangeTblEntry *rte;
    Relation    rel;
-   List       *te_tail = NIL,
-              *te_head = NIL;
-   Var        *varnode;
    int         varattno,
                maxattrs;
-   RangeTblEntry *rte;
 
    rte = refnameRangeTableEntry(pstate, refname);
    if (rte == NULL)
        rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
 
    rel = heap_open(rte->relid);
-
    if (rel == NULL)
        elog(ERROR, "Unable to expand all -- heap_open failed on %s",
             rte->refname);
 
    maxattrs = RelationGetNumberOfAttributes(rel);
 
-   for (varattno = 0; varattno <= maxattrs - 1; varattno++)
+   for (varattno = 0; varattno < maxattrs; varattno++)
    {
        char       *attrname;
-       char       *resname = NULL;
+       Var        *varnode;
        TargetEntry *te = makeNode(TargetEntry);
 
-       attrname = pstrdup((rel->rd_att->attrs[varattno]->attname).data);
-       varnode = (Var *) make_var(pstate, rte->relid, refname, attrname);
-
-       handleTargetColname(pstate, &resname, refname, attrname);
-       if (resname != NULL)
-           attrname = resname;
+       attrname = pstrdup(rel->rd_att->attrs[varattno]->attname.data);
+       varnode = make_var(pstate, rte->relid, refname, attrname);
 
        /*
         * Even if the elements making up a set are complex, the set
@@ -285,15 +275,12 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
                                (Oid) 0,
                                false);
        te->expr = (Node *) varnode;
-       if (te_head == NIL)
-           te_head = te_tail = lcons(te, NIL);
-       else
-           te_tail = lappend(te_tail, te);
+       te_list = lappend(te_list, te);
    }
 
    heap_close(rel);
 
-   return te_head;
+   return te_list;
 }
 
 /*
@@ -378,102 +365,3 @@ attnumTypeId(Relation rd, int attid)
     */
    return rd->rd_att->attrs[attid - 1]->atttypid;
 }
-
-/* handleTargetColname()
- * Use column names from insert.
- */
-void
-handleTargetColname(ParseState *pstate, char **resname,
-                   char *refname, char *colname)
-{
-   if (pstate->p_is_insert)
-   {
-       if (pstate->p_insert_columns != NIL)
-       {
-           Ident      *id = lfirst(pstate->p_insert_columns);
-
-           *resname = id->name;
-           pstate->p_insert_columns = lnext(pstate->p_insert_columns);
-       }
-       else
-           elog(ERROR, "INSERT has more expressions than target columns");
-   }
-   if (pstate->p_is_insert || pstate->p_is_update)
-       checkTargetTypes(pstate, *resname, refname, colname);
-}
-
-/* checkTargetTypes()
- * Checks value and target column types.
- */
-static void
-checkTargetTypes(ParseState *pstate, char *target_colname,
-                char *refname, char *colname)
-{
-   Oid         attrtype_id,
-               attrtype_target;
-   int         resdomno_id,
-               resdomno_target;
-   RangeTblEntry *rte;
-
-   if (target_colname == NULL || colname == NULL)
-       return;
-
-   if (refname != NULL)
-       rte = refnameRangeTableEntry(pstate, refname);
-   else
-   {
-       rte = colnameRangeTableEntry(pstate, colname);
-       if (rte == (RangeTblEntry *) NULL)
-           elog(ERROR, "Attribute %s not found", colname);
-       refname = rte->refname;
-   }
-
-/*
-   if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry)
-       elog(ERROR, "'%s' not available in this context", colname);
-*/
-   resdomno_id = get_attnum(rte->relid, colname);
-   attrtype_id = get_atttype(rte->relid, resdomno_id);
-
-   resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
-   attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
-
-#ifdef NOT_USED
-   if ((attrtype_id != attrtype_target)
-       || (get_atttypmod(rte->relid, resdomno_id) !=
-      get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target)))
-   {
-       if (can_coerce_type(1, &attrtype_id, &attrtype_target))
-       {
-           Node       *expr = coerce_type(pstate, expr, attrtype_id,
-                                          attrtype_target,
-                                          get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-
-           elog(ERROR, "Type %s(%d) can be coerced to match target column %s(%d)",
-                colname, get_atttypmod(rte->relid, resdomno_id),
-                target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-       }
-       else
-       {
-           elog(ERROR, "Type or size of %s(%d) does not match target column %s(%d)",
-                colname, get_atttypmod(rte->relid, resdomno_id),
-                target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-       }
-   }
-#else
-   if (attrtype_id != attrtype_target)
-       elog(ERROR, "Type of '%s' does not match target column '%s'",
-            colname, target_colname);
-
-   if (attrtype_id == BPCHAROID &&
-       get_atttypmod(rte->relid, resdomno_id) !=
-       get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
-       elog(ERROR, "Length of '%s' is not equal to the length of target column '%s'",
-            colname, target_colname);
-   if (attrtype_id == VARCHAROID &&
-       get_atttypmod(rte->relid, resdomno_id) >
-       get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
-       elog(ERROR, "Length of '%s' is longer than length of target column '%s'",
-            colname, target_colname);
-#endif
-}
index 35d064b09808d5c44a8a26afb7a00fb7a9c2c053..48973f67d9b928a7532ab2688d4dd45076e661b4 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.45 1999/07/17 20:17:26 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.46 1999/07/19 00:26:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/syscache.h"
 
 
+static Node *SizeTargetExpr(ParseState *pstate, Node *expr,
+                           Oid attrtype, int32 attrtypmod);
 static List *ExpandAllTables(ParseState *pstate);
 static char *FigureColname(Node *expr, Node *resval);
-static Node *SizeTargetExpr(ParseState *pstate,
-              Node *expr,
-              Oid attrtype,
-              int32 attrtypmod);
 
 
-/* MakeTargetEntryIdent()
- * Transforms an Ident Node to a Target Entry
- * Created this function to allow the ORDER/GROUP BY clause to be able
- * to construct a TargetEntry from an Ident.
- *
- * resjunk = TRUE will hide the target entry in the final result tuple.
- *     [email protected]     5/20/98
+/*
+ * transformTargetEntry()
+ * Transform any ordinary "expression-type" node into a targetlist entry.
+ * This is exported so that parse_clause.c can generate targetlist entries
+ * for ORDER/GROUP BY items that are not already in the targetlist.
  *
- * Added more conversion logic to match up types from source to target.
- * - thomas 1998-06-02
+ * node        the (untransformed) parse tree for the value expression.
+ * expr        the transformed expression, or NULL if caller didn't do it yet.
+ * colname the column name to be assigned, or NULL if none yet set.
+ * resjunk true if the target should be marked resjunk, ie, it is not
+ *         wanted in the final projected tuple.
  */
 TargetEntry *
-MakeTargetEntryIdent(ParseState *pstate,
+transformTargetEntry(ParseState *pstate,
                     Node *node,
-                    char **resname,
-                    char *refname,
+                    Node *expr,
                     char *colname,
                     bool resjunk)
 {
-   Node       *expr = NULL;
-   Oid         attrtype_target;
-   TargetEntry *tent = makeNode(TargetEntry);
-
-   if (pstate->p_is_insert && !resjunk)
-   {
-
-       /*
-        * Assign column name of destination column to the new TLE. XXX
-        * this is probably WRONG in INSERT ... SELECT case, since
-        * handling of GROUP BY and so forth probably should use the
-        * source table's names not the destination's names.
-        */
-       if (pstate->p_insert_columns != NIL)
-       {
-           Ident      *id = lfirst(pstate->p_insert_columns);
-
-           *resname = id->name;
-           pstate->p_insert_columns = lnext(pstate->p_insert_columns);
-       }
-       else
-           elog(ERROR, "INSERT has more expressions than target columns");
-   }
-
-   if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
-   {
-       Oid         attrtype_id;
-       int         resdomno_id,
-                   resdomno_target;
-       RangeTblEntry *rte;
-       char       *target_colname;
-       int32       attrtypmod,
-                   attrtypmod_target;
-
-       target_colname = *resname;
-
-       /*
-        * this looks strange to me, returning an empty TargetEntry bjm
-        * 1998/08/24
-        */
-       if (target_colname == NULL || colname == NULL)
-           return tent;
-
-       if (refname != NULL)
-           rte = refnameRangeTableEntry(pstate, refname);
-       else
-       {
-           rte = colnameRangeTableEntry(pstate, colname);
-           if (rte == (RangeTblEntry *) NULL)
-               elog(ERROR, "Attribute %s not found", colname);
-           refname = rte->refname;
-       }
-
-       resdomno_id = get_attnum(rte->relid, colname);
-       attrtype_id = get_atttype(rte->relid, resdomno_id);
-       attrtypmod = get_atttypmod(rte->relid, resdomno_id);
-
-       resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
-       attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
-       attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target);
-
-       if ((attrtype_id != attrtype_target)
-           || ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod)))
-       {
-           if (can_coerce_type(1, &attrtype_id, &attrtype_target))
-           {
-               expr = coerce_type(pstate, node, attrtype_id,
-                                  attrtype_target,
-                                  get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-               expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST);
-               tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false);
-               expr = tent->expr;
-           }
-           else
-           {
-               elog(ERROR, "Unable to convert %s to %s for column %s",
-                    typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
-                    target_colname);
-           }
-       }
-   }
-
-   /*
-    * here we want to look for column names only, not relation names
-    * (even though they can be stored in Ident nodes, too)
-    */
-   if (expr == NULL)
-   {
-       char       *name;
-       int32       type_mod;
-
-       name = ((*resname != NULL) ? *resname : colname);
-
-       expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-
-       attrtype_target = exprType(expr);
-       if (nodeTag(expr) == T_Var)
-           type_mod = ((Var *) expr)->vartypmod;
-       else
-           type_mod = -1;
-
-       tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                 (Oid) attrtype_target,
-                                 type_mod,
-                                 name,
-                                 (Index) 0,
-                                 (Oid) 0,
-                                 resjunk);
-       tent->expr = expr;
-   }
-
-   return tent;
-}  /* MakeTargetEntryIdent() */
-
-
-/* MakeTargetEntryExpr()
- * Make a TargetEntry from an expression.
- * arrayRef is a list of transformed A_Indices.
- *
- * For type mismatches between expressions and targets, use the same
- * techniques as for function and operator type coersion.
- * - thomas 1998-05-08
- *
- * Added resjunk flag and made extern so that it can be use by GROUP/
- * ORDER BY a function or expression not in the target_list
- * -  [email protected] 1998-07-31
- */
-TargetEntry *
-MakeTargetEntryExpr(ParseState *pstate,
-                   char *colname,
-                   Node *expr,
-                   List *arrayRef,
-                   bool resjunk)
-{
-   Oid         type_id,
-               attrtype;
-   int32       type_mod,
-               attrtypmod;
-   int         resdomno;
-   Relation    rd;
-   bool        attrisset;
+   Oid         type_id;
+   int32       type_mod;
    Resdom     *resnode;
 
+   /* Transform the node if caller didn't do it already */
    if (expr == NULL)
-       elog(ERROR, "Invalid use of NULL expression (internal error)");
+       expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
 
    type_id = exprType(expr);
-   if (nodeTag(expr) == T_Var)
-       type_mod = ((Var *) expr)->vartypmod;
-   else
-       type_mod = -1;
+   type_mod = exprTypmod(expr);
 
-   /* Process target columns that will be receiving results */
-   if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
+   if (colname == NULL)
    {
-
-       /*
-        * insert or update query -- insert, update work only on one
-        * relation, so multiple occurence of same resdomno is bogus
+       /* Generate a suitable column name for a column without any
+        * explicit 'AS ColumnName' clause.
         */
-       rd = pstate->p_target_relation;
-       Assert(rd != NULL);
-       resdomno = attnameAttNum(rd, colname);
-       if (resdomno <= 0)
-           elog(ERROR, "Cannot assign to system attribute '%s'", colname);
-       attrisset = attnameIsSet(rd, colname);
-       attrtype = attnumTypeId(rd, resdomno);
-       if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
-           attrtype = GetArrayElementType(attrtype);
-       attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-
-       /*
-        * Check for InvalidOid since that seems to indicate a NULL
-        * constant...
-        */
-       if (type_id != InvalidOid)
-       {
-           /* Mismatch on types? then try to coerce to target...  */
-           if (attrtype != type_id)
-           {
-               Oid         typelem;
-
-               if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
-                   typelem = typeTypElem(typeidType(attrtype));
-               else
-                   typelem = attrtype;
-
-               expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
-
-               if (!HeapTupleIsValid(expr))
-                   elog(ERROR, "Attribute '%s' is of type '%s'"
-                        " but expression is of type '%s'"
-                   "\n\tYou will need to rewrite or cast the expression",
-                        colname,
-                        typeidTypeName(attrtype),
-                        typeidTypeName(type_id));
-           }
-
-           /*
-            * Apparently going to a fixed-length string? Then explicitly
-            * size for storage...
-            */
-           if (attrtypmod > 0)
-               expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod);
-       }
-
-       if (arrayRef != NIL)
-       {
-           Expr       *target_expr;
-           Attr       *att = makeNode(Attr);
-           List       *ar = arrayRef;
-           List       *upperIndexpr = NIL;
-           List       *lowerIndexpr = NIL;
-
-           att->relname = pstrdup(RelationGetRelationName(rd)->data);
-           att->attrs = lcons(makeString(colname), NIL);
-           target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
-                                                  &pstate->p_last_resno,
-                                                     EXPR_COLUMN_FIRST);
-           while (ar != NIL)
-           {
-               A_Indices  *ind = lfirst(ar);
-
-               if (lowerIndexpr || (!upperIndexpr && ind->lidx))
-               {
-
-                   /*
-                    * XXX assume all lowerIndexpr is non-null in this
-                    * case
-                    */
-                   lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-               }
-               upperIndexpr = lappend(upperIndexpr, ind->uidx);
-               ar = lnext(ar);
-           }
-
-           expr = (Node *) make_array_set(target_expr,
-                                          upperIndexpr,
-                                          lowerIndexpr,
-                                          (Expr *) expr);
-           attrtype = attnumTypeId(rd, resdomno);
-           attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno);
-       }
-   }
-   else
-   {
-       resdomno = pstate->p_last_resno++;
-       attrtype = type_id;
-       attrtypmod = type_mod;
+       colname = FigureColname(expr, node);
    }
 
-   resnode = makeResdom((AttrNumber) resdomno,
-                        (Oid) attrtype,
-                        attrtypmod,
+   resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
+                        type_id,
+                        type_mod,
                         colname,
                         (Index) 0,
-                        (Oid) 0,
+                        (Oid) InvalidOid,
                         resjunk);
 
    return makeTargetEntry(resnode, expr);
-}  /* MakeTargetEntryExpr() */
-
-/*
- * MakeTargetEntryCase()
- * Make a TargetEntry from a case node.
- */
-static TargetEntry *
-MakeTargetEntryCase(ParseState *pstate,
-                   ResTarget *res)
-{
-   TargetEntry *tent;
-   CaseExpr   *expr;
-   Resdom     *resnode;
-   int         resdomno;
-   Oid         type_id;
-   int32       type_mod;
-
-   expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
-   type_id = expr->casetype;
-   type_mod = -1;
-   handleTargetColname(pstate, &res->name, NULL, NULL);
-   if (res->name == NULL)
-       res->name = FigureColname((Node *) expr, res->val);
-
-   resdomno = pstate->p_last_resno++;
-   resnode = makeResdom((AttrNumber) resdomno,
-                        (Oid) type_id,
-                        type_mod,
-                        res->name,
-                        (Index) 0,
-                        (Oid) 0,
-                        false);
-
-   tent = makeNode(TargetEntry);
-   tent->resdom = resnode;
-   tent->expr = (Node *) expr;
+}
 
-   return tent;
-}  /* MakeTargetEntryCase() */
 
 /*
- * MakeTargetEntryComplex()
- * Make a TargetEntry from a complex node.
+ * transformTargetList()
+ * 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.
  */
-static TargetEntry *
-MakeTargetEntryComplex(ParseState *pstate,
-                      ResTarget *res)
+List *
+transformTargetList(ParseState *pstate, List *targetlist)
 {
-   Node       *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
+   List       *p_target = NIL;
 
-   handleTargetColname(pstate, &res->name, NULL, NULL);
-   /* note indirection has not been transformed */
-   if (pstate->p_is_insert && res->indirection != NIL)
+   while (targetlist != NIL)
    {
-       /* this is an array assignment */
-       char       *val;
-       char       *str,
-                  *save_str;
-       List       *elt;
-       int         i = 0,
-                   ndims;
-       int         lindx[MAXDIM],
-                   uindx[MAXDIM];
-       int         resdomno;
-       Relation    rd;
-       Value      *constval;
-
-       if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
-           elog(ERROR, "String constant expected (internal error)");
-
-       val = (char *) textout((struct varlena *)
-                              ((Const *) expr)->constvalue);
-       str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
-       foreach(elt, res->indirection)
-       {
-           A_Indices  *aind = (A_Indices *) lfirst(elt);
+       ResTarget  *res = (ResTarget *) lfirst(targetlist);
 
-           aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
-           if (!IsA(aind->uidx, Const))
-               elog(ERROR, "Array Index for Append should be a constant");
+       if (IsA(res->val, Attr))
+       {
+           Attr       *att = (Attr *) res->val;
 
-           uindx[i] = ((Const *) aind->uidx)->constvalue;
-           if (aind->lidx != NULL)
+           if (att->relname != NULL && strcmp(att->relname, "*") == 0)
            {
-               aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
-               if (!IsA(aind->lidx, Const))
-                   elog(ERROR, "Array Index for Append should be a constant");
-
-               lindx[i] = ((Const *) aind->lidx)->constvalue;
+               /*
+                * Target item is a single '*', expand all tables
+                * (eg. SELECT * FROM emp)
+                */
+               p_target = nconc(p_target,
+                                ExpandAllTables(pstate));
+           }
+           else if (att->attrs != NIL &&
+                    strcmp(strVal(lfirst(att->attrs)), "*") == 0)
+           {
+               /*
+                * Target item is relation.*, expand that table
+                * (eg. SELECT emp.*, dname FROM emp, dept)
+                */
+               p_target = nconc(p_target,
+                                expandAll(pstate,
+                                          att->relname,
+                                          att->relname,
+                                          &pstate->p_last_resno));
            }
            else
-               lindx[i] = 1;
-           if (lindx[i] > uindx[i])
-               elog(ERROR, "Lower index cannot be greater than upper index");
-
-           sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
-           str += strlen(str);
-           i++;
+           {
+               /* Plain Attr node, treat it as an expression */
+               p_target = lappend(p_target,
+                                  transformTargetEntry(pstate,
+                                                       res->val,
+                                                       NULL,
+                                                       res->name,
+                                                       false));
+           }
        }
-       sprintf(str, "=%s", val);
-       rd = pstate->p_target_relation;
-       Assert(rd != NULL);
-       resdomno = attnameAttNum(rd, res->name);
-       ndims = attnumAttNelems(rd, resdomno);
-       if (i != ndims)
-           elog(ERROR, "Array dimensions do not match");
-
-       constval = makeNode(Value);
-       constval->type = T_String;
-       constval->val.str = save_str;
-       return MakeTargetEntryExpr(pstate, res->name,
-                                  (Node *) make_const(constval),
-                                  NULL, false);
-       pfree(save_str);
-   }
-   else
-   {
-       /* this is not an array assignment */
-       char       *colname = res->name;
-
-       if (colname == NULL)
+       else
        {
-
-           /*
-            * if you're wondering why this is here, look at the yacc
-            * grammar for why a name can be missing. -ay
-            */
-           colname = FigureColname(expr, res->val);
+           /* Everything else but Attr */
+           p_target = lappend(p_target,
+                              transformTargetEntry(pstate,
+                                                   res->val,
+                                                   NULL,
+                                                   res->name,
+                                                   false));
        }
-       if (res->indirection)
-       {
-           List       *ilist = res->indirection;
-
-           while (ilist != NIL)
-           {
-               A_Indices  *ind = lfirst(ilist);
 
-               ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-               ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-               ilist = lnext(ilist);
-           }
-       }
-       res->name = colname;
-       return MakeTargetEntryExpr(pstate, res->name, expr,
-                                  res->indirection, false);
+       targetlist = lnext(targetlist);
    }
+
+   return p_target;
 }
 
+
 /*
- * MakeTargetEntryAttr()
- * Make a TargetEntry from a complex node.
+ * updateTargetListEntry()
+ * 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.
+ *
+ * pstate      parse state
+ * tle         target list entry to be modified
+ * colname     target column name (ie, name of attribute to be assigned to)
+ * indirection subscripts for target column, if any
  */
-static TargetEntry *
-MakeTargetEntryAttr(ParseState *pstate,
-                   ResTarget *res)
+void
+updateTargetListEntry(ParseState *pstate,
+                     TargetEntry *tle,
+                     char *colname,
+                     List *indirection)
 {
-   Oid         type_id;
-   int32       type_mod;
-   Attr       *att = (Attr *) res->val;
-   Node       *result;
-   char       *attrname;
-   char       *resname;
-   Resdom     *resnode;
+   Oid         type_id = exprType(tle->expr); /* type of value provided */
+   Oid         attrtype;       /* type of target column */
+   int32       attrtypmod;
+   Resdom     *resnode = tle->resdom;
+   Relation    rd = pstate->p_target_relation;
    int         resdomno;
-   List       *attrs = att->attrs;
-   TargetEntry *tent;
 
-   attrname = strVal(lfirst(att->attrs));
+   Assert(rd != NULL);
+   resdomno = attnameAttNum(rd, colname);
+   if (resdomno <= 0)
+       elog(ERROR, "Cannot assign to system attribute '%s'", colname);
+   attrtype = attnumTypeId(rd, resdomno);
+   attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
 
    /*
-    * Target item is fully specified: ie. relation.attribute
+    * 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.
     */
-   result = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST);
-   handleTargetColname(pstate, &res->name, att->relname, attrname);
-   if (att->indirection != NIL)
+   if (indirection)
    {
-       List       *ilist = att->indirection;
-
-       while (ilist != NIL)
+       Attr       *att = makeNode(Attr);
+       Node       *arrayBase;
+       ArrayRef   *aref;
+
+       att->relname = pstrdup(RelationGetRelationName(rd)->data);
+       att->attrs = lcons(makeString(colname), NIL);
+       arrayBase = ParseNestedFuncOrColumn(pstate, att,
+                                           &pstate->p_last_resno,
+                                           EXPR_COLUMN_FIRST);
+       aref = transformArraySubscripts(pstate, arrayBase,
+                                       indirection,
+                                       pstate->p_is_insert,
+                                       tle->expr);
+       if (pstate->p_is_insert)
        {
-           A_Indices  *ind = lfirst(ilist);
-
-           ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-           ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-           ilist = lnext(ilist);
+           /*
+            * 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 forced transformArraySubscripts to treat
+            * the subscripting op as an array-slice op above, so the
+            * source data will have been coerced to array type.
+            */
+           aref->refexpr = NULL; /* signal there is no source array */
        }
-       result = (Node *) make_array_ref(result, att->indirection);
+       tle->expr = (Node *) aref;
    }
-   type_id = exprType(result);
-   if (nodeTag(result) == T_Var)
-       type_mod = ((Var *) result)->vartypmod;
    else
-       type_mod = -1;
-   /* move to last entry */
-   while (lnext(attrs) != NIL)
-       attrs = lnext(attrs);
-   resname = (res->name) ? res->name : strVal(lfirst(attrs));
-   if (pstate->p_is_insert || pstate->p_is_update)
    {
-       Relation    rd;
-
        /*
-        * insert or update query -- insert, update work only on one
-        * relation, so multiple occurence of same resdomno is bogus
+        * For normal non-subscripted target column, do type checking
+        * and coercion.  But accept InvalidOid, which indicates the
+        * source is a NULL constant.
         */
-       rd = pstate->p_target_relation;
-       Assert(rd != NULL);
-       resdomno = attnameAttNum(rd, res->name);
-   }
-   else
-       resdomno = pstate->p_last_resno++;
-   resnode = makeResdom((AttrNumber) resdomno,
-                        (Oid) type_id,
-                        type_mod,
-                        resname,
-                        (Index) 0,
-                        (Oid) 0,
-                        false);
-   tent = makeNode(TargetEntry);
-   tent->resdom = resnode;
-   tent->expr = result;
-   return tent;
-}
-
-
-/* transformTargetList()
- * Turns a list of ResTarget's into a list of TargetEntry's.
- */
-List *
-transformTargetList(ParseState *pstate, List *targetlist)
-{
-   List       *p_target = NIL;
-   List       *tail_p_target = NIL;
-
-   while (targetlist != NIL)
-   {
-       ResTarget  *res = (ResTarget *) lfirst(targetlist);
-       TargetEntry *tent = NULL;
-
-       switch (nodeTag(res->val))
-       {
-           case T_Ident:
-               {
-                   char       *identname;
-
-                   identname = ((Ident *) res->val)->name;
-                   tent = MakeTargetEntryIdent(pstate,
-                                               (Node *) res->val, &res->name, NULL, identname, false);
-                   break;
-               }
-           case T_ParamNo:
-           case T_FuncCall:
-           case T_A_Const:
-           case T_A_Expr:
-               {
-                   tent = MakeTargetEntryComplex(pstate, res);
-                   break;
-               }
-           case T_CaseExpr:
-               {
-                   tent = MakeTargetEntryCase(pstate, res);
-                   break;
-               }
-           case T_Attr:
-               {
-                   bool        expand_star = false;
-                   char       *attrname;
-                   Attr       *att = (Attr *) res->val;
-
-                   /*
-                    * Target item is a single '*', expand all tables (eg.
-                    * SELECT * FROM emp)
-                    */
-                   if (att->relname != NULL && !strcmp(att->relname, "*"))
-                   {
-                       if (tail_p_target == NIL)
-                           p_target = tail_p_target = ExpandAllTables(pstate);
-                       else
-                           lnext(tail_p_target) = ExpandAllTables(pstate);
-                       expand_star = true;
-                   }
-                   else
-                   {
-
-                       /*
-                        * Target item is relation.*, expand the table
-                        * (eg. SELECT emp.*, dname FROM emp, dept)
-                        */
-                       attrname = strVal(lfirst(att->attrs));
-                       if (att->attrs != NIL && !strcmp(attrname, "*"))
-                       {
-
-                           /*
-                            * tail_p_target is the target list we're
-                            * building in the while loop. Make sure we
-                            * fix it after appending more nodes.
-                            */
-                           if (tail_p_target == NIL)
-                               p_target = tail_p_target = expandAll(pstate, att->relname,
-                                   att->relname, &pstate->p_last_resno);
-                           else
-                               lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname,
-                                                 &pstate->p_last_resno);
-                           expand_star = true;
-                       }
-                   }
-                   if (expand_star)
-                   {
-                       while (lnext(tail_p_target) != NIL)
-                           /* make sure we point to the last target entry */
-                           tail_p_target = lnext(tail_p_target);
-
-                       /*
-                        * skip rest of while loop
-                        */
-                       targetlist = lnext(targetlist);
-                       continue;
-                   }
-                   else
-                   {
-                       tent = MakeTargetEntryAttr(pstate, res);
-                       break;
-                   }
-               }
-           default:
-               /* internal error */
-               elog(ERROR, "Unable to transform targetlist (internal error)");
-               break;
-       }
-
-       if (p_target == NIL)
-           p_target = tail_p_target = lcons(tent, NIL);
-       else
+       if (type_id != InvalidOid)
        {
-           lnext(tail_p_target) = lcons(tent, NIL);
-           tail_p_target = lnext(tail_p_target);
+           if (type_id != attrtype)
+           {
+               tle->expr = CoerceTargetExpr(pstate, tle->expr,
+                                            type_id, attrtype);
+               if (tle->expr == NULL)
+                   elog(ERROR, "Attribute '%s' is of type '%s'"
+                        " but expression is of type '%s'"
+                        "\n\tYou will need to rewrite or cast the expression",
+                        colname,
+                        typeidTypeName(attrtype),
+                        typeidTypeName(type_id));
+           }
+           /*
+            * If the target is a fixed-length type, it may need a length
+            * coercion as well as a type coercion.
+            */
+           if (attrtypmod > 0 &&
+               attrtypmod != exprTypmod(tle->expr))
+               tle->expr = SizeTargetExpr(pstate, tle->expr,
+                                          attrtype, attrtypmod);
        }
-       targetlist = lnext(targetlist);
    }
 
-   return p_target;
-}  /* transformTargetList() */
+   /*
+    * The result of the target expression should now match the destination
+    * column's type.  Also, reset the resname and resno to identify
+    * the destination column --- rewriter and planner depend on that!
+    */
+   resnode->restype = attrtype;
+   resnode->restypmod = attrtypmod;
+   resnode->resname = colname;
+   resnode->resno = (AttrNumber) resdomno;
+}
 
 
 Node *
@@ -689,12 +297,19 @@ CoerceTargetExpr(ParseState *pstate,
        expr = NULL;
 
    return expr;
-}  /* CoerceTargetExpr() */
+}
 
 
-/* SizeTargetExpr()
- * Apparently going to a fixed-length string?
- * Then explicitly size for storage...
+/*
+ * SizeTargetExpr()
+ *
+ * If the target column type possesses a function named for the type
+ * and having parameter signature (columntype, int4), we assume that
+ * the type requires coercion to its own length and that the said
+ * function should be invoked to do that.
+ *
+ * Currently, "bpchar" (ie, char(N)) is the only such type, but try
+ * to be more general than a hard-wired test...
  */
 static Node *
 SizeTargetExpr(ParseState *pstate,
@@ -702,13 +317,10 @@ SizeTargetExpr(ParseState *pstate,
               Oid attrtype,
               int32 attrtypmod)
 {
-   int         i;
-   HeapTuple   ftup;
    char       *funcname;
    Oid         oid_array[MAXFARGS];
-
-   FuncCall   *func;
-   A_Const    *cons;
+   HeapTuple   ftup;
+   int         i;
 
    funcname = typeidTypeName(attrtype);
    oid_array[0] = attrtype;
@@ -725,6 +337,9 @@ SizeTargetExpr(ParseState *pstate,
 
    if (HeapTupleIsValid(ftup))
    {
+       FuncCall   *func;
+       A_Const    *cons;
+
        func = makeNode(FuncCall);
        func->funcname = funcname;
 
@@ -737,29 +352,27 @@ SizeTargetExpr(ParseState *pstate,
    }
 
    return expr;
-}  /* SizeTargetExpr() */
+}
 
 
 /*
  * makeTargetNames -
  *   generate a list of column names if not supplied or
- *   test supplied column names to make sure they are in target table
+ *   test supplied column names to make sure they are in target table.
  *   (used exclusively for inserts)
  */
 List *
 makeTargetNames(ParseState *pstate, List *cols)
 {
-   List       *tl = NULL;
-
-   /* Generate ResTarget if not supplied */
-
    if (cols == NIL)
    {
-       int         numcol;
-       int         i;
+       /*
+        * Generate default column list for INSERT.
+        */
        Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
+       int         numcol = pstate->p_target_relation->rd_rel->relnatts;
+       int         i;
 
-       numcol = pstate->p_target_relation->rd_rel->relnatts;
        for (i = 0; i < numcol; i++)
        {
            Ident      *id = makeNode(Ident);
@@ -768,27 +381,30 @@ makeTargetNames(ParseState *pstate, List *cols)
            StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN);
            id->indirection = NIL;
            id->isRel = false;
-           if (tl == NIL)
-               cols = tl = lcons(id, NIL);
-           else
-           {
-               lnext(tl) = lcons(id, NIL);
-               tl = lnext(tl);
-           }
+           cols = lappend(cols, id);
        }
    }
    else
    {
+       /*
+        * Do initial validation of user-supplied INSERT column list.
+        */
+       List       *tl;
+
        foreach(tl, cols)
        {
-           List       *nxt;
            char       *name = ((Ident *) lfirst(tl))->name;
+           List       *nxt;
 
-           /* elog on failure */
+           /* Lookup column name, elog on failure */
            attnameAttNum(pstate->p_target_relation, name);
+           /* Check for duplicates */
            foreach(nxt, lnext(tl))
-               if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
-               elog(ERROR, "Attribute '%s' should be specified only once", name);
+           {
+               if (strcmp(name, ((Ident *) lfirst(nxt))->name) == 0)
+                   elog(ERROR, "Attribute '%s' specified more than once",
+                        name);
+           }
        }
    }
 
@@ -804,57 +420,37 @@ static List *
 ExpandAllTables(ParseState *pstate)
 {
    List       *target = NIL;
-   List       *legit_rtable = NIL;
    List       *rt,
               *rtable;
 
    rtable = pstate->p_rtable;
    if (pstate->p_is_rule)
    {
-
        /*
         * skip first two entries, "*new*" and "*current*"
         */
-       rtable = lnext(lnext(pstate->p_rtable));
+       rtable = lnext(lnext(rtable));
    }
 
    /* SELECT *; */
-   if (rtable == NULL)
+   if (rtable == NIL)
        elog(ERROR, "Wildcard with no tables specified.");
 
-   /*
-    * go through the range table and make a list of range table entries
-    * which we will expand.
-    */
    foreach(rt, rtable)
    {
        RangeTblEntry *rte = lfirst(rt);
 
        /*
-        * we only expand those specify in the from clause. (This will
+        * we only expand those listed in the from clause. (This will
         * also prevent us from using the wrong table in inserts: eg.
         * tenk2 in "insert into tenk2 select * from tenk1;")
         */
        if (!rte->inFromCl)
            continue;
-       legit_rtable = lappend(legit_rtable, rte);
-   }
-
-   foreach(rt, legit_rtable)
-   {
-       RangeTblEntry *rte = lfirst(rt);
-       List       *temp = target;
 
-       if (temp == NIL)
-           target = expandAll(pstate, rte->relname, rte->refname,
-                              &pstate->p_last_resno);
-       else
-       {
-           while (temp != NIL && lnext(temp) != NIL)
-               temp = lnext(temp);
-           lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
-                                   &pstate->p_last_resno);
-       }
+       target = nconc(target,
+                      expandAll(pstate, rte->relname, rte->refname,
+                                &pstate->p_last_resno));
    }
    return target;
 }
@@ -862,29 +458,47 @@ ExpandAllTables(ParseState *pstate)
 /*
  * FigureColname -
  *   if the name of the resulting column is not specified in the target
- *   list, we have to guess.
+ *   list, we have to guess a suitable name.  The SQL spec provides some
+ *   guidance, but not much...
  *
  */
 static char *
 FigureColname(Node *expr, Node *resval)
 {
-   switch (nodeTag(expr))
+   /* Some of these are easiest to do with the untransformed node */
+   switch (nodeTag(resval))
    {
-           case T_Aggref:
-           return (char *) ((Aggref *) expr)->aggname;
-       case T_Expr:
-           if (((Expr *) expr)->opType == FUNC_EXPR)
+       case T_Ident:
+           return ((Ident *) resval)->name;
+       case T_Attr:
            {
-               if (nodeTag(resval) == T_FuncCall)
-                   return ((FuncCall *) resval)->funcname;
+               List       *attrs = ((Attr *) resval)->attrs;
+               if (attrs)
+               {
+                   while (lnext(attrs) != NIL)
+                       attrs = lnext(attrs);
+                   return strVal(lfirst(attrs));
+               }
            }
            break;
+       default:
+           break;
+   }
+   /* Otherwise, work with the transformed node */
+   switch (nodeTag(expr))
+   {
+       case T_Expr:
+           if (((Expr *) expr)->opType == FUNC_EXPR && IsA(resval, FuncCall))
+               return ((FuncCall *) resval)->funcname;
+           break;
+       case T_Aggref:
+           return ((Aggref *) expr)->aggname;
        case T_CaseExpr:
            {
                char       *name;
 
                name = FigureColname(((CaseExpr *) expr)->defresult, resval);
-               if (!strcmp(name, "?column?"))
+               if (strcmp(name, "?column?") == 0)
                    name = "case";
                return name;
            }
index a50ab4e039146f834ebcfa93b6a4f74045428b39..3b1aafa07d923e709a14b6aaa917d7cb78a7ec47 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.11 1999/07/15 23:04:01 momjian Exp $
+ * $Id: parse_clause.h,v 1.12 1999/07/19 00:26:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "parser/parse_node.h"
 
-extern void makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual);
-extern Node *transformWhereClause(ParseState *pstate, Node *where, Node *using);
+extern void makeRangeTable(ParseState *pstate, List *frmList, Node **qual);
+extern void setTargetTable(ParseState *pstate, char *relname);
+extern Node *transformWhereClause(ParseState *pstate, Node *where,
+                                 Node *using);
 extern List *transformGroupClause(ParseState *pstate, List *grouplist,
                     List *targetlist);
 extern List *transformSortClause(ParseState *pstate,
index cbc4095e330fa3f5401aa2dac18387c88b4c0f4c..3bd4d3fb47c6b53e3b7bb57d23288a61ce7c94a8 100644 (file)
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
- * parse_exer.h
+ * parse_expr.h
  *
  *
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_expr.h,v 1.13 1999/07/15 23:04:02 momjian Exp $
+ * $Id: parse_expr.h,v 1.14 1999/07/19 00:26:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parse_node.h"
 #include "parser/parse_type.h"
 
+#define EXPR_COLUMN_FIRST  1
+#define EXPR_RELATION_FIRST 2
+
 extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
 extern Oid exprType(Node *expr);
+extern int32 exprTypmod(Node *expr);
 extern Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int32 attypmod);
 
 #endif  /* PARSE_EXPR_H */
index 3dd2257db5ef4760db520617d012d6c40e88692d..1591743e0646acf1e2c31e53e12579d1a1b935bd 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_node.h,v 1.14 1999/07/15 23:04:02 momjian Exp $
+ * $Id: parse_node.h,v 1.15 1999/07/19 00:26:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@ typedef struct ParseState
 {
    int         p_last_resno;
    List       *p_rtable;
-   List       *p_insert_columns;
    struct ParseState *parentParseState;
    bool        p_hasAggs;
    bool        p_hasSubLinks;
@@ -36,12 +35,11 @@ extern ParseState *make_parsestate(ParseState *parentParseState);
 extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
 extern Var *make_var(ParseState *pstate, Oid relid, char *refname,
         char *attrname);
-extern ArrayRef *make_array_ref(Node *expr,
-              List *indirection);
-extern ArrayRef *make_array_set(Expr *target_expr,
-              List *upperIndexpr,
-              List *lowerIndexpr,
-              Expr *expr);
+extern ArrayRef *transformArraySubscripts(ParseState *pstate,
+                                         Node *arrayBase,
+                                         List *indirection,
+                                         bool forceSlice,
+                                         Node *assignFrom);
 extern Const *make_const(Value *value);
 
 #endif  /* PARSE_NODE_H */
index e44f89c79baa4531eda67b1dddd57a5aa878ca73..68e5ac7bf1757b08713a03e64925592f51a2489f 100644 (file)
@@ -1,17 +1,17 @@
 /*-------------------------------------------------------------------------
  *
- * parse_query.h
- *   prototypes for parse_query.c.
+ * parse_relation.h
+ *   prototypes for parse_relation.c.
  *
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.11 1999/07/15 23:04:03 momjian Exp $
+ * $Id: parse_relation.h,v 1.12 1999/07/19 00:26:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#ifndef PARSE_QUERY_H
-#define PARSE_RANGE_H
+#ifndef PARSE_RELATION_H
+#define PARSE_RELATION_H
 
 #include "parser/parse_node.h"
 
@@ -30,7 +30,5 @@ extern int    attnameAttNum(Relation rd, char *a);
 extern bool attnameIsSet(Relation rd, char *name);
 extern int attnumAttNelems(Relation rd, int attid);
 extern Oid attnumTypeId(Relation rd, int attid);
-extern void handleTargetColname(ParseState *pstate, char **resname,
-                   char *refname, char *colname);
 
-#endif  /* PARSE_RANGE_H */
+#endif  /* PARSE_RELATION_H */
index bd520d1bab94fd7c264357e8043dd8f6e0284a0d..c2babecb769d9e1b9b257fbc58420c157132db00 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_target.h,v 1.14 1999/07/15 23:04:03 momjian Exp $
+ * $Id: parse_target.h,v 1.15 1999/07/19 00:26:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "parser/parse_node.h"
 
-#define EXPR_COLUMN_FIRST  1
-#define EXPR_RELATION_FIRST 2
-
 extern List *transformTargetList(ParseState *pstate, List *targetlist);
-extern List *makeTargetNames(ParseState *pstate, List *cols);
-extern TargetEntry *MakeTargetEntryIdent(ParseState *pstate,
-                    Node *node,
-                    char **resname,
-                    char *refname,
-                    char *colname,
-                    bool resjunk);
+extern TargetEntry *transformTargetEntry(ParseState *pstate,
+                                        Node *node, Node *expr,
+                                        char *colname, bool resjunk);
+extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
+                                 char *colname, List *indirection);
 extern Node *CoerceTargetExpr(ParseState *pstate, Node *expr,
-                Oid type_id, Oid attrtype);
-TargetEntry *MakeTargetEntryExpr(ParseState *pstate,
-                   char *colname,
-                   Node *expr,
-                   List *arrayRef,
-                   bool resjunk);
+                             Oid type_id, Oid attrtype);
+extern List *makeTargetNames(ParseState *pstate, List *cols);
 
 #endif  /* PARSE_TARGET_H */