OSDN Git Service

Reimplement parsing and storage of default expressions and constraint
[pg-rex/syncrep.git] / src / backend / parser / analyze.c
index 9f61e83..4bcf79a 100644 (file)
@@ -1,38 +1,27 @@
 /*-------------------------------------------------------------------------
  *
- * analyze.c--
+ * analyze.c
  *       transform the parse tree into a query tree
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *  $Id: analyze.c,v 1.94 1999/01/21 22:48:07 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 "nodes/memnodes.h"
-#include "nodes/pg_list.h"
+#include "parse.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_clause.h"
-#include "parser/parse_node.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
-/***S*I***/
-#include "parser/parse_expr.h"
-#include "catalog/pg_type.h"
-#include "parse.h"
-
 #include "utils/builtins.h"
-#include "utils/mcxt.h"
 
 static Query *transformStmt(ParseState *pstate, Node *stmt);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
@@ -45,10 +34,13 @@ static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
 static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
 
-static void   transformForUpdate(Query *qry, List *forUpdate);
+static void transformForUpdate(Query *qry, List *forUpdate);
+void           CheckSelectForUpdate(Query *qry);
+
+/* kluge to return extra info from transformCreateStmt() */
+static List       *extras_before;
+static List       *extras_after;
 
-List      *extras_before = NIL;
-List      *extras_after = NIL;
 
 /*
  * parse_analyze -
@@ -58,57 +50,50 @@ List           *extras_after = NIL;
  * all transformed to Query while the rest stays the same.
  *
  */
-QueryTreeList *
+List *
 parse_analyze(List *pl, ParseState *parentParseState)
 {
-       QueryTreeList *result;
+       List       *result = NIL;
        ParseState *pstate;
-       Query *parsetree;
-       int                     i = 0;
-
-       result = malloc(sizeof(QueryTreeList));
-       result->len = length(pl);
-       result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
+       Query      *parsetree;
 
        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;
 
-               if (extras_before != NIL)
+               while (extras_before != NIL)
                {
-                       result->len += length(extras_before);
-                       result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
-                       while (extras_before != NIL)
-                       {
-                               result->qtrees[i++] = transformStmt(pstate, lfirst(extras_before));
-                               if (pstate->p_target_relation != NULL)
-                                       heap_close(pstate->p_target_relation);
-                               extras_before = lnext(extras_before);
-                       }
+                       result = lappend(result,
+                                                        transformStmt(pstate, lfirst(extras_before)));
+                       if (pstate->p_target_relation != NULL)
+                               heap_close(pstate->p_target_relation, AccessShareLock);
+                       pstate->p_target_relation = NULL;
+                       pstate->p_target_rangetblentry = NULL;
+                       extras_before = lnext(extras_before);
                }
-               extras_before = NIL;
 
-               result->qtrees[i++] = parsetree;
+               result = lappend(result, parsetree);
 
-               if (extras_after != NIL)
+               while (extras_after != NIL)
                {
-                       result->len += length(extras_after);
-                       result->qtrees = (Query **) realloc(result->qtrees, result->len * sizeof(Query *));
-                       while (extras_after != NIL)
-                       {
-                               result->qtrees[i++] = transformStmt(pstate, lfirst(extras_after));
-                               if (pstate->p_target_relation != NULL)
-                                       heap_close(pstate->p_target_relation);
-                               extras_after = lnext(extras_after);
-                       }
+                       result = lappend(result,
+                                                        transformStmt(pstate, lfirst(extras_after)));
+                       if (pstate->p_target_relation != NULL)
+                               heap_close(pstate->p_target_relation, AccessShareLock);
+                       pstate->p_target_relation = NULL;
+                       pstate->p_target_rangetblentry = NULL;
+                       extras_after = lnext(extras_after);
                }
-               extras_after = NIL;
 
-               pl = lnext(pl);
                pfree(pstate);
+               pl = lnext(pl);
        }
 
        return result;
@@ -172,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;
@@ -204,7 +189,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 
                case T_SelectStmt:
                        if (!((SelectStmt *) parseTree)->portalname)
+                       {
                                result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
+                               result->limitOffset = ((SelectStmt *) parseTree)->limitOffset;
+                               result->limitCount = ((SelectStmt *) parseTree)->limitCount;
+                       }
                        else
                                result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
                        break;
@@ -235,12 +224,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
        qry->commandType = CMD_DELETE;
 
        /* set up a range table */
-       makeRangeTable(pstate, stmt->relname, NULL);
+       makeRangeTable(pstate, NULL, NULL);
+       setTargetTable(pstate, stmt->relname);
 
        qry->uniqueFlag = NULL;
 
        /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
        qry->hasSubLinks = pstate->p_hasSubLinks;
 
        qry->rtable = pstate->p_rtable;
@@ -260,154 +250,172 @@ 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);
+       /*----------
+        * 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);
+       qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-                       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, 0),
-                                                         (Node *) stringToNode(defval[ndef].adbin));
-                       qry->targetList = lappend(qry->targetList, te);
-               }
-       }
-
-       /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
-
-       /*
-        * 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);
-
-       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->havingQual = transformWhereClause(pstate, stmt->havingClause, 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)
+       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
-,       */
-       qry->unionall = stmt->unionall; 
+        */
+       qry->unionall = stmt->unionall;
 
-       /***S*I***/
-       /* Just hand through the unionClause and intersectClause. 
-        * We will handle it in the function Except_Intersect_Rewrite() */
-       qry->unionClause = stmt->unionClause;
-       qry->intersectClause = stmt->intersectClause;   
+       /*
+        * Just hand through the unionClause and intersectClause. We will
+        * handle it in the function Except_Intersect_Rewrite()
+        */
+       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.
+        * 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)
@@ -417,39 +425,79 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 }
 
 /*
- *     makeTableName()
- *     Create a table name from a list of fields.
+ *     makeObjectName()
+ *
+ *     Create a name for an implicitly created index, sequence, constraint, etc.
+ *
+ *     The parameters are: the original table name, the original field name, and
+ *     a "type" string (such as "seq" or "pkey").  The field name and/or type
+ *     can be NULL if not relevant.
+ *
+ *     The result is a palloc'd string.
+ *
+ *     The basic result we want is "name1_name2_type", omitting "_name2" or
+ *     "_type" when those parameters are NULL.  However, we must generate
+ *     a name with less than NAMEDATALEN characters!  So, we truncate one or
+ *     both names if necessary to make a short-enough string.  The type part
+ *     is never truncated (so it had better be reasonably short).
+ *
+ *     To reduce the probability of collisions, we might someday add more
+ *     smarts to this routine, like including some "hash" characters computed
+ *     from the truncated characters.  Currently it seems best to keep it simple,
+ *     so that the generated names are easily predictable by a person.
  */
 static char *
-makeTableName(void *elem,...)
+makeObjectName(char *name1, char *name2, char *typename)
 {
-       va_list         args;
-
        char       *name;
-       char            buf[NAMEDATALEN + 1];
-
-       buf[0] = '\0';
-
-       va_start(args, elem);
-
-       name = elem;
-       while (name != NULL)
+       int                     overhead = 0;   /* chars needed for type and underscores */
+       int                     availchars;             /* chars available for name(s) */
+       int                     name1chars;             /* chars allocated to name1 */
+       int                     name2chars;             /* chars allocated to name2 */
+       int                     ndx;
+
+       name1chars = strlen(name1);
+       if (name2)
        {
-               /* not enough room for next part? then return nothing */
-               if ((strlen(buf) + strlen(name)) >= (sizeof(buf) - 1))
-                       return NULL;
+               name2chars = strlen(name2);
+               overhead++;                             /* allow for separating underscore */
+       }
+       else
+               name2chars = 0;
+       if (typename)
+               overhead += strlen(typename) + 1;
 
-               if (strlen(buf) > 0)
-                       strcat(buf, "_");
-               strcat(buf, name);
+       availchars = NAMEDATALEN-1 - overhead;
 
-               name = va_arg(args, void *);
+       /* If we must truncate,  preferentially truncate the longer name.
+        * This logic could be expressed without a loop, but it's simple and
+        * obvious as a loop.
+        */
+       while (name1chars + name2chars > availchars)
+       {
+               if (name1chars > name2chars)
+                       name1chars--;
+               else
+                       name2chars--;
        }
 
-       va_end(args);
-
-       name = palloc(strlen(buf) + 1);
-       strcpy(name, buf);
+       /* Now construct the string using the chosen lengths */
+       name = palloc(name1chars + name2chars + overhead + 1);
+       strncpy(name, name1, name1chars);
+       ndx = name1chars;
+       if (name2)
+       {
+               name[ndx++] = '_';
+               strncpy(name+ndx, name2, name2chars);
+               ndx += name2chars;
+       }
+       if (typename)
+       {
+               name[ndx++] = '_';
+               strcpy(name+ndx, typename);
+       }
+       else
+               name[ndx] = '\0';
 
        return name;
 }
@@ -460,26 +508,24 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
        int                     pass = 0;
        char       *iname = NULL;
        List       *ilist;
-       IndexStmt  *index;
-       char            name2[NAMEDATALEN + 1];
+       char            typename[NAMEDATALEN];
 
-       /* use working storage, since we might be trying several possibilities */
-       strcpy(name2, column_name);
-       while (iname == NULL)
+       /* The type name for makeObjectName is label, or labelN if that's
+        * necessary to prevent collisions among multiple indexes for the same
+        * table.  Note there is no check for collisions with already-existing
+        * indexes; this ought to be rethought someday.
+        */
+       strcpy(typename, label);
+
+       for (;;)
        {
-               iname = makeTableName(table_name, name2, label, NULL);
-               /* unable to make a name at all? then quit */
-               if (iname == NULL)
-                       break;
+               iname = makeObjectName(table_name, column_name, typename);
 
-               ilist = indices;
-               while (ilist != NIL)
+               foreach(ilist, indices)
                {
-                       index = lfirst(ilist);
+                       IndexStmt  *index = lfirst(ilist);
                        if (strcasecmp(iname, index->idxname) == 0)
                                break;
-
-                       ilist = lnext(ilist);
                }
                /* ran through entire list? then no name conflict found so done */
                if (ilist == NIL)
@@ -487,9 +533,7 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
 
                /* the last one conflicted, so try a new name component */
                pfree(iname);
-               iname = NULL;
-               pass++;
-               sprintf(name2, "%s_%d", column_name, (pass + 1));
+               sprintf(typename, "%s%d", label, ++pass);
        }
 
        return iname;
@@ -508,7 +552,6 @@ static Query *
 transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 {
        Query      *q;
-       int                     have_pkey = FALSE;
        List       *elements;
        Node       *element;
        List       *columns;
@@ -519,20 +562,25 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
        Constraint *constraint;
        List       *keys;
        Ident      *key;
-       List       *blist = NIL;
-       List       *ilist = NIL;
-       IndexStmt  *index;
+       List       *blist = NIL;        /* "before list" of things to do before
+                                                                * creating the table */
+       List       *ilist = NIL;        /* "index list" of things to do after
+                                                                * creating the table */
+       IndexStmt  *index,
+                          *pkey = NULL;
        IndexElem  *iparam;
 
        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))
@@ -541,103 +589,115 @@ 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       *sname;
+                                       char       *qstring;
+                                       A_Const    *snamenode;
+                                       FuncCall   *funccallnode;
                                        CreateSeqStmt *sequence;
 
-                                       sname = makeTableName(stmt->relname, column->colname, "seq", NULL);
+                                       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(9 + strlen(constraint->name) + 2 + 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;
 
-                                       /* The parser only allows PRIMARY KEY as a constraint for the SERIAL type.
-                                        * So, if there is a constraint of any kind, assume it is that.
-                                        * If PRIMARY KEY is specified, then don't need to gin up a UNIQUE constraint
-                                        * since that will be covered already.
-                                        * - thomas 1998-09-15
-                                        */
-                                       if (column->constraints != NIL)
-                                       {
-                                               column->constraints = lappend(column->constraints, constraint);
-                                       }
-                                       else
-                                       {
-                                               column->constraints = lcons(constraint, NIL);
+                                       column->constraints = lappend(column->constraints, constraint);
 
-                                               constraint = makeNode(Constraint);
-                                               constraint->contype = CONSTR_UNIQUE;
-                                               constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
-                                               column->constraints = lappend(column->constraints, constraint);
-                                       }
+                                       constraint = makeNode(Constraint);
+                                       constraint->contype = CONSTR_UNIQUE;
+                                       constraint->name = makeObjectName(stmt->relname,
+                                                                                                         column->colname,
+                                                                                                         "key");
+                                       column->constraints = lappend(column->constraints, constraint);
 
                                        sequence = makeNode(CreateSeqStmt);
                                        sequence->seqname = pstrdup(sname);
                                        sequence->options = NIL;
 
-                                       elog(NOTICE, "CREATE TABLE will create implicit sequence %s for SERIAL column %s.%s",
+                                       elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
                                          sequence->seqname, stmt->relname, column->colname);
 
                                        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_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 = makeTableName(stmt->relname, "pkey", NULL);
-                                                               if (constraint->keys == NIL)
-                                                                       constraint->keys = lappend(constraint->keys, column);
-                                                               dlist = lappend(dlist, constraint);
-                                                               break;
-
-                                                       case CONSTR_UNIQUE:
-                                                               if (constraint->name == NULL)
-                                                                       constraint->name = makeTableName(stmt->relname, column->colname, "key", NULL);
-                                                               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 = makeTableName(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;
@@ -648,15 +708,11 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                                {
                                        case CONSTR_PRIMARY:
                                                if (constraint->name == NULL)
-                                                       constraint->name = makeTableName(stmt->relname, "pkey", NULL);
+                                                       constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
                                                dlist = lappend(dlist, constraint);
                                                break;
 
                                        case CONSTR_UNIQUE:
-#if FALSE
-                                               if (constraint->name == NULL)
-                                                       constraint->name = makeTableName(stmt->relname, "key", NULL);
-#endif
                                                dlist = lappend(dlist, constraint);
                                                break;
 
@@ -666,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;
@@ -690,45 +744,34 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
  *
  * Note that this code does not currently look for all possible redundant cases
  *     and either ignore or stop with warning. The create might fail later when
- *     names for indices turn out to be redundant, or a user might have specified
+ *     names for indices turn out to be duplicated, or a user might have specified
  *     extra useless indices which might hurt performance. - thomas 1997-12-08
  */
        while (dlist != NIL)
        {
                constraint = lfirst(dlist);
-               if (nodeTag(constraint) != T_Constraint)
-                       elog(ERROR, "parser: unrecognized deferred node (internal error)", NULL);
+               Assert(nodeTag(constraint) == T_Constraint);
+               Assert((constraint->contype == CONSTR_PRIMARY)
+                          || (constraint->contype == CONSTR_UNIQUE));
 
-               if (constraint->contype == CONSTR_PRIMARY)
+               index = makeNode(IndexStmt);
+
+               index->unique = TRUE;
+               index->primary = (constraint->contype == CONSTR_PRIMARY ? TRUE : FALSE);
+               if (index->primary)
                {
-                       if (have_pkey)
+                       if (pkey != NULL)
                                elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
-                                        " for table %s are not legal", stmt->relname);
-                       else
-                               have_pkey = TRUE;
+                                        " for table '%s' are not allowed", stmt->relname);
+                       pkey = (IndexStmt *) index;
                }
-               else if (constraint->contype != CONSTR_UNIQUE)
-                       elog(ERROR, "parser: unrecognized deferred constraint (internal error)", NULL);
 
-               index = makeNode(IndexStmt);
-
-               index->unique = TRUE;
                if (constraint->name != NULL)
-                       index->idxname = constraint->name;
+                       index->idxname = pstrdup(constraint->name);
                else if (constraint->contype == CONSTR_PRIMARY)
-               {
-                       if (have_pkey)
-                               elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple keys for table %s are not legal", stmt->relname);
-
-                       have_pkey = TRUE;
-                       index->primary = TRUE;
-                       index->idxname = makeTableName(stmt->relname, "pkey", NULL);
-               }
+                       index->idxname = makeObjectName(stmt->relname, NULL, "pkey");
                else
-               {
-                       index->primary = FALSE;
                        index->idxname = NULL;
-               }
 
                index->relname = stmt->relname;
                index->accessMethod = "btree";
@@ -757,7 +800,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                        if (constraint->contype == CONSTR_PRIMARY)
                                column->is_not_null = TRUE;
                        iparam = makeNode(IndexElem);
-                       iparam->name = strcpy(palloc(strlen(column->colname) + 1), column->colname);
+                       iparam->name = pstrdup(column->colname);
                        iparam->args = NIL;
                        iparam->class = NULL;
                        iparam->typename = NULL;
@@ -769,24 +812,79 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                        keys = lnext(keys);
                }
 
-               if (index->idxname == NULL)
-                       elog(ERROR, "CREATE TABLE unable to construct implicit index for table %s"
-                                "; name too long", stmt->relname);
-               else
-                       elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s",
-                                ((constraint->contype == CONSTR_PRIMARY) ? "PRIMARY KEY" : "UNIQUE"),
-                                index->idxname, stmt->relname);
+               if (index->idxname == NULL)     /* should not happen */
+                       elog(ERROR, "CREATE TABLE: failed to make implicit index name");
 
                ilist = lappend(ilist, index);
                dlist = lnext(dlist);
        }
 
+/* OK, now finally, if there is a primary key, then make sure that there aren't any redundant
+ * unique indices defined on columns. This can arise if someone specifies UNIQUE explicitly
+ * or if a SERIAL column was defined along with a table PRIMARY KEY constraint.
+ * - thomas 1999-05-11
+ */
+       if (pkey != NULL)
+       {
+               dlist = ilist;
+               ilist = NIL;
+               while (dlist != NIL)
+               {
+                       List *pcols, *icols;
+                       int plen, ilen;
+                       int     keep = TRUE;
+
+                       index = lfirst(dlist);
+                       pcols = pkey->indexParams;
+                       icols = index->indexParams;
+
+                       plen = length(pcols);
+                       ilen = length(icols);
+
+                       /* Not the same as the primary key? Then we should look... */
+                       if ((index != pkey) && (ilen == plen))
+                       {
+                               keep = FALSE;
+                               while ((pcols != NIL) && (icols != NIL))
+                               {
+                                       IndexElem *pcol = lfirst(pcols);
+                                       IndexElem *icol = lfirst(icols);
+                                       char *pname = pcol->name;
+                                       char *iname = icol->name;
+
+                                       /* different names? then no match... */
+                                       if (strcmp(iname, pname) != 0)
+                                       {
+                                               keep = TRUE;
+                                               break;
+                                       }
+                                       pcols = lnext(pcols);
+                                       icols = lnext(icols);
+                               }
+                       }
+
+                       if (keep)
+                               ilist = lappend(ilist, index);
+                       dlist = lnext(dlist);
+               }
+       }
+
+       dlist = ilist;
+       while (dlist != NIL)
+       {
+               index = lfirst(dlist);
+               elog(NOTICE, "CREATE TABLE/%s will create implicit index '%s' for table '%s'",
+                        (index->primary ? "PRIMARY KEY" : "UNIQUE"),
+                        index->idxname, stmt->relname);
+               dlist = lnext(dlist);
+       }
+
        q->utilityStmt = (Node *) stmt;
        extras_before = blist;
        extras_after = ilist;
 
        return q;
-}
+}      /* transformCreateStmt() */
 
 /*
  * transformIndexStmt -
@@ -801,7 +899,7 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
        qry->commandType = CMD_UTILITY;
 
        /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
        qry->hasSubLinks = pstate->p_hasSubLinks;
 
        stmt->rangetable = pstate->p_rtable;
@@ -825,7 +923,7 @@ transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
        qry->commandType = CMD_UTILITY;
 
        /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
        qry->hasSubLinks = pstate->p_hasSubLinks;
 
        stmt->rangetable = pstate->p_rtable;
@@ -900,7 +998,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
        }
 
        /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
        qry->hasSubLinks = pstate->p_hasSubLinks;
 
        qry->utilityStmt = (Node *) stmt;
@@ -917,68 +1015,65 @@ static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       Node       *fromQual;
 
        qry->commandType = CMD_SELECT;
 
        /* set up a range table */
-       makeRangeTable(pstate, NULL, stmt->fromClause);
+       makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
        qry->uniqueFlag = stmt->unique;
 
        qry->into = stmt->into;
+       qry->isTemp = stmt->istemp;
        qry->isPortal = FALSE;
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       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);
+       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)
+       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
         */
        qry->unionall = stmt->unionall;
 
-       /***S*I***/
-       /* Just hand through the unionClause and intersectClause. 
-        * We will handle it in the function Except_Intersect_Rewrite() */
-       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.
+        * Just hand through the unionClause and intersectClause. We will
+        * handle it in the function Except_Intersect_Rewrite()
         */
-       if ((qry->hasAggs == false) && (qry->havingQual != NULL))
-       {
-               elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-               return (Query *) NIL;
-       }
+       qry->unionClause = stmt->unionClause;
+       qry->intersectClause = stmt->intersectClause;
+
+       qry->rtable = pstate->p_rtable;
 
        if (stmt->forUpdate != NULL)
                transformForUpdate(qry, stmt->forUpdate);
@@ -995,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;
@@ -1003,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);
+       makeRangeTable(pstate, stmt->fromClause, NULL);
+       setTargetTable(pstate, stmt->relname);
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       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;
 }
 
@@ -1034,148 +1169,164 @@ transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
        qry = transformSelectStmt(pstate, stmt);
 
        qry->into = stmt->portalname;
+       qry->isTemp = stmt->istemp;
        qry->isPortal = TRUE;
        qry->isBinary = stmt->binary;           /* internal portal */
 
        return qry;
 }
 
-/***S*I***/
 /* This function steps through the tree
  * built up by the select_w_o_sort rule
  * and builds a list of all SelectStmt Nodes found
  * The built up list is handed back in **select_list.
  * If one of the SelectStmt Nodes has the 'unionall' flag
  * set to true *unionall_present hands back 'true' */
-void 
+void
 create_select_list(Node *ptr, List **select_list, bool *unionall_present)
 {
-  if(IsA(ptr, SelectStmt)) {
-    *select_list = lappend(*select_list, ptr);    
-    if(((SelectStmt *)ptr)->unionall == TRUE) *unionall_present = TRUE;    
-    return;    
-  }
-  
-  /* Recursively call for all arguments. A NOT expr has no lexpr! */
-  if (((A_Expr *)ptr)->lexpr != NULL) 
-     create_select_list(((A_Expr *)ptr)->lexpr, select_list, unionall_present);
-  create_select_list(((A_Expr *)ptr)->rexpr, select_list, unionall_present);
+       if (IsA(ptr, SelectStmt))
+       {
+               *select_list = lappend(*select_list, ptr);
+               if (((SelectStmt *) ptr)->unionall == TRUE)
+                       *unionall_present = TRUE;
+               return;
+       }
+
+       /* Recursively call for all arguments. A NOT expr has no lexpr! */
+       if (((A_Expr *) ptr)->lexpr != NULL)
+               create_select_list(((A_Expr *) ptr)->lexpr, select_list, unionall_present);
+       create_select_list(((A_Expr *) ptr)->rexpr, select_list, unionall_present);
 }
 
 /* Changes the A_Expr Nodes to Expr Nodes and exchanges ANDs and ORs.
- * The reason for the exchange is easy: We implement INTERSECTs and EXCEPTs 
+ * The reason for the exchange is easy: We implement INTERSECTs and EXCEPTs
  * by rewriting these queries to semantically equivalent queries that use
- * IN and NOT IN subselects. To be able to use all three operations 
- * (UNIONs INTERSECTs and EXCEPTs) in one complex query we have to 
+ * IN and NOT IN subselects. To be able to use all three operations
+ * (UNIONs INTERSECTs and EXCEPTs) in one complex query we have to
  * translate the queries into Disjunctive Normal Form (DNF). Unfortunately
  * there is no function 'dnfify' but there is a function 'cnfify'
  * which produces DNF when we exchange ANDs and ORs before calling
  * 'cnfify' and exchange them back in the result.
  *
  * If an EXCEPT or INTERSECT is present *intersect_present
- * hands back 'true' */ 
-Node *A_Expr_to_Expr(Node *ptr, bool *intersect_present)
+ * hands back 'true' */
+Node *
+A_Expr_to_Expr(Node *ptr, bool *intersect_present)
+{
+       Node       *result = NULL;
+
+       switch (nodeTag(ptr))
+       {
+               case T_A_Expr:
+                       {
+                               A_Expr     *a = (A_Expr *) ptr;
+
+                               switch (a->oper)
+                               {
+                                       case AND:
+                                               {
+                                                       Expr       *expr = makeNode(Expr);
+                                                       Node       *lexpr = A_Expr_to_Expr(((A_Expr *) ptr)->lexpr, intersect_present);
+                                                       Node       *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
+
+                                                       *intersect_present = TRUE;
+
+                                                       expr->typeOid = BOOLOID;
+                                                       expr->opType = OR_EXPR;
+                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       result = (Node *) expr;
+                                                       break;
+                                               }
+                                       case OR:
+                                               {
+                                                       Expr       *expr = makeNode(Expr);
+                                                       Node       *lexpr = A_Expr_to_Expr(((A_Expr *) ptr)->lexpr, intersect_present);
+                                                       Node       *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
+
+                                                       expr->typeOid = BOOLOID;
+                                                       expr->opType = AND_EXPR;
+                                                       expr->args = makeList(lexpr, rexpr, -1);
+                                                       result = (Node *) expr;
+                                                       break;
+                                               }
+                                       case NOT:
+                                               {
+                                                       Expr       *expr = makeNode(Expr);
+                                                       Node       *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
+
+                                                       expr->typeOid = BOOLOID;
+                                                       expr->opType = NOT_EXPR;
+                                                       expr->args = makeList(rexpr, -1);
+                                                       result = (Node *) expr;
+                                                       break;
+                                               }
+                               }
+                               break;
+                       }
+               default:
+                       result = ptr;
+       }
+       return result;
+}
+
+void
+CheckSelectForUpdate(Query *qry)
 {
-  Node *result;
-  
-  switch(nodeTag(ptr))
-    {
-    case T_A_Expr:
-      {
-       A_Expr *a = (A_Expr *)ptr;
-       
-       switch (a->oper)
-         {
-         case AND:
-           {
-             Expr *expr = makeNode(Expr);
-             Node         *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present);
-             Node         *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
-
-             *intersect_present = TRUE;
-             
-             expr->typeOid = BOOLOID;
-             expr->opType = OR_EXPR;
-             expr->args = makeList(lexpr, rexpr, -1);
-             result = (Node *) expr;
-             break;          
-           }             
-         case OR:
-           {
-             Expr *expr = makeNode(Expr);
-             Node         *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present);
-             Node         *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
-
-             expr->typeOid = BOOLOID;
-             expr->opType = AND_EXPR;
-             expr->args = makeList(lexpr, rexpr, -1);
-             result = (Node *) expr;
-             break;          
-           }
-         case NOT:
-           {
-             Expr *expr = makeNode(Expr);
-             Node         *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present);
-
-             expr->typeOid = BOOLOID;
-             expr->opType = NOT_EXPR;
-             expr->args = makeList(rexpr, -1);
-             result = (Node *) expr;
-             break;          
-           }
-         }     
-       break;  
-      }
-    default:
-      {
-       result = ptr;
-      }      
-    }
-  return result;  
+       if (qry->unionClause != NULL)
+               elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
+       if (qry->uniqueFlag != NULL)
+               elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
+       if (qry->groupClause != NULL)
+               elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
+       if (qry->hasAggs)
+               elog(ERROR, "SELECT FOR UPDATE is not allowed with AGGREGATE");
 }
 
 static void
 transformForUpdate(Query *qry, List *forUpdate)
 {
        List       *rowMark = NULL;
-       RowMark    *newrm;
+       RowMark    *newrm;
        List       *l;
        Index           i;
 
+       CheckSelectForUpdate(qry);
+
        if (lfirst(forUpdate) == NULL)          /* all tables */
        {
                i = 1;
-               foreach (l, qry->rtable)
+               foreach(l, qry->rtable)
                {
                        newrm = makeNode(RowMark);
                        newrm->rti = i++;
-                       newrm->info = ROW_MARK_FOR_UPDATE|ROW_ACL_FOR_UPDATE;
+                       newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
                        rowMark = lappend(rowMark, newrm);
                }
                qry->rowMark = nconc(qry->rowMark, rowMark);
                return;
        }
 
-       foreach (l, forUpdate)
+       foreach(l, forUpdate)
        {
-               List   *l2;
-               List   *l3;
+               List       *l2;
+               List       *l3;
 
                i = 1;
-               foreach (l2, qry->rtable)
+               foreach(l2, qry->rtable)
                {
-                       if (strcmp(((RangeTblEntry*)lfirst(l2))->refname, lfirst(l)) == 0)
+                       if (strcmp(((RangeTblEntry *) lfirst(l2))->refname, lfirst(l)) == 0)
                        {
-                               foreach (l3, rowMark)
+                               foreach(l3, rowMark)
                                {
-                                       if (((RowMark*)lfirst(l3))->rti == i)   /* duplicate */
+                                       if (((RowMark *) lfirst(l3))->rti == i)         /* duplicate */
                                                break;
                                }
                                if (l3 == NULL)
                                {
                                        newrm = makeNode(RowMark);
                                        newrm->rti = i;
-                                       newrm->info = ROW_MARK_FOR_UPDATE|ROW_ACL_FOR_UPDATE;
+                                       newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
                                        rowMark = lappend(rowMark, newrm);
                                }
                                break;