OSDN Git Service

Reimplement parsing and storage of default expressions and constraint
[pg-rex/syncrep.git] / src / backend / parser / analyze.c
index 68b38f1..4bcf79a 100644 (file)
@@ -5,27 +5,22 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: analyze.c,v 1.113 1999/07/15 22:39:33 momjian Exp $
+ *     $Id: analyze.c,v 1.120 1999/10/03 23:55:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-
 #include "postgres.h"
+
 #include "access/heapam.h"
+#include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
+#include "parse.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
-#include "catalog/pg_type.h"
-#include "parse.h"
-
 #include "utils/builtins.h"
 
 static Query *transformStmt(ParseState *pstate, Node *stmt);
@@ -42,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 -
@@ -62,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);
+                       heap_close(pstate->p_target_relation, AccessShareLock);
+               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);
+                               heap_close(pstate->p_target_relation, AccessShareLock);
+                       pstate->p_target_relation = NULL;
+                       pstate->p_target_rangetblentry = NULL;
                        extras_before = lnext(extras_before);
                }
 
@@ -83,12 +86,14 @@ parse_analyze(List *pl, ParseState *parentParseState)
                        result = lappend(result,
                                                         transformStmt(pstate, lfirst(extras_after)));
                        if (pstate->p_target_relation != NULL)
-                               heap_close(pstate->p_target_relation);
+                               heap_close(pstate->p_target_relation, AccessShareLock);
+                       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;
@@ -152,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;
@@ -219,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;
 
@@ -244,135 +250,69 @@ 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;
-               }
-
-               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);
+       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;
-
-       /* 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;
 
@@ -384,15 +324,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.)
         */
-       if ((qry->hasAggs == false) && (qry->havingQual != NULL))
+       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)
        {
-               elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-               return (Query *) NIL;
+               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?
+        */
+       rd_att = pstate->p_target_relation->rd_att;
+       if (rd_att->constr && rd_att->constr->num_defval > 0)
+       {
+               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)          /* found TLE 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)
@@ -550,12 +573,14 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
        q = makeNode(Query);
        q->commandType = CMD_UTILITY;
 
-       elements = stmt->tableElts;
        constraints = stmt->constraints;
        columns = NIL;
        dlist = NIL;
 
-       while (elements != NIL)
+       /*
+        * Run through each primary element in the table creation clause
+        */
+       foreach(elements, stmt->tableElts)
        {
                element = lfirst(elements);
                switch (nodeTag(element))
@@ -564,22 +589,35 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                column = (ColumnDef *) element;
                                columns = lappend(columns, column);
 
+                               /* Special case SERIAL type? */
                                if (column->is_sequence)
                                {
                                        char       *sname;
-                                       char       *cstring;
+                                       char       *qstring;
+                                       A_Const    *snamenode;
+                                       FuncCall   *funccallnode;
                                        CreateSeqStmt *sequence;
 
                                        sname = makeObjectName(stmt->relname, column->colname,
                                                                                   "seq");
+                                       /*
+                                        * Create an expression tree representing the function
+                                        * call  nextval('"sequencename"')
+                                        */
+                                       qstring = palloc(strlen(sname) + 2 + 1);
+                                       sprintf(qstring, "\"%s\"", sname);
+                                       snamenode = makeNode(A_Const);
+                                       snamenode->val.type = T_String;
+                                       snamenode->val.val.str = qstring;
+                                       funccallnode = makeNode(FuncCall);
+                                       funccallnode->funcname = "nextval";
+                                       funccallnode->args = lcons(snamenode, NIL);
+
                                        constraint = makeNode(Constraint);
                                        constraint->contype = CONSTR_DEFAULT;
                                        constraint->name = sname;
-                                       cstring = palloc(10 + strlen(constraint->name) + 3 + 1);
-                                       strcpy(cstring, "nextval('\"");
-                                       strcat(cstring, constraint->name);
-                                       strcat(cstring, "\"')");
-                                       constraint->def = cstring;
+                                       constraint->raw_expr = (Node *) funccallnode;
+                                       constraint->cooked_expr = NULL;
                                        constraint->keys = NULL;
 
                                        column->constraints = lappend(column->constraints, constraint);
@@ -601,68 +639,65 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                        blist = lcons(sequence, NIL);
                                }
 
-                               if (column->constraints != NIL)
+                               /* Process column constraints, if any... */
+                               foreach(clist, column->constraints)
                                {
-                                       clist = column->constraints;
-                                       while (clist != NIL)
+                                       constraint = lfirst(clist);
+                                       switch (constraint->contype)
                                        {
-                                               constraint = lfirst(clist);
-                                               switch (constraint->contype)
-                                               {
-                                                       case CONSTR_NULL:
-
-                                                               /*
-                                                                * We should mark this explicitly, so we
-                                                                * can tell if NULL and NOT NULL are both
-                                                                * specified
-                                                                */
-                                                               if (column->is_not_null)
-                                                                       elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
-                                                                                " for '%s.%s'", stmt->relname, column->colname);
-                                                               column->is_not_null = FALSE;
-                                                               break;
-
-                                                       case CONSTR_NOTNULL:
-                                                               if (column->is_not_null)
-                                                                       elog(ERROR, "CREATE TABLE/NOT NULL already specified"
-                                                                                " for '%s.%s'", stmt->relname, column->colname);
-                                                               column->is_not_null = TRUE;
-                                                               break;
-
-                                                       case CONSTR_DEFAULT:
-                                                               if (column->defval != NULL)
-                                                                       elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
-                                                                                " for '%s.%s'", stmt->relname, column->colname);
-                                                               column->defval = constraint->def;
-                                                               break;
-
-                                                       case CONSTR_PRIMARY:
-                                                               if (constraint->name == NULL)
-                                                                       constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
-                                                               if (constraint->keys == NIL)
-                                                                       constraint->keys = lappend(constraint->keys, column);
-                                                               dlist = lappend(dlist, constraint);
-                                                               break;
-
-                                                       case CONSTR_UNIQUE:
-                                                               if (constraint->name == NULL)
-                                                                       constraint->name = makeObjectName(stmt->relname, column->colname, "key");
-                                                               if (constraint->keys == NIL)
-                                                                       constraint->keys = lappend(constraint->keys, column);
-                                                               dlist = lappend(dlist, constraint);
-                                                               break;
-
-                                                       case CONSTR_CHECK:
-                                                               constraints = lappend(constraints, constraint);
-                                                               if (constraint->name == NULL)
-                                                                       constraint->name = makeObjectName(stmt->relname, column->colname, NULL);
-                                                               break;
-
-                                                       default:
-                                                               elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
-                                                               break;
-                                               }
-                                               clist = lnext(clist);
+                                               case CONSTR_NULL:
+
+                                                       /*
+                                                        * We should mark this explicitly, so we
+                                                        * can tell if NULL and NOT NULL are both
+                                                        * specified
+                                                        */
+                                                       if (column->is_not_null)
+                                                               elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
+                                                                        " for '%s.%s'", stmt->relname, column->colname);
+                                                       column->is_not_null = FALSE;
+                                                       break;
+
+                                               case CONSTR_NOTNULL:
+                                                       if (column->is_not_null)
+                                                               elog(ERROR, "CREATE TABLE/NOT NULL already specified"
+                                                                        " for '%s.%s'", stmt->relname, column->colname);
+                                                       column->is_not_null = TRUE;
+                                                       break;
+
+                                               case CONSTR_DEFAULT:
+                                                       if (column->raw_default != NULL)
+                                                               elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
+                                                                        " for '%s.%s'", stmt->relname, column->colname);
+                                                       column->raw_default = constraint->raw_expr;
+                                                       Assert(constraint->cooked_expr == NULL);
+                                                       break;
+
+                                               case CONSTR_PRIMARY:
+                                                       if (constraint->name == NULL)
+                                                               constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
+                                                       if (constraint->keys == NIL)
+                                                               constraint->keys = lappend(constraint->keys, column);
+                                                       dlist = lappend(dlist, constraint);
+                                                       break;
+
+                                               case CONSTR_UNIQUE:
+                                                       if (constraint->name == NULL)
+                                                               constraint->name = makeObjectName(stmt->relname, column->colname, "key");
+                                                       if (constraint->keys == NIL)
+                                                               constraint->keys = lappend(constraint->keys, column);
+                                                       dlist = lappend(dlist, constraint);
+                                                       break;
+
+                                               case CONSTR_CHECK:
+                                                       if (constraint->name == NULL)
+                                                               constraint->name = makeObjectName(stmt->relname, column->colname, NULL);
+                                                       constraints = lappend(constraints, constraint);
+                                                       break;
+
+                                               default:
+                                                       elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
+                                                       break;
                                        }
                                }
                                break;
@@ -687,19 +722,17 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 
                                        case CONSTR_NOTNULL:
                                        case CONSTR_DEFAULT:
-                                               elog(ERROR, "parser: illegal context for constraint (internal error)", NULL);
+                                               elog(ERROR, "parser: illegal context for constraint (internal error)");
                                                break;
                                        default:
-                                               elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
+                                               elog(ERROR, "parser: unrecognized constraint (internal error)");
                                                break;
                                }
                                break;
 
                        default:
-                               elog(ERROR, "parser: unrecognized node (internal error)", NULL);
+                               elog(ERROR, "parser: unrecognized node (internal error)");
                }
-
-               elements = lnext(elements);
        }
 
        stmt->tableElts = columns;
@@ -791,28 +824,43 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
  * or if a SERIAL column was defined along with a table PRIMARY KEY constraint.
  * - thomas 1999-05-11
  */
-       if ((pkey != NULL) && (length(lfirst(pkey->indexParams)) == 1))
+       if (pkey != NULL)
        {
                dlist = ilist;
                ilist = NIL;
                while (dlist != NIL)
                {
-                       int                     keep = TRUE;
+                       List *pcols, *icols;
+                       int plen, ilen;
+                       int     keep = TRUE;
 
                        index = lfirst(dlist);
+                       pcols = pkey->indexParams;
+                       icols = index->indexParams;
 
-                       /*
-                        * has a single column argument, so might be a conflicting
-                        * index...
-                        */
-                       if ((index != pkey)
-                               && (length(index->indexParams) == 1))
+                       plen = length(pcols);
+                       ilen = length(icols);
+
+                       /* Not the same as the primary key? Then we should look... */
+                       if ((index != pkey) && (ilen == plen))
                        {
-                               char       *pname = ((IndexElem *) lfirst(index->indexParams))->name;
-                               char       *iname = ((IndexElem *) lfirst(index->indexParams))->name;
+                               keep = FALSE;
+                               while ((pcols != NIL) && (icols != NIL))
+                               {
+                                       IndexElem *pcol = lfirst(pcols);
+                                       IndexElem *icol = lfirst(icols);
+                                       char *pname = pcol->name;
+                                       char *iname = icol->name;
 
-                               /* same names? then don't keep... */
-                               keep = (strcmp(iname, pname) != 0);
+                                       /* different names? then no match... */
+                                       if (strcmp(iname, pname) != 0)
+                                       {
+                                               keep = TRUE;
+                                               break;
+                                       }
+                                       pcols = lnext(pcols);
+                                       icols = lnext(icols);
+                               }
                        }
 
                        if (keep)
@@ -967,12 +1015,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;
 
@@ -982,33 +1030,37 @@ 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,
-                                                                                 NIL,
                                                                                  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
         */
@@ -1021,17 +1073,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);
@@ -1048,6 +1090,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;
@@ -1056,21 +1100,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;
 }