OSDN Git Service

Reimplement parsing and storage of default expressions and constraint
[pg-rex/syncrep.git] / src / backend / parser / analyze.c
index 9e5f830..4bcf79a 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * analyze.c--
+ * analyze.c
  *       transform the parse tree into a query tree
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *
- * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.42 1997/09/08 21:46:00 momjian Exp $
+ *     $Id: analyze.c,v 1.120 1999/10/03 23:55:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "postgres.h"
-#include "nodes/nodes.h"
-#include "nodes/params.h"
-#include "nodes/primnodes.h"
-#include "nodes/parsenodes.h"
-#include "nodes/relation.h"
-#include "parse.h"                             /* for AND, OR, etc. */
-#include "catalog/pg_type.h"   /* for INT4OID, etc. */
-#include "catalog/pg_proc.h"
-#include "utils/elog.h"
-#include "utils/builtins.h"            /* namecmp(), textout() */
-#include "utils/lsyscache.h"
-#include "utils/palloc.h"
-#include "utils/mcxt.h"
-#include "utils/syscache.h"
-#include "utils/acl.h"
-#include "parser/parse_query.h"
-#include "parser/parse_state.h"
-#include "nodes/makefuncs.h"   /* for makeResdom(), etc. */
-#include "nodes/nodeFuncs.h"
-#include "commands/sequence.h"
-
-#include "optimizer/clauses.h"
-#include "access/heapam.h"
 
-#include "miscadmin.h"
+#include "postgres.h"
 
-#include "port-protos.h"               /* strdup() */
+#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 "utils/builtins.h"
 
-/* convert the parse tree into a query tree */
 static Query *transformStmt(ParseState *pstate, Node *stmt);
-
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
-static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt);
+static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
 static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
 static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
-static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt);
-static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt);
-static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt);
-static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno);
-
-#define EXPR_COLUMN_FIRST       1
-#define EXPR_RELATION_FIRST  2
-static Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
-static Node *transformIdent(ParseState *pstate, Node *expr, int precedence);
-
-static void makeRangeTable(ParseState *pstate, char *relname, List *frmList);
-static List *expandAllTables(ParseState *pstate);
-static char *figureColname(Node *expr, Node *resval);
-static List *makeTargetNames(ParseState *pstate, List *cols);
-static List *transformTargetList(ParseState *pstate, List *targetlist);
-static TargetEntry *
-make_targetlist_expr(ParseState *pstate,
-                                        char *colname, Node *expr,
-                                        List *arrayRef);
-static bool inWhereClause = false;
-static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
-static List *
-transformGroupClause(ParseState *pstate, List *grouplist,
-                                        List *targetlist);
-static List *
-transformSortClause(ParseState *pstate,
-                                       List *orderlist, List *targetlist,
-                                       char *uniqueFlag);
-
-static void parseFromClause(ParseState *pstate, List *frmList);
-static Node *
-ParseFunc(ParseState *pstate, char *funcname,
-                 List *fargs, int *curr_resno);
-static List *setup_tlist(char *attname, Oid relid);
-static List *setup_base_tlist(Oid typeid);
-static void
-make_arguments(int nargs, List *fargs, Oid *input_typeids,
-                          Oid *function_typeids);
-static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
-static void finalizeAggregates(ParseState *pstate, Query *qry);
-static void parseCheckAggregates(ParseState *pstate, Query *qry);
-static ParseState *makeParseState(void);
-
-/*****************************************************************************
- *
- *****************************************************************************/
+static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
+static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
+static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
 
-/*
- * makeParseState() --
- *       allocate and initialize a new ParseState.
- *     the CALLERS is responsible for freeing the ParseState* returned
- *
- */
+static void transformForUpdate(Query *qry, List *forUpdate);
+void           CheckSelectForUpdate(Query *qry);
 
-static ParseState *
-makeParseState(void)
-{
-       ParseState *pstate;
+/* kluge to return extra info from transformCreateStmt() */
+static List       *extras_before;
+static List       *extras_after;
 
-       pstate = malloc(sizeof(ParseState));
-       pstate->p_last_resno = 1;
-       pstate->p_rtable = NIL;
-       pstate->p_numAgg = 0;
-       pstate->p_aggs = NIL;
-       pstate->p_is_insert = false;
-       pstate->p_insert_columns = NIL;
-       pstate->p_is_update = false;
-       pstate->p_is_rule = false;
-       pstate->p_target_relation = NULL;
-       pstate->p_target_rangetblentry = NULL;
-
-       return (pstate);
-}
 
 /*
  * parse_analyze -
@@ -132,29 +49,51 @@ makeParseState(void)
  * Returns a list of transformed parse trees. Optimizable statements are
  * all transformed to Query while the rest stays the same.
  *
- * CALLER is responsible for freeing the QueryTreeList* returned
  */
-QueryTreeList *
-parse_analyze(List *pl)
+List *
+parse_analyze(List *pl, ParseState *parentParseState)
 {
-       QueryTreeList *result;
+       List       *result = NIL;
        ParseState *pstate;
-       int                     i = 0;
-
-       result = malloc(sizeof(QueryTreeList));
-       result->len = length(pl);
-       result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
-
-       inWhereClause = false;          /* to avoid nextval(sequence) in WHERE */
+       Query      *parsetree;
 
        while (pl != NIL)
        {
-               pstate = makeParseState();
-               result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
-               pl = lnext(pl);
+               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);
-               free(pstate);
+                       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)));
+                       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);
+               }
+
+               result = lappend(result, parsetree);
+
+               while (extras_after != NIL)
+               {
+                       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);
+               }
+
+               pfree(pstate);
+               pl = lnext(pl);
        }
 
        return result;
@@ -176,6 +115,10 @@ transformStmt(ParseState *pstate, Node *parseTree)
                         *      Non-optimizable statements
                         *------------------------
                         */
+               case T_CreateStmt:
+                       result = transformCreateStmt(pstate, (CreateStmt *) parseTree);
+                       break;
+
                case T_IndexStmt:
                        result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
                        break;
@@ -214,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;
@@ -232,31 +175,34 @@ transformStmt(ParseState *pstate, Node *parseTree)
                         *      Optimizable statements
                         *------------------------
                         */
-               case T_AppendStmt:
-                       result = transformInsertStmt(pstate, (AppendStmt *) parseTree);
+               case T_InsertStmt:
+                       result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
                        break;
 
                case T_DeleteStmt:
                        result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
                        break;
 
-               case T_ReplaceStmt:
-                       result = transformUpdateStmt(pstate, (ReplaceStmt *) parseTree);
-                       break;
-
-               case T_CursorStmt:
-                       result = transformCursorStmt(pstate, (CursorStmt *) parseTree);
+               case T_UpdateStmt:
+                       result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
                        break;
 
-               case T_RetrieveStmt:
-                       result = transformSelectStmt(pstate, (RetrieveStmt *) 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;
 
                default:
 
                        /*
                         * other statments don't require any transformation-- just
-                        * return the original parsetree
+                        * return the original parsetree, yea!
                         */
                        result = makeNode(Query);
                        result->commandType = CMD_UTILITY;
@@ -278,18 +224,20 @@ 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;
-       qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
+       qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
-       /* make sure we don't have aggregates in the where clause */
-       if (pstate->p_numAgg > 0)
+       qry->hasAggs = pstate->p_hasAggs;
+       if (pstate->p_hasAggs)
                parseCheckAggregates(pstate, qry);
 
        return (Query *) qry;
@@ -300,2462 +248,1095 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
  *       transform an Insert Statement
  */
 static Query *
-transformInsertStmt(ParseState *pstate, AppendStmt *stmt)
+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 = NULL;
+       /* set up a range table --- note INSERT target is not in it yet */
+       makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
-       /* fix the target list */
-       pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
+       qry->uniqueFlag = stmt->unique;
 
        qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+       qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
+
+       /* 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->groupClause = transformGroupClause(pstate,
+                                                                                       stmt->groupClause,
+                                                                                       qry->targetList);
+
+       /* An InsertStmt has no sortClause, but we still call
+        * transformSortClause because it also handles uniqueFlag.
+        */
+       qry->sortClause = transformSortClause(pstate,
+                                                                                 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
+        */
+       qry->unionall = stmt->unionall;
+
+       /*
+        * 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;
+
+       /*
+        * Now we are done with SELECT-like processing, and can get on with
+        * transforming the target list to match the INSERT target columns.
+        *
+        * In particular, it's time to add the INSERT target to the rangetable.
+        * (We didn't want it there until now since it shouldn't be visible in
+        * the SELECT part.)
+        */
+       setTargetTable(pstate, stmt->relname);
 
        /* now the range table will not change */
        qry->rtable = pstate->p_rtable;
-       qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
+       qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+
+       /* Prepare to assign non-conflicting resnos to resjunk attributes */
+       if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+               pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+       /* Validate stmt->cols list, or build default list if no list given */
+       icolumns = makeTargetNames(pstate, stmt->cols);
+
+       /* Prepare non-junk columns for assignment to target table */
+       foreach(tl, qry->targetList)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Resdom     *resnode = tle->resdom;
+               Ident      *id;
+
+               if (resnode->resjunk)
+               {
+                       /* Resjunk nodes need no additional processing, but be sure they
+                        * have names and resnos that do not match any target columns;
+                        * else rewriter or planner might get confused.
+                        */
+                       resnode->resname = "?resjunk?";
+                       resnode->resno = (AttrNumber) pstate->p_last_resno++;
+                       continue;
+               }
+               if (icolumns == NIL)
+                       elog(ERROR, "INSERT has more expressions than target columns");
+               id = (Ident *) lfirst(icolumns);
+               updateTargetListEntry(pstate, tle, id->name, id->indirection);
+               icolumns = lnext(icolumns);
+       }
+
+       /*
+        * Add targetlist items to assign DEFAULT values to any columns that
+        * have defaults and were not assigned to by the user.
+        * XXX wouldn't it make more sense to do this further downstream,
+        * after the rule rewriter?
+        */
+       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 (pstate->p_numAgg > 0)
-               finalizeAggregates(pstate, qry);
+       if (stmt->forUpdate != NULL)
+               transformForUpdate(qry, stmt->forUpdate);
 
        return (Query *) qry;
 }
 
 /*
- * transformIndexStmt -
- *       transforms the qualification of the index statement
+ *     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 Query *
-transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
+static char *
+makeObjectName(char *name1, char *name2, char *typename)
 {
-       Query      *q;
+       char       *name;
+       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)
+       {
+               name2chars = strlen(name2);
+               overhead++;                             /* allow for separating underscore */
+       }
+       else
+               name2chars = 0;
+       if (typename)
+               overhead += strlen(typename) + 1;
 
-       q = makeNode(Query);
-       q->commandType = CMD_UTILITY;
+       availchars = NAMEDATALEN-1 - overhead;
 
-       /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
-       stmt->rangetable = pstate->p_rtable;
+       /* 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--;
+       }
 
-       q->utilityStmt = (Node *) stmt;
+       /* 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 q;
+       return name;
 }
 
-/*
- * transformExtendStmt -
- *       transform the qualifications of the Extend Index Statement
- *
- */
-static Query *
-transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
+static char *
+CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
 {
-       Query      *q;
+       int                     pass = 0;
+       char       *iname = NULL;
+       List       *ilist;
+       char            typename[NAMEDATALEN];
+
+       /* 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);
 
-       q = makeNode(Query);
-       q->commandType = CMD_UTILITY;
+       for (;;)
+       {
+               iname = makeObjectName(table_name, column_name, typename);
 
-       /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
-       stmt->rangetable = pstate->p_rtable;
+               foreach(ilist, indices)
+               {
+                       IndexStmt  *index = lfirst(ilist);
+                       if (strcasecmp(iname, index->idxname) == 0)
+                               break;
+               }
+               /* ran through entire list? then no name conflict found so done */
+               if (ilist == NIL)
+                       break;
 
-       q->utilityStmt = (Node *) stmt;
-       return q;
+               /* the last one conflicted, so try a new name component */
+               pfree(iname);
+               sprintf(typename, "%s%d", label, ++pass);
+       }
+
+       return iname;
 }
 
 /*
- * transformRuleStmt -
- *       transform a Create Rule Statement. The actions is a list of parse
- *       trees which is transformed into a list of query trees.
+ * transformCreateStmt -
+ *       transforms the "create table" statement
+ *       SQL92 allows constraints to be scattered all over, so thumb through
+ *        the columns and collect all constraints into one place.
+ *       If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
+ *        then expand those into multiple IndexStmt blocks.
+ *       - thomas 1997-12-02
  */
 static Query *
-transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
+transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 {
        Query      *q;
-       List       *actions;
+       List       *elements;
+       Node       *element;
+       List       *columns;
+       List       *dlist;
+       ColumnDef  *column;
+       List       *constraints,
+                          *clist;
+       Constraint *constraint;
+       List       *keys;
+       Ident      *key;
+       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;
 
-       actions = stmt->actions;
+       constraints = stmt->constraints;
+       columns = NIL;
+       dlist = NIL;
 
        /*
-        * transform each statment, like parse_analyze()
+        * Run through each primary element in the table creation clause
         */
-       while (actions != NIL)
+       foreach(elements, stmt->tableElts)
        {
+               element = lfirst(elements);
+               switch (nodeTag(element))
+               {
+                       case T_ColumnDef:
+                               column = (ColumnDef *) element;
+                               columns = lappend(columns, column);
 
-               /*
-                * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
-                * equal to 2.
-                */
-               addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
-                                                  FALSE, FALSE, NULL);
-               addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
-                                                  FALSE, FALSE, NULL);
+                               /* Special case SERIAL type? */
+                               if (column->is_sequence)
+                               {
+                                       char       *sname;
+                                       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;
+                                       constraint->raw_expr = (Node *) funccallnode;
+                                       constraint->cooked_expr = NULL;
+                                       constraint->keys = 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'",
+                                         sequence->seqname, stmt->relname, column->colname);
+
+                                       blist = lcons(sequence, NIL);
+                               }
 
-               pstate->p_last_resno = 1;
-               pstate->p_is_rule = true;               /* for expand all */
-               pstate->p_numAgg = 0;
-               pstate->p_aggs = NULL;
+                               /* Process column constraints, if any... */
+                               foreach(clist, column->constraints)
+                               {
+                                       constraint = lfirst(clist);
+                                       switch (constraint->contype)
+                                       {
+                                               case CONSTR_NULL:
 
-               lfirst(actions) = transformStmt(pstate, lfirst(actions));
-               actions = lnext(actions);
-       }
+                                                       /*
+                                                        * 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;
 
-       /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+                       case T_Constraint:
+                               constraint = (Constraint *) element;
+                               switch (constraint->contype)
+                               {
+                                       case CONSTR_PRIMARY:
+                                               if (constraint->name == NULL)
+                                                       constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
+                                               dlist = lappend(dlist, constraint);
+                                               break;
 
-       q->utilityStmt = (Node *) stmt;
-       return q;
-}
+                                       case CONSTR_UNIQUE:
+                                               dlist = lappend(dlist, constraint);
+                                               break;
 
+                                       case CONSTR_CHECK:
+                                               constraints = lappend(constraints, constraint);
+                                               break;
 
-/*
- * transformSelectStmt -
- *       transforms a Select Statement
+                                       case CONSTR_NOTNULL:
+                                       case CONSTR_DEFAULT:
+                                               elog(ERROR, "parser: illegal context for constraint (internal error)");
+                                               break;
+                                       default:
+                                               elog(ERROR, "parser: unrecognized constraint (internal error)");
+                                               break;
+                               }
+                               break;
+
+                       default:
+                               elog(ERROR, "parser: unrecognized node (internal error)");
+               }
+       }
+
+       stmt->tableElts = columns;
+       stmt->constraints = constraints;
+
+/* Now run through the "deferred list" to complete the query transformation.
+ * For PRIMARY KEYs, mark each column as NOT NULL and create an index.
+ * For UNIQUE, create an index as for PRIMARY KEYS, but do not insist on NOT NULL.
  *
+ * 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 duplicated, or a user might have specified
+ *     extra useless indices which might hurt performance. - thomas 1997-12-08
  */
-static Query *
-transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt)
-{
-       Query      *qry = makeNode(Query);
+       while (dlist != NIL)
+       {
+               constraint = lfirst(dlist);
+               Assert(nodeTag(constraint) == T_Constraint);
+               Assert((constraint->contype == CONSTR_PRIMARY)
+                          || (constraint->contype == CONSTR_UNIQUE));
 
-       qry->commandType = CMD_SELECT;
+               index = makeNode(IndexStmt);
 
-       /* set up a range table */
-       makeRangeTable(pstate, NULL, stmt->fromClause);
+               index->unique = TRUE;
+               index->primary = (constraint->contype == CONSTR_PRIMARY ? TRUE : FALSE);
+               if (index->primary)
+               {
+                       if (pkey != NULL)
+                               elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
+                                        " for table '%s' are not allowed", stmt->relname);
+                       pkey = (IndexStmt *) index;
+               }
 
-       qry->uniqueFlag = stmt->unique;
+               if (constraint->name != NULL)
+                       index->idxname = pstrdup(constraint->name);
+               else if (constraint->contype == CONSTR_PRIMARY)
+                       index->idxname = makeObjectName(stmt->relname, NULL, "pkey");
+               else
+                       index->idxname = NULL;
 
-       qry->into = stmt->into;
-       qry->isPortal = FALSE;
+               index->relname = stmt->relname;
+               index->accessMethod = "btree";
+               index->indexParams = NIL;
+               index->withClause = NIL;
+               index->whereClause = NULL;
 
-       /* fix the target list */
-       qry->targetList = transformTargetList(pstate, stmt->targetList);
+               keys = constraint->keys;
+               while (keys != NIL)
+               {
+                       key = lfirst(keys);
+                       columns = stmt->tableElts;
+                       column = NULL;
+                       while (columns != NIL)
+                       {
+                               column = lfirst(columns);
+                               if (strcasecmp(column->colname, key->name) == 0)
+                                       break;
+                               else
+                                       column = NULL;
+                               columns = lnext(columns);
+                       }
+                       if (column == NULL)
+                               elog(ERROR, "CREATE TABLE column '%s' in key does not exist", key->name);
+
+                       if (constraint->contype == CONSTR_PRIMARY)
+                               column->is_not_null = TRUE;
+                       iparam = makeNode(IndexElem);
+                       iparam->name = pstrdup(column->colname);
+                       iparam->args = NIL;
+                       iparam->class = NULL;
+                       iparam->typename = NULL;
+                       index->indexParams = lappend(index->indexParams, iparam);
+
+                       if (index->idxname == NULL)
+                               index->idxname = CreateIndexName(stmt->relname, iparam->name, "key", ilist);
+
+                       keys = lnext(keys);
+               }
 
-       /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
+               if (index->idxname == NULL)     /* should not happen */
+                       elog(ERROR, "CREATE TABLE: failed to make implicit index name");
 
-       /* check subselect clause */
-       if (stmt->selectClause)
-               elog(NOTICE, "UNION not yet supported; using first SELECT only", NULL);
+               ilist = lappend(ilist, index);
+               dlist = lnext(dlist);
+       }
 
-       /* check subselect clause */
-       if (stmt->havingClause)
-               elog(NOTICE, "HAVING not yet supported; ignore clause", NULL);
+/* 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;
 
-       /* fix order clause */
-       qry->sortClause = transformSortClause(pstate,
-                                                                                 stmt->sortClause,
-                                                                                 qry->targetList,
-                                                                                 qry->uniqueFlag);
+                       index = lfirst(dlist);
+                       pcols = pkey->indexParams;
+                       icols = index->indexParams;
 
-       /* fix group by clause */
-       qry->groupClause = transformGroupClause(pstate,
-                                                                                       stmt->groupClause,
-                                                                                       qry->targetList);
-       qry->rtable = pstate->p_rtable;
+                       plen = length(pcols);
+                       ilen = length(icols);
 
-       if (pstate->p_numAgg > 0)
-               finalizeAggregates(pstate, qry);
+                       /* 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;
 
-       return (Query *) qry;
-}
+                                       /* different names? then no match... */
+                                       if (strcmp(iname, pname) != 0)
+                                       {
+                                               keep = TRUE;
+                                               break;
+                                       }
+                                       pcols = lnext(pcols);
+                                       icols = lnext(icols);
+                               }
+                       }
 
-/*
- * transformUpdateStmt -
- *       transforms an update statement
- *
- */
-static Query *
-transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt)
-{
-       Query      *qry = makeNode(Query);
+                       if (keep)
+                               ilist = lappend(ilist, index);
+                       dlist = lnext(dlist);
+               }
+       }
 
-       qry->commandType = CMD_UPDATE;
-       pstate->p_is_update = true;
+       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);
+       }
 
-       /*
-        * 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);
-
-       /* fix the target list */
-       qry->targetList = transformTargetList(pstate, stmt->targetList);
-
-       /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
-
-       qry->rtable = pstate->p_rtable;
-       qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
-
-       /* make sure we don't have aggregates in the where clause */
-       if (pstate->p_numAgg > 0)
-               parseCheckAggregates(pstate, qry);
+       q->utilityStmt = (Node *) stmt;
+       extras_before = blist;
+       extras_after = ilist;
 
-       return (Query *) qry;
-}
+       return q;
+}      /* transformCreateStmt() */
 
 /*
- * transformCursorStmt -
- *       transform a Create Cursor Statement
- *
+ * transformIndexStmt -
+ *       transforms the qualification of the index statement
  */
 static Query *
-transformCursorStmt(ParseState *pstate, CursorStmt *stmt)
-{
-       Query      *qry = makeNode(Query);
-
-       /*
-        * in the old days, a cursor statement is a 'retrieve into portal'; If
-        * you change the following, make sure you also go through the code in
-        * various places that tests the kind of operation.
-        */
-       qry->commandType = CMD_SELECT;
-
-       /* set up a range table */
-       makeRangeTable(pstate, NULL, stmt->fromClause);
-
-       qry->uniqueFlag = stmt->unique;
-
-       qry->into = stmt->portalname;
-       qry->isPortal = TRUE;
-       qry->isBinary = stmt->binary;           /* internal portal */
-
-       /* fix the target list */
-       qry->targetList = transformTargetList(pstate, stmt->targetList);
-
-       /* fix where clause */
-       qry->qual = transformWhereClause(pstate, stmt->whereClause);
-
-       /* fix order clause */
-       qry->sortClause = transformSortClause(pstate,
-                                                                                 stmt->sortClause,
-                                                                                 qry->targetList,
-                                                                                 qry->uniqueFlag);
-       /* fix group by clause */
-       qry->groupClause = transformGroupClause(pstate,
-                                                                                       stmt->groupClause,
-                                                                                       qry->targetList);
-
-       qry->rtable = pstate->p_rtable;
-
-       if (pstate->p_numAgg > 0)
-               finalizeAggregates(pstate, qry);
-
-       return (Query *) qry;
-}
-
-/*****************************************************************************
- *
- * Transform Exprs, Aggs, etc.
- *
- *****************************************************************************/
-
-/*
- * transformExpr -
- *       analyze and transform expressions. Type checking and type casting is
- *       done here. The optimizer and the executor cannot handle the original
- *       (raw) expressions collected by the parse tree. Hence the transformation
- *       here.
- */
-static Node *
-transformExpr(ParseState *pstate, Node *expr, int precedence)
-{
-       Node       *result = NULL;
-
-       if (expr == NULL)
-               return NULL;
-
-       switch (nodeTag(expr))
-       {
-               case T_Attr:
-                       {
-                               Attr       *att = (Attr *) expr;
-                               Node       *temp;
-
-                               /* what if att.attrs == "*"?? */
-                               temp = handleNestedDots(pstate, att, &pstate->p_last_resno);
-                               if (att->indirection != NIL)
-                               {
-                                       List       *idx = att->indirection;
-
-                                       while (idx != NIL)
-                                       {
-                                               A_Indices  *ai = (A_Indices *) lfirst(idx);
-                                               Node       *lexpr = NULL,
-                                                                  *uexpr;
-
-                                               uexpr = transformExpr(pstate, ai->uidx, precedence);    /* must exists */
-                                               if (exprType(uexpr) != INT4OID)
-                                                       elog(WARN, "array index expressions must be int4's");
-                                               if (ai->lidx != NULL)
-                                               {
-                                                       lexpr = transformExpr(pstate, ai->lidx, precedence);
-                                                       if (exprType(lexpr) != INT4OID)
-                                                               elog(WARN, "array index expressions must be int4's");
-                                               }
-#if 0
-                                               pfree(ai->uidx);
-                                               if (ai->lidx != NULL)
-                                                       pfree(ai->lidx);
-#endif
-                                               ai->lidx = lexpr;
-                                               ai->uidx = uexpr;
-
-                                               /*
-                                                * note we reuse the list of indices, make sure we
-                                                * don't free them! Otherwise, make a new list
-                                                * here
-                                                */
-                                               idx = lnext(idx);
-                                       }
-                                       result = (Node *) make_array_ref(temp, att->indirection);
-                               }
-                               else
-                               {
-                                       result = temp;
-                               }
-                               break;
-                       }
-               case T_A_Const:
-                       {
-                               A_Const    *con = (A_Const *) expr;
-                               Value      *val = &con->val;
-
-                               if (con->typename != NULL)
-                               {
-                                       result = parser_typecast(val, con->typename, -1);
-                               }
-                               else
-                               {
-                                       result = (Node *) make_const(val);
-                               }
-                               break;
-                       }
-               case T_ParamNo:
-                       {
-                               ParamNo    *pno = (ParamNo *) expr;
-                               Oid                     toid;
-                               int                     paramno;
-                               Param      *param;
-
-                               paramno = pno->number;
-                               toid = param_type(paramno);
-                               if (!OidIsValid(toid))
-                               {
-                                       elog(WARN, "Parameter '$%d' is out of range",
-                                                paramno);
-                               }
-                               param = makeNode(Param);
-                               param->paramkind = PARAM_NUM;
-                               param->paramid = (AttrNumber) paramno;
-                               param->paramname = "<unnamed>";
-                               param->paramtype = (Oid) toid;
-                               param->param_tlist = (List *) NULL;
-
-                               result = (Node *) param;
-                               break;
-                       }
-               case T_A_Expr:
-                       {
-                               A_Expr     *a = (A_Expr *) expr;
-
-                               switch (a->oper)
-                               {
-                                       case OP:
-                                               {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
-
-                                                       result = (Node *) make_op(a->opname, lexpr, rexpr);
-                                               }
-                                               break;
-                                       case ISNULL:
-                                               {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-
-                                                       result = ParseFunc(pstate,
-                                                                                 "nullvalue", lcons(lexpr, NIL),
-                                                                                          &pstate->p_last_resno);
-                                               }
-                                               break;
-                                       case NOTNULL:
-                                               {
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-
-                                                       result = ParseFunc(pstate,
-                                                                          "nonnullvalue", lcons(lexpr, NIL),
-                                                                                          &pstate->p_last_resno);
-                                               }
-                                               break;
-                                       case AND:
-                                               {
-                                                       Expr       *expr = makeNode(Expr);
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
-
-                                                       if (exprType(lexpr) != BOOLOID)
-                                                               elog(WARN,
-                                                                        "left-hand side of AND is type '%s', not bool",
-                                                                        tname(get_id_type(exprType(lexpr))));
-                                                       if (exprType(rexpr) != BOOLOID)
-                                                               elog(WARN,
-                                                                        "right-hand side of AND is type '%s', not bool",
-                                                                        tname(get_id_type(exprType(rexpr))));
-                                                       expr->typeOid = BOOLOID;
-                                                       expr->opType = AND_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
-                                                       result = (Node *) expr;
-                                               }
-                                               break;
-                                       case OR:
-                                               {
-                                                       Expr       *expr = makeNode(Expr);
-                                                       Node       *lexpr = transformExpr(pstate, a->lexpr, precedence);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
-
-                                                       if (exprType(lexpr) != BOOLOID)
-                                                               elog(WARN,
-                                                                        "left-hand side of OR is type '%s', not bool",
-                                                                        tname(get_id_type(exprType(lexpr))));
-                                                       if (exprType(rexpr) != BOOLOID)
-                                                               elog(WARN,
-                                                                        "right-hand side of OR is type '%s', not bool",
-                                                                        tname(get_id_type(exprType(rexpr))));
-                                                       expr->typeOid = BOOLOID;
-                                                       expr->opType = OR_EXPR;
-                                                       expr->args = makeList(lexpr, rexpr, -1);
-                                                       result = (Node *) expr;
-                                               }
-                                               break;
-                                       case NOT:
-                                               {
-                                                       Expr       *expr = makeNode(Expr);
-                                                       Node       *rexpr = transformExpr(pstate, a->rexpr, precedence);
-
-                                                       if (exprType(rexpr) != BOOLOID)
-                                                               elog(WARN,
-                                                               "argument to NOT is type '%s', not bool",
-                                                                        tname(get_id_type(exprType(rexpr))));
-                                                       expr->typeOid = BOOLOID;
-                                                       expr->opType = NOT_EXPR;
-                                                       expr->args = makeList(rexpr, -1);
-                                                       result = (Node *) expr;
-                                               }
-                                               break;
-                               }
-                               break;
-                       }
-               case T_Ident:
-                       {
-
-                               /*
-                                * look for a column name or a relation name (the default
-                                * behavior)
-                                */
-                               result = transformIdent(pstate, expr, precedence);
-                               break;
-                       }
-               case T_FuncCall:
-                       {
-                               FuncCall   *fn = (FuncCall *) expr;
-                               List       *args;
-
-                               /* transform the list of arguments */
-                               foreach(args, fn->args)
-                                       lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
-                               result = ParseFunc(pstate,
-                                                 fn->funcname, fn->args, &pstate->p_last_resno);
-                               break;
-                       }
-               default:
-                       /* should not reach here */
-                       elog(WARN, "transformExpr: does not know how to transform %d\n",
-                                nodeTag(expr));
-                       break;
-       }
-
-       return result;
-}
-
-static Node *
-transformIdent(ParseState *pstate, Node *expr, int precedence)
-{
-       Ident      *ident = (Ident *) expr;
-       RangeTblEntry *rte;
-       Node       *column_result,
-                          *relation_result,
-                          *result;
-
-       column_result = relation_result = result = 0;
-       /* try to find the ident as a column */
-       if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
-       {
-               Attr       *att = makeNode(Attr);
-
-               att->relname = rte->refname;
-               att->attrs = lcons(makeString(ident->name), NIL);
-               column_result =
-                       (Node *) handleNestedDots(pstate, att, &pstate->p_last_resno);
-       }
-
-       /* try to find the ident as a relation */
-       if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL)
-       {
-               ident->isRel = TRUE;
-               relation_result = (Node *) ident;
-       }
-
-       /* choose the right result based on the precedence */
-       if (precedence == EXPR_COLUMN_FIRST)
-       {
-               if (column_result)
-                       result = column_result;
-               else
-                       result = relation_result;
-       }
-       else
-       {
-               if (relation_result)
-                       result = relation_result;
-               else
-                       result = column_result;
-       }
-
-       if (result == NULL)
-               elog(WARN, "attribute \"%s\" not found", ident->name);
-
-       return result;
-}
-
-/*****************************************************************************
- *
- * From Clause
- *
- *****************************************************************************/
-
-/*
- * parseFromClause -
- *       turns the table references specified in the from-clause into a
- *       range table. The range table may grow as we transform the expressions
- *       in the target list. (Note that this happens because in POSTQUEL, we
- *       allow references to relations not specified in the from-clause. We
- *       also allow that in our POST-SQL)
- *
- */
-static void
-parseFromClause(ParseState *pstate, List *frmList)
-{
-       List       *fl;
-
-       foreach(fl, frmList)
-       {
-               RangeVar   *r = lfirst(fl);
-               RelExpr    *baserel = r->relExpr;
-               char       *relname = baserel->relname;
-               char       *refname = r->name;
-               RangeTblEntry *rte;
-
-               if (refname == NULL)
-                       refname = relname;
-
-               /*
-                * marks this entry to indicate it comes from the FROM clause. In
-                * SQL, the target list can only refer to range variables
-                * specified in the from clause but we follow the more powerful
-                * POSTQUEL semantics and automatically generate the range
-                * variable if not specified. However there are times we need to
-                * know whether the entries are legitimate.
-                *
-                * eg. select * from foo f where f.x = 1; will generate wrong answer
-                * if we expand * to foo.x.
-                */
-               rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE,
-                                                                baserel->timeRange);
-       }
-}
-
-/*
- * makeRangeTable -
- *       make a range table with the specified relation (optional) and the
- *       from-clause.
- */
-static void
-makeRangeTable(ParseState *pstate, char *relname, List *frmList)
-{
-       RangeTblEntry *rte;
-
-       parseFromClause(pstate, frmList);
-
-       if (relname == NULL)
-               return;
-
-       if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1)
-               rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE, NULL);
-       else
-               rte = refnameRangeTableEntry(pstate->p_rtable, relname);
-
-       pstate->p_target_rangetblentry = rte;
-       Assert(pstate->p_target_relation == NULL);
-       pstate->p_target_relation = heap_open(rte->relid);
-       Assert(pstate->p_target_relation != NULL);
-       /* will close relation later */
-}
-
-/*
- *     exprType -
- *       returns the Oid of the type of the expression. (Used for typechecking.)
- */
-Oid
-exprType(Node *expr)
-{
-       Oid                     type = (Oid) 0;
-
-       switch (nodeTag(expr))
-       {
-               case T_Func:
-                       type = ((Func *) expr)->functype;
-                       break;
-               case T_Iter:
-                       type = ((Iter *) expr)->itertype;
-                       break;
-               case T_Var:
-                       type = ((Var *) expr)->vartype;
-                       break;
-               case T_Expr:
-                       type = ((Expr *) expr)->typeOid;
-                       break;
-               case T_Const:
-                       type = ((Const *) expr)->consttype;
-                       break;
-               case T_ArrayRef:
-                       type = ((ArrayRef *) expr)->refelemtype;
-                       break;
-               case T_Aggreg:
-                       type = ((Aggreg *) expr)->aggtype;
-                       break;
-               case T_Param:
-                       type = ((Param *) expr)->paramtype;
-                       break;
-               case T_Ident:
-                       /* is this right? */
-                       type = UNKNOWNOID;
-                       break;
-               default:
-                       elog(WARN, "exprType: don't know how to get type for %d node",
-                                nodeTag(expr));
-                       break;
-       }
-       return type;
-}
-
-/*
- * expandAllTables -
- *       turns '*' (in the target list) into a list of attributes (of all
- *       relations in the range table)
- */
-static List *
-expandAllTables(ParseState *pstate)
+transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 {
-       List       *target = NIL;
-       List       *legit_rtable = NIL;
-       List       *rt,
-                          *rtable;
-
-       rtable = pstate->p_rtable;
-       if (pstate->p_is_rule)
-       {
-
-               /*
-                * skip first two entries, "*new*" and "*current*"
-                */
-               rtable = lnext(lnext(pstate->p_rtable));
-       }
+       Query      *qry;
 
-       /* this should not happen */
-       if (rtable == NULL)
-               elog(WARN, "cannot expand: null p_rtable");
+       qry = makeNode(Query);
+       qry->commandType = CMD_UTILITY;
 
-       /*
-        * go through the range table and make a list of range table entries
-        * which we will expand.
-        */
-       foreach(rt, rtable)
-       {
-               RangeTblEntry *rte = lfirst(rt);
+       /* take care of the where clause */
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+       qry->hasSubLinks = pstate->p_hasSubLinks;
 
-               /*
-                * we only expand those specify in the from clause. (This will
-                * also prevent us from using the wrong table in inserts: eg.
-                * tenk2 in "insert into tenk2 select * from tenk1;")
-                */
-               if (!rte->inFromCl)
-                       continue;
-               legit_rtable = lappend(legit_rtable, rte);
-       }
+       stmt->rangetable = pstate->p_rtable;
 
-       foreach(rt, legit_rtable)
-       {
-               RangeTblEntry *rte = lfirst(rt);
-               List       *temp = target;
+       qry->utilityStmt = (Node *) stmt;
 
-               if (temp == NIL)
-                       target = expandAll(pstate, rte->relname, rte->refname,
-                                                          &pstate->p_last_resno);
-               else
-               {
-                       while (temp != NIL && lnext(temp) != NIL)
-                               temp = lnext(temp);
-                       lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
-                                                                       &pstate->p_last_resno);
-               }
-       }
-       return target;
+       return qry;
 }
 
-
 /*
- * figureColname -
- *       if the name of the resulting column is not specified in the target
- *       list, we have to guess.
+ * transformExtendStmt -
+ *       transform the qualifications of the Extend Index Statement
  *
  */
-static char *
-figureColname(Node *expr, Node *resval)
+static Query *
+transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
 {
-       switch (nodeTag(expr))
-       {
-                       case T_Aggreg:
-                       return (char *)         /* XXX */
-                       ((Aggreg *) expr)->aggname;
-               case T_Expr:
-                       if (((Expr *) expr)->opType == FUNC_EXPR)
-                       {
-                               if (nodeTag(resval) == T_FuncCall)
-                                       return ((FuncCall *) resval)->funcname;
-                       }
-                       break;
-               default:
-                       break;
-       }
-
-       return "?column?";
-}
-
-/*****************************************************************************
- *
- * Target list
- *
- *****************************************************************************/
-
-/*
- * makeTargetNames -
- *       generate a list of column names if not supplied or
- *       test supplied column names to make sure they are in target table
- *       (used exclusively for inserts)
- */
-static List *
-makeTargetNames(ParseState *pstate, List *cols)
-{
-       List       *tl = NULL;
-
-       /* Generate ResTarget if not supplied */
-
-       if (cols == NIL)
-       {
-               int                     numcol;
-               int                     i;
-               AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs;
-
-               numcol = pstate->p_target_relation->rd_rel->relnatts;
-               for (i = 0; i < numcol; i++)
-               {
-                       Ident      *id = makeNode(Ident);
-
-                       id->name = palloc(NAMEDATALEN);
-                       strNcpy(id->name, attr[i]->attname.data, NAMEDATALEN - 1);
-                       id->indirection = NIL;
-                       id->isRel = false;
-                       if (tl == NIL)
-                               cols = tl = lcons(id, NIL);
-                       else
-                       {
-                               lnext(tl) = lcons(id, NIL);
-                               tl = lnext(tl);
-                       }
-               }
-       }
-       else
-               foreach(tl, cols)
-               /* elog on failure */
-                       varattno(pstate->p_target_relation, ((Ident *) lfirst(tl))->name);
-
-       return cols;
-}
-
-/*
- * transformTargetList -
- *       turns a list of ResTarget's into a list of TargetEntry's
- */
-static List *
-transformTargetList(ParseState *pstate, List *targetlist)
-{
-       List       *p_target = NIL;
-       List       *tail_p_target = NIL;
-
-       while (targetlist != NIL)
-       {
-               ResTarget  *res = (ResTarget *) lfirst(targetlist);
-               TargetEntry *tent = makeNode(TargetEntry);
-
-               switch (nodeTag(res->val))
-               {
-                       case T_Ident:
-                               {
-                                       Node       *expr;
-                                       Oid                     type_id;
-                                       int                     type_len;
-                                       char       *identname;
-                                       char       *resname;
-
-                                       identname = ((Ident *) res->val)->name;
-                                       handleTargetColname(pstate, &res->name, NULL, identname);
-
-                                       /*
-                                        * here we want to look for column names only, not
-                                        * relation
-                                        */
-
-                                       /*
-                                        * names (even though they can be stored in Ident
-                                        * nodes,
-                                        */
-                                       /* too)                                                                                                         */
-                                       expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-                                       type_id = exprType(expr);
-                                       type_len = tlen(get_id_type(type_id));
-                                       resname = (res->name) ? res->name : identname;
-                                       tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                                                                         (Oid) type_id,
-                                                                                         (Size) type_len,
-                                                                                         resname,
-                                                                                         (Index) 0,
-                                                                                         (Oid) 0,
-                                                                                         0);
-
-                                       tent->expr = expr;
-                                       break;
-                               }
-                       case T_ParamNo:
-                       case T_FuncCall:
-                       case T_A_Const:
-                       case T_A_Expr:
-                               {
-                                       Node       *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
-                                       handleTargetColname(pstate, &res->name, NULL, NULL);
-                                       /* note indirection has not been transformed */
-                                       if (pstate->p_is_insert && res->indirection != NIL)
-                                       {
-                                               /* this is an array assignment */
-                                               char       *val;
-                                               char       *str,
-                                                                  *save_str;
-                                               List       *elt;
-                                               int                     i = 0,
-                                                                       ndims;
-                                               int                     lindx[MAXDIM],
-                                                                       uindx[MAXDIM];
-                                               int                     resdomno;
-                                               Relation        rd;
-                                               Value      *constval;
-
-                                               if (exprType(expr) != UNKNOWNOID ||
-                                                       !IsA(expr, Const))
-                                                       elog(WARN, "yyparse: string constant expected");
-
-                                               val = (char *) textout((struct varlena *)
-                                                                                  ((Const *) expr)->constvalue);
-                                               str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
-                                               foreach(elt, res->indirection)
-                                               {
-                                                       A_Indices  *aind = (A_Indices *) lfirst(elt);
-
-                                                       aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
-                                                       if (!IsA(aind->uidx, Const))
-                                                               elog(WARN,
-                                                                        "Array Index for Append should be a constant");
-                                                       uindx[i] = ((Const *) aind->uidx)->constvalue;
-                                                       if (aind->lidx != NULL)
-                                                       {
-                                                               aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
-                                                               if (!IsA(aind->lidx, Const))
-                                                                       elog(WARN,
-                                                                                "Array Index for Append should be a constant");
-                                                               lindx[i] = ((Const *) aind->lidx)->constvalue;
-                                                       }
-                                                       else
-                                                       {
-                                                               lindx[i] = 1;
-                                                       }
-                                                       if (lindx[i] > uindx[i])
-                                                               elog(WARN, "yyparse: lower index cannot be greater than upper index");
-                                                       sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
-                                                       str += strlen(str);
-                                                       i++;
-                                               }
-                                               sprintf(str, "=%s", val);
-                                               rd = pstate->p_target_relation;
-                                               Assert(rd != NULL);
-                                               resdomno = varattno(rd, res->name);
-                                               ndims = att_attnelems(rd, resdomno);
-                                               if (i != ndims)
-                                                       elog(WARN, "yyparse: array dimensions do not match");
-                                               constval = makeNode(Value);
-                                               constval->type = T_String;
-                                               constval->val.str = save_str;
-                                               tent = make_targetlist_expr(pstate, res->name,
-                                                                                  (Node *) make_const(constval),
-                                                                                                       NULL);
-                                               pfree(save_str);
-                                       }
-                                       else
-                                       {
-                                               char       *colname = res->name;
-
-                                               /* this is not an array assignment */
-                                               if (colname == NULL)
-                                               {
-
-                                                       /*
-                                                        * if you're wondering why this is here, look
-                                                        * at the yacc grammar for why a name can be
-                                                        * missing. -ay
-                                                        */
-                                                       colname = figureColname(expr, res->val);
-                                               }
-                                               if (res->indirection)
-                                               {
-                                                       List       *ilist = res->indirection;
-
-                                                       while (ilist != NIL)
-                                                       {
-                                                               A_Indices  *ind = lfirst(ilist);
-
-                                                               ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-                                                               ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-                                                               ilist = lnext(ilist);
-                                                       }
-                                               }
-                                               res->name = colname;
-                                               tent = make_targetlist_expr(pstate, res->name, expr,
-                                                                                                       res->indirection);
-                                       }
-                                       break;
-                               }
-                       case T_Attr:
-                               {
-                                       Oid                     type_id;
-                                       int                     type_len;
-                                       Attr       *att = (Attr *) res->val;
-                                       Node       *result;
-                                       char       *attrname;
-                                       char       *resname;
-                                       Resdom     *resnode;
-                                       List       *attrs = att->attrs;
-
-                                       /*
-                                        * Target item is a single '*', expand all tables (eg.
-                                        * SELECT * FROM emp)
-                                        */
-                                       if (att->relname != NULL && !strcmp(att->relname, "*"))
-                                       {
-                                               if (tail_p_target == NIL)
-                                                       p_target = tail_p_target = expandAllTables(pstate);
-                                               else
-                                                       lnext(tail_p_target) = expandAllTables(pstate);
-
-                                               while (lnext(tail_p_target) != NIL)
-                                                       /* make sure we point to the last target entry */
-                                                       tail_p_target = lnext(tail_p_target);
-
-                                               /*
-                                                * skip rest of while loop
-                                                */
-                                               targetlist = lnext(targetlist);
-                                               continue;
-                                       }
-
-                                       /*
-                                        * Target item is relation.*, expand the table (eg.
-                                        * SELECT emp.*, dname FROM emp, dept)
-                                        */
-                                       attrname = strVal(lfirst(att->attrs));
-                                       if (att->attrs != NIL && !strcmp(attrname, "*"))
-                                       {
-
-                                               /*
-                                                * tail_p_target is the target list we're building
-                                                * in the while loop. Make sure we fix it after
-                                                * appending more nodes.
-                                                */
-                                               if (tail_p_target == NIL)
-                                                       p_target = tail_p_target = expandAll(pstate, att->relname,
-                                                                       att->relname, &pstate->p_last_resno);
-                                               else
-                                                       lnext(tail_p_target) =
-                                                               expandAll(pstate, att->relname, att->relname,
-                                                                                 &pstate->p_last_resno);
-                                               while (lnext(tail_p_target) != NIL)
-                                                       /* make sure we point to the last target entry */
-                                                       tail_p_target = lnext(tail_p_target);
-
-                                               /*
-                                                * skip the rest of the while loop
-                                                */
-                                               targetlist = lnext(targetlist);
-                                               continue;
-                                       }
-
-
-                                       /*
-                                        * Target item is fully specified: ie.
-                                        * relation.attribute
-                                        */
-                                       result = handleNestedDots(pstate, att, &pstate->p_last_resno);
-                                       handleTargetColname(pstate, &res->name, att->relname, attrname);
-                                       if (att->indirection != NIL)
-                                       {
-                                               List       *ilist = att->indirection;
-
-                                               while (ilist != NIL)
-                                               {
-                                                       A_Indices  *ind = lfirst(ilist);
-
-                                                       ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-                                                       ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-                                                       ilist = lnext(ilist);
-                                               }
-                                               result = (Node *) make_array_ref(result, att->indirection);
-                                       }
-                                       type_id = exprType(result);
-                                       type_len = tlen(get_id_type(type_id));
-                                       /* move to last entry */
-                                       while (lnext(attrs) != NIL)
-                                               attrs = lnext(attrs);
-                                       resname = (res->name) ? res->name : strVal(lfirst(attrs));
-                                       resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                                                                (Oid) type_id,
-                                                                                (Size) type_len,
-                                                                                resname,
-                                                                                (Index) 0,
-                                                                                (Oid) 0,
-                                                                                0);
-                                       tent->resdom = resnode;
-                                       tent->expr = result;
-                                       break;
-                               }
-                       default:
-                               /* internal error */
-                               elog(WARN,
-                                        "internal error: do not know how to transform targetlist");
-                               break;
-               }
-
-               if (p_target == NIL)
-               {
-                       p_target = tail_p_target = lcons(tent, NIL);
-               }
-               else
-               {
-                       lnext(tail_p_target) = lcons(tent, NIL);
-                       tail_p_target = lnext(tail_p_target);
-               }
-               targetlist = lnext(targetlist);
-       }
-
-       return p_target;
-}
-
-
-/*
- * make_targetlist_expr -
- *       make a TargetEntry from an expression
- *
- * arrayRef is a list of transformed A_Indices
- */
-static TargetEntry *
-make_targetlist_expr(ParseState *pstate,
-                                        char *colname,
-                                        Node *expr,
-                                        List *arrayRef)
-{
-       Oid                     type_id,
-                               attrtype;
-       int                     type_len,
-                               attrlen;
-       int                     resdomno;
-       Relation        rd;
-       bool            attrisset;
-       TargetEntry *tent;
-       Resdom     *resnode;
-
-       if (expr == NULL)
-               elog(WARN, "make_targetlist_expr: invalid use of NULL expression");
-
-       type_id = exprType(expr);
-       if (type_id == InvalidOid)
-       {
-               type_len = 0;
-       }
-       else
-               type_len = tlen(get_id_type(type_id));
-
-       /* I have no idea what the following does! */
-       /* It appears to process target columns that will be receiving results */
-       if (pstate->p_is_insert || pstate->p_is_update)
-       {
-
-               /*
-                * append or replace query -- append, replace work only on one
-                * relation, so multiple occurence of same resdomno is bogus
-                */
-               rd = pstate->p_target_relation;
-               Assert(rd != NULL);
-               resdomno = varattno(rd, colname);
-               attrisset = varisset(rd, colname);
-               attrtype = att_typeid(rd, resdomno);
-               if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
-                       attrtype = GetArrayElementType(attrtype);
-               if (attrtype == BPCHAROID || attrtype == VARCHAROID)
-               {
-                       attrlen = rd->rd_att->attrs[resdomno - 1]->attlen;
-               }
-               else
-               {
-                       attrlen = tlen(get_id_type(attrtype));
-               }
-#if 0
-               if (Input_is_string && Typecast_ok)
-               {
-                       Datum           val;
-
-                       if (type_id == typeid(type("unknown")))
-                       {
-                               val = (Datum) textout((struct varlena *)
-                                                                         ((Const) lnext(expr))->constvalue);
-                       }
-                       else
-                       {
-                               val = ((Const) lnext(expr))->constvalue;
-                       }
-                       if (attrisset)
-                       {
-                               lnext(expr) = makeConst(attrtype,
-                                                                               attrlen,
-                                                                               val,
-                                                                               false,
-                                                                               true,
-                                                                               true,   /* is set */
-                                                                               false);
-                       }
-                       else
-                       {
-                               lnext(expr) =
-                                       makeConst(attrtype,
-                                                         attrlen,
-                                                         (Datum) fmgr(typeid_get_retinfunc(attrtype),
-                                                                                val, get_typelem(attrtype), -1),
-                                                         false,
-                                                         true /* Maybe correct-- 80% chance */ ,
-                                                         false,        /* is not a set */
-                                                         false);
-                       }
-               }
-               else if ((Typecast_ok) && (attrtype != type_id))
-               {
-                       lnext(expr) =
-                               parser_typecast2(expr, get_id_type(attrtype));
-               }
-               else if (attrtype != type_id)
-               {
-                       if ((attrtype == INT2OID) && (type_id == INT4OID))
-                               lfirst(expr) = lispInteger(INT2OID);    /* handle CASHOID too */
-                       else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
-                               lfirst(expr) = lispInteger(FLOAT4OID);
-                       else
-                               elog(WARN, "unequal type in tlist : %s \n", colname);
-               }
-
-               Input_is_string = false;
-               Input_is_integer = false;
-               Typecast_ok = true;
-#endif
-
-               if (attrtype != type_id)
-               {
-                       if (IsA(expr, Const))
-                       {
-                               /* try to cast the constant */
-                               if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
-                               {
-                                       /* updating a single item */
-                                       Oid                     typelem = get_typelem(attrtype);
-
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                        get_id_type(typelem),
-                                                                                                        attrlen);
-                               }
-                               else
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                  get_id_type(attrtype),
-                                                                                                        attrlen);
-                       }
-                       else
-                       {
-                               /* currently, we can't handle casting of expressions */
-                               elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
-                                        colname,
-                                        get_id_typname(attrtype),
-                                        get_id_typname(type_id));
-                       }
-               }
-
-               if (arrayRef != NIL)
-               {
-                       Expr       *target_expr;
-                       Attr       *att = makeNode(Attr);
-                       List       *ar = arrayRef;
-                       List       *upperIndexpr = NIL;
-                       List       *lowerIndexpr = NIL;
-
-                       att->relname = pstrdup(RelationGetRelationName(rd)->data);
-                       att->attrs = lcons(makeString(colname), NIL);
-                       target_expr = (Expr *) handleNestedDots(pstate, att,
-                                                                                                 &pstate->p_last_resno);
-                       while (ar != NIL)
-                       {
-                               A_Indices  *ind = lfirst(ar);
-
-                               if (lowerIndexpr || (!upperIndexpr && ind->lidx))
-                               {
-
-                                       /*
-                                        * XXX assume all lowerIndexpr is non-null in this
-                                        * case
-                                        */
-                                       lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-                               }
-                               upperIndexpr = lappend(upperIndexpr, ind->uidx);
-                               ar = lnext(ar);
-                       }
-
-                       expr = (Node *) make_array_set(target_expr,
-                                                                                  upperIndexpr,
-                                                                                  lowerIndexpr,
-                                                                                  (Expr *) expr);
-                       attrtype = att_typeid(rd, resdomno);
-                       attrlen = tlen(get_id_type(attrtype));
-               }
-       }
-       else
-       {
-               resdomno = pstate->p_last_resno++;
-               attrtype = type_id;
-               attrlen = type_len;
-       }
-       tent = makeNode(TargetEntry);
-
-       resnode = makeResdom((AttrNumber) resdomno,
-                                                (Oid) attrtype,
-                                                (Size) attrlen,
-                                                colname,
-                                                (Index) 0,
-                                                (Oid) 0,
-                                                0);
-
-       tent->resdom = resnode;
-       tent->expr = expr;
-
-       return tent;
-}
-
-
-/*****************************************************************************
- *
- * Where Clause
- *
- *****************************************************************************/
-
-/*
- * transformWhereClause -
- *       transforms the qualification and make sure it is of type Boolean
- *
- */
-static Node *
-transformWhereClause(ParseState *pstate, Node *a_expr)
-{
-       Node       *qual;
-
-       if (a_expr == NULL)
-               return (Node *) NULL;   /* no qualifiers */
-
-       inWhereClause = true;
-       qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
-       inWhereClause = false;
-       if (exprType(qual) != BOOLOID)
-       {
-               elog(WARN,
-                        "where clause must return type bool, not %s",
-                        tname(get_id_type(exprType(qual))));
-       }
-       return qual;
-}
-
-/*****************************************************************************
- *
- * Sort Clause
- *
- *****************************************************************************/
-
-/*
- *     find_targetlist_entry -
- *       returns the Resdom in the target list matching the specified varname
- *       and range
- *
- */
-static TargetEntry *
-find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
-{
-       List       *i;
-       int                     real_rtable_pos = 0,
-                               target_pos = 0;
-       TargetEntry *target_result = NULL;
-
-       if (sortgroupby->range)
-               real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable,
-                                                                                               sortgroupby->range);
-
-       foreach(i, tlist)
-       {
-               TargetEntry *target = (TargetEntry *) lfirst(i);
-               Resdom     *resnode = target->resdom;
-               Var                *var = (Var *) target->expr;
-               char       *resname = resnode->resname;
-               int                     test_rtable_pos = var->varno;
-
-#ifdef PARSEDEBUG
-               printf("find_targetlist_entry- target name is %s, position %d, resno %d\n",
-                          (sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno);
-#endif
-
-               if (!sortgroupby->name)
-               {
-                       if (sortgroupby->resno == ++target_pos)
-                       {
-                               target_result = target;
-                               break;
-                       }
-               }
-               else
-               {
-                       if (!strcmp(resname, sortgroupby->name))
-                       {
-                               if (sortgroupby->range)
-                               {
-                                       if (real_rtable_pos == test_rtable_pos)
-                                       {
-                                               if (target_result != NULL)
-                                                       elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name);
-                                               else
-                                                       target_result = target;
-                                       }
-                               }
-                               else
-                               {
-                                       if (target_result != NULL)
-                                               elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name);
-                                       else
-                                               target_result = target;
-                               }
-                       }
-               }
-       }
-       return target_result;
-}
-
-static Oid
-any_ordering_op(int restype)
-{
-       Operator        order_op;
-       Oid                     order_opid;
-
-       order_op = oper("<", restype, restype, false);
-       order_opid = oprid(order_op);
-
-       return order_opid;
-}
-
-/*
- * transformGroupClause -
- *       transform a Group By clause
- *
- */
-static List *
-transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
-{
-       List       *glist = NIL,
-                          *gl = NIL;
-
-       while (grouplist != NIL)
-       {
-               GroupClause *grpcl = makeNode(GroupClause);
-               TargetEntry *restarget;
-               Resdom     *resdom;
-
-               restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist);
-
-               if (restarget == NULL)
-                       elog(WARN, "The field being grouped by must appear in the target list");
-
-               grpcl->entry = restarget;
-               resdom = restarget->resdom;
-               grpcl->grpOpoid = oprid(oper("<",
-                                                                        resdom->restype,
-                                                                        resdom->restype, false));
-               if (glist == NIL)
-                       gl = glist = lcons(grpcl, NIL);
-               else
-               {
-                       lnext(gl) = lcons(grpcl, NIL);
-                       gl = lnext(gl);
-               }
-               grouplist = lnext(grouplist);
-       }
-
-       return glist;
-}
-
-/*
- * transformSortClause -
- *       transform an Order By clause
- *
- */
-static List *
-transformSortClause(ParseState *pstate,
-                                       List *orderlist, List *targetlist,
-                                       char *uniqueFlag)
-{
-       List       *sortlist = NIL;
-       List       *s = NIL,
-                          *i;
-
-       while (orderlist != NIL)
-       {
-               SortGroupBy *sortby = lfirst(orderlist);
-               SortClause *sortcl = makeNode(SortClause);
-               TargetEntry *restarget;
-               Resdom     *resdom;
-
-               restarget = find_targetlist_entry(pstate, sortby, targetlist);
-               if (restarget == NULL)
-                       elog(WARN, "The field being ordered by must appear in the target list");
-
-               sortcl->resdom = resdom = restarget->resdom;
-               sortcl->opoid = oprid(oper(sortby->useOp,
-                                                                  resdom->restype,
-                                                                  resdom->restype, false));
-               if (sortlist == NIL)
-               {
-                       s = sortlist = lcons(sortcl, NIL);
-               }
-               else
-               {
-                       lnext(s) = lcons(sortcl, NIL);
-                       s = lnext(s);
-               }
-               orderlist = lnext(orderlist);
-       }
-
-       if (uniqueFlag)
-       {
-               if (uniqueFlag[0] == '*')
-               {
-
-                       /*
-                        * concatenate all elements from target list that are not
-                        * already in the sortby list
-                        */
-                       foreach(i, targetlist)
-                       {
-                               TargetEntry *tlelt = (TargetEntry *) lfirst(i);
-
-                               s = sortlist;
-                               while (s != NIL)
-                               {
-                                       SortClause *sortcl = lfirst(s);
-
-                                       if (sortcl->resdom == tlelt->resdom)
-                                               break;
-                                       s = lnext(s);
-                               }
-                               if (s == NIL)
-                               {
-                                       /* not a member of the sortclauses yet */
-                                       SortClause *sortcl = makeNode(SortClause);
-
-                                       sortcl->resdom = tlelt->resdom;
-                                       sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
-
-                                       sortlist = lappend(sortlist, sortcl);
-                               }
-                       }
-               }
-               else
-               {
-                       TargetEntry *tlelt = NULL;
-                       char       *uniqueAttrName = uniqueFlag;
-
-                       /* only create sort clause with the specified unique attribute */
-                       foreach(i, targetlist)
-                       {
-                               tlelt = (TargetEntry *) lfirst(i);
-                               if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0)
-                                       break;
-                       }
-                       if (i == NIL)
-                       {
-                               elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist");
-                       }
-                       s = sortlist;
-                       foreach(s, sortlist)
-                       {
-                               SortClause *sortcl = lfirst(s);
-
-                               if (sortcl->resdom == tlelt->resdom)
-                                       break;
-                       }
-                       if (s == NIL)
-                       {
-                               /* not a member of the sortclauses yet */
-                               SortClause *sortcl = makeNode(SortClause);
-
-                               sortcl->resdom = tlelt->resdom;
-                               sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
-
-                               sortlist = lappend(sortlist, sortcl);
-                       }
-               }
-
-       }
-
-       return sortlist;
-}
-
-/*
- ** HandleNestedDots --
- **    Given a nested dot expression (i.e. (relation func ... attr), build up
- ** a tree with of Iter and Func nodes.
- */
-static Node *
-handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno)
-{
-       List       *mutator_iter;
-       Node       *retval = NULL;
-
-       if (attr->paramNo != NULL)
-       {
-               Param      *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST);
-
-               retval =
-                       ParseFunc(pstate, strVal(lfirst(attr->attrs)),
-                                         lcons(param, NIL),
-                                         curr_resno);
-       }
-       else
-       {
-               Ident      *ident = makeNode(Ident);
-
-               ident->name = attr->relname;
-               ident->isRel = TRUE;
-               retval =
-                       ParseFunc(pstate, strVal(lfirst(attr->attrs)),
-                                         lcons(ident, NIL),
-                                         curr_resno);
-       }
-
-       foreach(mutator_iter, lnext(attr->attrs))
-       {
-               retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)),
-                                                  lcons(retval, NIL),
-                                                  curr_resno);
-       }
-
-       return (retval);
-}
-
-/*
- ** make_arguments --
- **   Given the number and types of arguments to a function, and the
- **   actual arguments and argument types, do the necessary typecasting.
- */
-static void
-make_arguments(int nargs,
-                          List *fargs,
-                          Oid *input_typeids,
-                          Oid *function_typeids)
-{
-
-       /*
-        * there are two ways an input typeid can differ from a function
-        * typeid : either the input type inherits the function type, so no
-        * typecasting is necessary, or the input type can be typecast into
-        * the function type. right now, we only typecast unknowns, and that
-        * is all we check for.
-        */
-
-       List       *current_fargs;
-       int                     i;
-
-       for (i = 0, current_fargs = fargs;
-                i < nargs;
-                i++, current_fargs = lnext(current_fargs))
-       {
-
-               if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid)
-               {
-                       lfirst(current_fargs) =
-                               parser_typecast2(lfirst(current_fargs),
-                                                                input_typeids[i],
-                                                                get_id_type(function_typeids[i]),
-                                                                -1);
-               }
-       }
-}
-
-/*
- ** setup_tlist --
- **            Build a tlist that says which attribute to project to.
- **            This routine is called by ParseFunc() to set up a target list
- **            on a tuple parameter or return value.  Due to a bug in 4.0,
- **            it's not possible to refer to system attributes in this case.
- */
-static List *
-setup_tlist(char *attname, Oid relid)
-{
-       TargetEntry *tle;
-       Resdom     *resnode;
-       Var                *varnode;
-       Oid                     typeid;
-       int                     attno;
-
-       attno = get_attnum(relid, attname);
-       if (attno < 0)
-               elog(WARN, "cannot reference attribute %s of tuple params/return values for functions", attname);
-
-       typeid = find_atttype(relid, attname);
-       resnode = makeResdom(1,
-                                                typeid,
-                                                tlen(get_id_type(typeid)),
-                                                get_attname(relid, attno),
-                                                0,
-                                                (Oid) 0,
-                                                0);
-       varnode = makeVar(-1, attno, typeid, -1, attno);
-
-       tle = makeNode(TargetEntry);
-       tle->resdom = resnode;
-       tle->expr = (Node *) varnode;
-       return (lcons(tle, NIL));
-}
-
-/*
- ** setup_base_tlist --
- **            Build a tlist that extracts a base type from the tuple
- **            returned by the executor.
- */
-static List *
-setup_base_tlist(Oid typeid)
-{
-       TargetEntry *tle;
-       Resdom     *resnode;
-       Var                *varnode;
-
-       resnode = makeResdom(1,
-                                                typeid,
-                                                tlen(get_id_type(typeid)),
-                                                "<noname>",
-                                                0,
-                                                (Oid) 0,
-                                                0);
-       varnode = makeVar(-1, 1, typeid, -1, 1);
-       tle = makeNode(TargetEntry);
-       tle->resdom = resnode;
-       tle->expr = (Node *) varnode;
-
-       return (lcons(tle, NIL));
-}
-
-/*
- * ParseComplexProjection -
- *       handles function calls with a single argument that is of complex type.
- *       This routine returns NULL if it can't handle the projection (eg. sets).
- */
-static Node *
-ParseComplexProjection(ParseState *pstate,
-                                          char *funcname,
-                                          Node *first_arg,
-                                          bool *attisset)
-{
-       Oid                     argtype;
-       Oid                     argrelid;
-       Name            relname;
-       Relation        rd;
-       Oid                     relid;
-       int                     attnum;
-
-       switch (nodeTag(first_arg))
-       {
-               case T_Iter:
-                       {
-                               Func       *func;
-                               Iter       *iter;
-
-                               iter = (Iter *) first_arg;
-                               func = (Func *) ((Expr *) iter->iterexpr)->oper;
-                               argtype = funcid_get_rettype(func->funcid);
-                               argrelid = typeid_get_relid(argtype);
-                               if (argrelid &&
-                                       ((attnum = get_attnum(argrelid, funcname))
-                                        != InvalidAttrNumber))
-                               {
-
-                                       /*
-                                        * the argument is a function returning a tuple, so
-                                        * funcname may be a projection
-                                        */
-
-                                       /* add a tlist to the func node and return the Iter */
-                                       rd = heap_openr(tname(get_id_type(argtype)));
-                                       if (RelationIsValid(rd))
-                                       {
-                                               relid = RelationGetRelationId(rd);
-                                               relname = RelationGetRelationName(rd);
-                                               heap_close(rd);
-                                       }
-                                       if (RelationIsValid(rd))
-                                       {
-                                               func->func_tlist =
-                                                       setup_tlist(funcname, argrelid);
-                                               iter->itertype = att_typeid(rd, attnum);
-                                               return ((Node *) iter);
-                                       }
-                                       else
-                                       {
-                                               elog(WARN,
-                                                        "Function %s has bad returntype %d",
-                                                        funcname, argtype);
-                                       }
-                               }
-                               else
-                               {
-                                       /* drop through */
-                                       ;
-                               }
-                               break;
-                       }
-               case T_Var:
-                       {
-
-                               /*
-                                * The argument is a set, so this is either a projection
-                                * or a function call on this set.
-                                */
-                               *attisset = true;
-                               break;
-                       }
-               case T_Expr:
-                       {
-                               Expr       *expr = (Expr *) first_arg;
-                               Func       *funcnode;
-
-                               if (expr->opType != FUNC_EXPR)
-                                       break;
-
-                               funcnode = (Func *) expr->oper;
-                               argtype = funcid_get_rettype(funcnode->funcid);
-                               argrelid = typeid_get_relid(argtype);
-
-                               /*
-                                * the argument is a function returning a tuple, so
-                                * funcname may be a projection
-                                */
-                               if (argrelid &&
-                                       (attnum = get_attnum(argrelid, funcname))
-                                       != InvalidAttrNumber)
-                               {
-
-                                       /* add a tlist to the func node */
-                                       rd = heap_openr(tname(get_id_type(argtype)));
-                                       if (RelationIsValid(rd))
-                                       {
-                                               relid = RelationGetRelationId(rd);
-                                               relname = RelationGetRelationName(rd);
-                                               heap_close(rd);
-                                       }
-                                       if (RelationIsValid(rd))
-                                       {
-                                               Expr       *newexpr;
-
-                                               funcnode->func_tlist =
-                                                       setup_tlist(funcname, argrelid);
-                                               funcnode->functype = att_typeid(rd, attnum);
-
-                                               newexpr = makeNode(Expr);
-                                               newexpr->typeOid = funcnode->functype;
-                                               newexpr->opType = FUNC_EXPR;
-                                               newexpr->oper = (Node *) funcnode;
-                                               newexpr->args = lcons(first_arg, NIL);
-
-                                               return ((Node *) newexpr);
-                                       }
-
-                               }
-
-                               elog(WARN, "Function %s has bad returntype %d",
-                                        funcname, argtype);
-                               break;
-                       }
-               case T_Param:
-                       {
-                               Param      *param = (Param *) first_arg;
-
-                               /*
-                                * If the Param is a complex type, this could be a
-                                * projection
-                                */
-                               rd = heap_openr(tname(get_id_type(param->paramtype)));
-                               if (RelationIsValid(rd))
-                               {
-                                       relid = RelationGetRelationId(rd);
-                                       relname = RelationGetRelationName(rd);
-                                       heap_close(rd);
-                               }
-                               if (RelationIsValid(rd) &&
-                                       (attnum = get_attnum(relid, funcname))
-                                       != InvalidAttrNumber)
-                               {
-
-                                       param->paramtype = att_typeid(rd, attnum);
-                                       param->param_tlist = setup_tlist(funcname, relid);
-                                       return ((Node *) param);
-                               }
-                               break;
-                       }
-               default:
-                       break;
-       }
-
-       return NULL;
-}
-
-static Node *
-ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
-{
-       Oid                     rettype = (Oid) 0;
-       Oid                     argrelid = (Oid) 0;
-       Oid                     funcid = (Oid) 0;
-       List       *i = NIL;
-       Node       *first_arg = NULL;
-       char       *relname = NULL;
-       char       *refname = NULL;
-       Relation        rd;
-       Oid                     relid;
-       int                     nargs;
-       Func       *funcnode;
-       Oid                     oid_array[8];
-       Oid                *true_oid_array;
-       Node       *retval;
-       bool            retset;
-       bool            exists;
-       bool            attisset = false;
-       Oid                     toid = (Oid) 0;
-       Expr       *expr;
-
-       if (fargs)
-       {
-               first_arg = lfirst(fargs);
-               if (first_arg == NULL)
-                       elog(WARN, "function %s does not allow NULL input", funcname);
-       }
-
-       /*
-        * * check for projection methods: if function takes one argument, and *
-        * that argument is a relation, param, or PQ function returning a
-        * complex * type, then the function could be a projection.
-        */
-       if (length(fargs) == 1)
-       {
-
-               if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel)
-               {
-                       RangeTblEntry *rte;
-                       Ident      *ident = (Ident *) first_arg;
-
-                       /*
-                        * first arg is a relation. This could be a projection.
-                        */
-                       refname = ident->name;
+       Query      *qry;
 
-                       rte = refnameRangeTableEntry(pstate->p_rtable, refname);
-                       if (rte == NULL)
-                               rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL);
+       qry = makeNode(Query);
+       qry->commandType = CMD_UTILITY;
 
-                       relname = rte->relname;
-                       relid = rte->relid;
+       /* take care of the where clause */
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+       qry->hasSubLinks = pstate->p_hasSubLinks;
 
-                       /*
-                        * If the attr isn't a set, just make a var for it.  If it is
-                        * a set, treat it like a function and drop through.
-                        */
-                       if (get_attnum(relid, funcname) != InvalidAttrNumber)
-                       {
-                               Oid                     dummyTypeId;
+       stmt->rangetable = pstate->p_rtable;
 
-                               return
-                                       ((Node *) make_var(pstate,
-                                                                          refname,
-                                                                          funcname,
-                                                                          &dummyTypeId));
-                       }
-                       else
-                       {
-                               /* drop through - attr is a set */
-                               ;
-                       }
-               }
-               else if (ISCOMPLEX(exprType(first_arg)))
-               {
+       qry->utilityStmt = (Node *) stmt;
+       return qry;
+}
 
-                       /*
-                        * Attempt to handle projection of a complex argument. If
-                        * ParseComplexProjection can't handle the projection, we have
-                        * to keep going.
-                        */
-                       retval = ParseComplexProjection(pstate,
-                                                                                       funcname,
-                                                                                       first_arg,
-                                                                                       &attisset);
-                       if (attisset)
-                       {
-                               toid = exprType(first_arg);
-                               rd = heap_openr(tname(get_id_type(toid)));
-                               if (RelationIsValid(rd))
-                               {
-                                       relname = RelationGetRelationName(rd)->data;
-                                       heap_close(rd);
-                               }
-                               else
-                                       elog(WARN,
-                                                "Type %s is not a relation type",
-                                                tname(get_id_type(toid)));
-                               argrelid = typeid_get_relid(toid);
+/*
+ * transformRuleStmt -
+ *       transform a Create Rule Statement. The actions is a list of parse
+ *       trees which is transformed into a list of query trees.
+ */
+static Query *
+transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
+{
+       Query      *qry;
+       Query      *action;
+       List       *actions;
 
-                               /*
-                                * A projection contains either an attribute name or the
-                                * "*".
-                                */
-                               if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
-                                       && strcmp(funcname, "*"))
-                               {
-                                       elog(WARN, "Functions on sets are not yet supported");
-                               }
-                       }
+       qry = makeNode(Query);
+       qry->commandType = CMD_UTILITY;
 
-                       if (retval)
-                               return retval;
-               }
-               else
-               {
+       /*
+        * 'instead nothing' rules with a qualification need a query a
+        * rangetable so the rewrite handler can add the negated rule
+        * qualification to the original query. We create a query with the new
+        * command type CMD_NOTHING here that is treated special by the
+        * rewrite system.
+        */
+       if (stmt->actions == NIL)
+       {
+               Query      *nothing_qry = makeNode(Query);
 
-                       /*
-                        * Parsing aggregates.
-                        */
-                       Oid                     basetype;
+               nothing_qry->commandType = CMD_NOTHING;
 
-                       /*
-                        * the aggregate count is a special case, ignore its base
-                        * type.  Treat it as zero
-                        */
-                       if (strcmp(funcname, "count") == 0)
-                               basetype = 0;
-                       else
-                               basetype = exprType(lfirst(fargs));
-                       if (SearchSysCacheTuple(AGGNAME,
-                                                                       PointerGetDatum(funcname),
-                                                                       ObjectIdGetDatum(basetype),
-                                                                       0, 0))
-                       {
-                               Aggreg     *aggreg = ParseAgg(funcname, basetype, lfirst(fargs));
+               addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
+                                                  FALSE, FALSE);
+               addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
+                                                  FALSE, FALSE);
 
-                               AddAggToParseState(pstate, aggreg);
-                               return (Node *) aggreg;
-                       }
-               }
+               nothing_qry->rtable = pstate->p_rtable;
+
+               stmt->actions = lappend(NIL, nothing_qry);
        }
 
+       actions = stmt->actions;
 
        /*
-        * * If we dropped through to here it's really a function (or a set,
-        * which * is implemented as a function.) * extract arg type info and
-        * transform relation name arguments into * varnodes of the
-        * appropriate form.
+        * transform each statment, like parse_analyze()
         */
-       memset(&oid_array[0], 0, 8 * sizeof(Oid));
-
-       nargs = 0;
-       foreach(i, fargs)
+       while (actions != NIL)
        {
-               int                     vnum;
-               RangeTblEntry *rte;
-               Node       *pair = lfirst(i);
 
-               if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel)
-               {
+               /*
+                * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
+                * equal to 2.
+                */
+               addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
+                                                  FALSE, FALSE);
+               addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
+                                                  FALSE, FALSE);
 
-                       /*
-                        * a relation
-                        */
-                       refname = ((Ident *) pair)->name;
+               pstate->p_last_resno = 1;
+               pstate->p_is_rule = true;               /* for expand all */
+               pstate->p_hasAggs = false;
 
-                       rte = refnameRangeTableEntry(pstate->p_rtable, refname);
-                       if (rte == NULL)
-                               rte = addRangeTableEntry(pstate, refname, refname,
-                                                                                FALSE, FALSE, NULL);
-                       relname = rte->relname;
+               action = (Query *) lfirst(actions);
+               if (action->commandType != CMD_NOTHING)
+                       lfirst(actions) = transformStmt(pstate, lfirst(actions));
+               actions = lnext(actions);
+       }
 
-                       vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname);
+       /* take care of the where clause */
+       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+       qry->hasSubLinks = pstate->p_hasSubLinks;
 
-                       /*
-                        * for func(relname), the param to the function is the tuple
-                        * under consideration.  we build a special VarNode to reflect
-                        * this -- it has varno set to the correct range table entry,
-                        * but has varattno == 0 to signal that the whole tuple is the
-                        * argument.
-                        */
-                       toid = typeid(type(relname));
-                       /* replace it in the arg list */
-                       lfirst(fargs) =
-                               makeVar(vnum, 0, toid, vnum, 0);
-               }
-               else if (!attisset)
-               {                                               /* set functions don't have parameters */
+       qry->utilityStmt = (Node *) stmt;
+       return qry;
+}
 
-                       /*
-                        * any functiona args which are typed "unknown", but aren't
-                        * constants, we don't know what to do with, because we can't
-                        * cast them    - jolly
-                        */
-                       if (exprType(pair) == UNKNOWNOID &&
-                               !IsA(pair, Const))
-                       {
-                               elog(WARN, "ParseFunc: no function named %s that takes in an unknown type as argument #%d", funcname, nargs);
-                       }
-                       else
-                               toid = exprType(pair);
-               }
 
-               oid_array[nargs++] = toid;
-       }
+/*
+ * transformSelectStmt -
+ *       transforms a Select Statement
+ *
+ */
+static Query *
+transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
+{
+       Query      *qry = makeNode(Query);
+       Node       *fromQual;
 
-       /*
-        * func_get_detail looks up the function in the catalogs, does
-        * disambiguation for polymorphic functions, handles inheritance, and
-        * returns the funcid and type and set or singleton status of the
-        * function's return value.  it also returns the true argument types
-        * to the function.  if func_get_detail returns true, the function
-        * exists.      otherwise, there was an error.
-        */
-       if (attisset)
-       {                                                       /* we know all of these fields already */
+       qry->commandType = CMD_SELECT;
 
-               /*
-                * We create a funcnode with a placeholder function SetEval.
-                * SetEval() never actually gets executed.      When the function
-                * evaluation routines see it, they use the funcid projected out
-                * from the relation as the actual function to call. Example:
-                * retrieve (emp.mgr.name) The plan for this will scan the emp
-                * relation, projecting out the mgr attribute, which is a funcid.
-                * This function is then called (instead of SetEval) and "name" is
-                * projected from its result.
-                */
-               funcid = SetEvalRegProcedure;
-               rettype = toid;
-               retset = true;
-               true_oid_array = oid_array;
-               exists = true;
-       }
-       else
-       {
-               exists = func_get_detail(funcname, nargs, oid_array, &funcid,
-                                                                &rettype, &retset, &true_oid_array);
-       }
+       /* set up a range table */
+       makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
-       if (!exists)
-               elog(WARN, "no such attribute or function %s", funcname);
+       qry->uniqueFlag = stmt->unique;
+
+       qry->into = stmt->into;
+       qry->isTemp = stmt->istemp;
+       qry->isPortal = FALSE;
 
-       /* got it */
-       funcnode = makeNode(Func);
-       funcnode->funcid = funcid;
-       funcnode->functype = rettype;
-       funcnode->funcisindex = false;
-       funcnode->funcsize = 0;
-       funcnode->func_fcache = NULL;
-       funcnode->func_tlist = NIL;
-       funcnode->func_planlist = NIL;
+       qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-       /* perform the necessary typecasting */
-       make_arguments(nargs, fargs, oid_array, true_oid_array);
+       qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-       /*
-        * for functions returning base types, we want to project out the
-        * return value.  set up a target list to do that.      the executor will
-        * ignore these for c functions, and do the right thing for postquel
-        * functions.
+       /* 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->groupClause = transformGroupClause(pstate,
+                                                                                       stmt->groupClause,
+                                                                                       qry->targetList);
+
+       qry->sortClause = transformSortClause(pstate,
+                                                                                 stmt->sortClause,
+                                                                                 qry->targetList,
+                                                                                 qry->uniqueFlag);
 
-       if (typeid_get_relid(rettype) == InvalidOid)
-               funcnode->func_tlist = setup_base_tlist(rettype);
+       qry->hasSubLinks = pstate->p_hasSubLinks;
+       qry->hasAggs = pstate->p_hasAggs;
+       if (pstate->p_hasAggs || qry->groupClause)
+               parseCheckAggregates(pstate, qry);
 
        /*
-        * For sets, we want to make a targetlist to project out this
-        * attribute of the set tuples.
+        * 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 (attisset)
-       {
-               if (!strcmp(funcname, "*"))
-               {
-                       funcnode->func_tlist =
-                               expandAll(pstate, relname, refname, curr_resno);
-               }
-               else
-               {
-                       funcnode->func_tlist = setup_tlist(funcname, argrelid);
-                       rettype = find_atttype(argrelid, funcname);
-               }
-       }
+       if (qry->havingQual && ! qry->hasAggs)
+               elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
 
        /*
-        * Sequence handling.
+        * The INSERT INTO ... SELECT ... could have a UNION in child, so
+        * unionClause may be false
         */
-       if (funcid == SeqNextValueRegProcedure ||
-               funcid == SeqCurrValueRegProcedure)
-       {
-               Const      *seq;
-               char       *seqrel;
-               int32           aclcheck_result = -1;
-
-               Assert(length(fargs) == 1);
-               seq = (Const *) lfirst(fargs);
-               if (!IsA((Node *) seq, Const))
-                       elog(WARN, "%s: only constant sequence names are acceptable", funcname);
-               seqrel = textout((struct varlena *) (seq->constvalue));
-
-               if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(),
-                          ((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD)))
-                       != ACLCHECK_OK)
-                       elog(WARN, "%s.%s: %s",
-                         seqrel, funcname, aclcheck_error_strings[aclcheck_result]);
-
-               pfree(seqrel);
-
-               if (funcid == SeqNextValueRegProcedure && inWhereClause)
-                       elog(WARN, "nextval of a sequence in WHERE disallowed");
-       }
-
-       expr = makeNode(Expr);
-       expr->typeOid = rettype;
-       expr->opType = FUNC_EXPR;
-       expr->oper = (Node *) funcnode;
-       expr->args = fargs;
-       retval = (Node *) expr;
+       qry->unionall = stmt->unionall;
 
        /*
-        * if the function returns a set of values, then we need to iterate
-        * over all the returned values in the executor, so we stick an iter
-        * node here.  if it returns a singleton, then we don't need the iter
-        * node.
+        * 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 (retset)
-       {
-               Iter       *iter = makeNode(Iter);
+       qry->rtable = pstate->p_rtable;
 
-               iter->itertype = rettype;
-               iter->iterexpr = retval;
-               retval = (Node *) iter;
-       }
+       if (stmt->forUpdate != NULL)
+               transformForUpdate(qry, stmt->forUpdate);
 
-       return (retval);
+       return (Query *) qry;
 }
 
-/*****************************************************************************
- *
- *****************************************************************************/
-
 /*
- * AddAggToParseState -
- *       add the aggregate to the list of unique aggregates in pstate.
+ * transformUpdateStmt -
+ *       transforms an update statement
  *
- * SIDE EFFECT: aggno in target list entry will be modified
  */
-static void
-AddAggToParseState(ParseState *pstate, Aggreg *aggreg)
+static Query *
+transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
-       List       *ag;
-       int                     i;
+       Query      *qry = makeNode(Query);
+       List       *origTargetList;
+       List       *tl;
+
+       qry->commandType = CMD_UPDATE;
+       pstate->p_is_update = true;
 
        /*
-        * see if we have the aggregate already (we only need to record the
-        * aggregate once)
+        * 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.
         */
-       i = 0;
-       foreach(ag, pstate->p_aggs)
-       {
-               Aggreg     *a = lfirst(ag);
+       makeRangeTable(pstate, stmt->fromClause, NULL);
+       setTargetTable(pstate, stmt->relname);
 
-               if (!strcmp(a->aggname, aggreg->aggname) &&
-                       equal(a->target, aggreg->target))
-               {
+       qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-                       /* fill in the aggno and we're done */
-                       aggreg->aggno = i;
-                       return;
-               }
-               i++;
-       }
+       qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
 
-       /* not found, new aggregate */
-       aggreg->aggno = i;
-       pstate->p_numAgg++;
-       pstate->p_aggs = lappend(pstate->p_aggs, aggreg);
-       return;
-}
+       qry->hasSubLinks = pstate->p_hasSubLinks;
 
-/*
- * finalizeAggregates -
- *       fill in qry_aggs from pstate. Also checks to make sure that aggregates
- *       are used in the proper place.
- */
-static void
-finalizeAggregates(ParseState *pstate, Query *qry)
-{
-       List       *l;
-       int                     i;
+       qry->rtable = pstate->p_rtable;
+       qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
-       parseCheckAggregates(pstate, qry);
+       qry->hasAggs = pstate->p_hasAggs;
+       if (pstate->p_hasAggs)
+               parseCheckAggregates(pstate, qry);
 
-       qry->qry_numAgg = pstate->p_numAgg;
-       qry->qry_aggs =
-               (Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg);
-       i = 0;
-       foreach(l, pstate->p_aggs)
-               qry->qry_aggs[i++] = (Aggreg *) lfirst(l);
-}
+       /*
+        * Now we are done with SELECT-like processing, and can get on with
+        * transforming the target list to match the UPDATE target columns.
+        */
 
-/*
- * contain_agg_clause--
- *       Recursively find aggreg nodes from a clause.
- *
- *       Returns true if any aggregate found.
- */
-static bool
-contain_agg_clause(Node *clause)
-{
-       if (clause == NULL)
-               return FALSE;
-       else if (IsA(clause, Aggreg))
-               return TRUE;
-       else if (IsA(clause, Iter))
-               return contain_agg_clause(((Iter *) clause)->iterexpr);
-       else if (single_node(clause))
-               return FALSE;
-       else if (or_clause(clause))
-       {
-               List       *temp;
+       /* 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;
 
-               foreach(temp, ((Expr *) clause)->args)
-                       if (contain_agg_clause(lfirst(temp)))
-                       return TRUE;
-               return FALSE;
-       }
-       else if (is_funcclause(clause))
+       /* Prepare non-junk columns for assignment to target table */
+       origTargetList = stmt->targetList;
+       foreach(tl, qry->targetList)
        {
-               List       *temp;
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Resdom     *resnode = tle->resdom;
+               ResTarget  *origTarget;
 
-               foreach(temp, ((Expr *) clause)->args)
-                       if (contain_agg_clause(lfirst(temp)))
-                       return TRUE;
-               return FALSE;
-       }
-       else if (IsA(clause, ArrayRef))
-       {
-               List       *temp;
-
-               foreach(temp, ((ArrayRef *) clause)->refupperindexpr)
-                       if (contain_agg_clause(lfirst(temp)))
-                       return TRUE;
-               foreach(temp, ((ArrayRef *) clause)->reflowerindexpr)
-                       if (contain_agg_clause(lfirst(temp)))
-                       return TRUE;
-               if (contain_agg_clause(((ArrayRef *) clause)->refexpr))
-                       return TRUE;
-               if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr))
-                       return TRUE;
-               return FALSE;
+               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);
        }
-       else if (not_clause(clause))
-               return contain_agg_clause((Node *) get_notclausearg((Expr *) clause));
-       else if (is_opclause(clause))
-               return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) ||
-                         contain_agg_clause((Node *) get_rightop((Expr *) clause)));
+       if (origTargetList != NIL)
+               elog(ERROR, "UPDATE target count mismatch --- internal error");
 
-       return FALSE;
+       return (Query *) qry;
 }
 
 /*
- * exprIsAggOrGroupCol -
- *       returns true if the expression does not contain non-group columns.
+ * transformCursorStmt -
+ *       transform a Create Cursor Statement
+ *
  */
-static bool
-exprIsAggOrGroupCol(Node *expr, List *groupClause)
+static Query *
+transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
 {
-       List       *gl;
+       Query      *qry;
 
-       if (expr == NULL || IsA(expr, Const) ||
-               IsA(expr, Param) ||IsA(expr, Aggreg))
-               return TRUE;
+       qry = transformSelectStmt(pstate, stmt);
 
-       foreach(gl, groupClause)
-       {
-               GroupClause *grpcl = lfirst(gl);
+       qry->into = stmt->portalname;
+       qry->isTemp = stmt->istemp;
+       qry->isPortal = TRUE;
+       qry->isBinary = stmt->binary;           /* internal portal */
 
-               if (equal(expr, grpcl->entry->expr))
-                       return TRUE;
-       }
+       return qry;
+}
 
-       if (IsA(expr, Expr))
+/* 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
+create_select_list(Node *ptr, List **select_list, bool *unionall_present)
+{
+       if (IsA(ptr, SelectStmt))
        {
-               List       *temp;
-
-               foreach(temp, ((Expr *) expr)->args)
-                       if (!exprIsAggOrGroupCol(lfirst(temp), groupClause))
-                       return FALSE;
-               return TRUE;
+               *select_list = lappend(*select_list, ptr);
+               if (((SelectStmt *) ptr)->unionall == TRUE)
+                       *unionall_present = TRUE;
+               return;
        }
 
-       return FALSE;
+       /* 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);
 }
 
-/*
- * tleIsAggOrGroupCol -
- *       returns true if the TargetEntry is Agg or GroupCol.
- */
-static bool
-tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
+/* 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
+ * 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
+ * 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)
 {
-       Node       *expr = tle->expr;
-       List       *gl;
-
-       if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param))
-               return TRUE;
+       Node       *result = NULL;
 
-       foreach(gl, groupClause)
+       switch (nodeTag(ptr))
        {
-               GroupClause *grpcl = lfirst(gl);
+               case T_A_Expr:
+                       {
+                               A_Expr     *a = (A_Expr *) ptr;
 
-               if (tle->resdom->resno == grpcl->entry->resdom->resno)
-               {
-                       if (contain_agg_clause((Node *) expr))
-                               elog(WARN, "parser: aggregates not allowed in GROUP BY clause");
-                       return TRUE;
-               }
-       }
+                               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);
 
-       if (IsA(expr, Aggreg))
-               return TRUE;
+                                                       *intersect_present = TRUE;
 
-       if (IsA(expr, Expr))
-       {
-               List       *temp;
+                                                       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);
 
-               foreach(temp, ((Expr *) expr)->args)
-                       if (!exprIsAggOrGroupCol(lfirst(temp), groupClause))
-                       return FALSE;
-               return TRUE;
+                                                       expr->typeOid = BOOLOID;
+                                                       expr->opType = NOT_EXPR;
+                                                       expr->args = makeList(rexpr, -1);
+                                                       result = (Node *) expr;
+                                                       break;
+                                               }
+                               }
+                               break;
+                       }
+               default:
+                       result = ptr;
        }
+       return result;
+}
 
-       return FALSE;
+void
+CheckSelectForUpdate(Query *qry)
+{
+       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");
 }
 
-/*
- * parseCheckAggregates -
- *       this should really be done earlier but the current grammar
- *       cannot differentiate functions from aggregates. So we have do check
- *       here when the target list and the qualifications are finalized.
- */
 static void
-parseCheckAggregates(ParseState *pstate, Query *qry)
+transformForUpdate(Query *qry, List *forUpdate)
 {
-       List       *tl;
+       List       *rowMark = NULL;
+       RowMark    *newrm;
+       List       *l;
+       Index           i;
 
-       Assert(pstate->p_numAgg > 0);
+       CheckSelectForUpdate(qry);
 
-       /*
-        * aggregates never appear in WHERE clauses. (we have to check where
-        * clause first because if there is an aggregate, the check for
-        * non-group column in target list may fail.)
-        */
-       if (contain_agg_clause(qry->qual))
-               elog(WARN, "parser: aggregates not allowed in WHERE clause");
+       if (lfirst(forUpdate) == NULL)          /* all tables */
+       {
+               i = 1;
+               foreach(l, qry->rtable)
+               {
+                       newrm = makeNode(RowMark);
+                       newrm->rti = i++;
+                       newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
+                       rowMark = lappend(rowMark, newrm);
+               }
+               qry->rowMark = nconc(qry->rowMark, rowMark);
+               return;
+       }
 
-       /*
-        * the target list can only contain aggregates, group columns and
-        * functions thereof.
-        */
-       foreach(tl, qry->targetList)
+       foreach(l, forUpdate)
        {
-               TargetEntry *tle = lfirst(tl);
+               List       *l2;
+               List       *l3;
 
-               if (!tleIsAggOrGroupCol(tle, qry->groupClause))
-                       elog(WARN,
-                                "parser: illegal use of aggregates or non-group column in target list");
+               i = 1;
+               foreach(l2, qry->rtable)
+               {
+                       if (strcmp(((RangeTblEntry *) lfirst(l2))->refname, lfirst(l)) == 0)
+                       {
+                               foreach(l3, rowMark)
+                               {
+                                       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;
+                                       rowMark = lappend(rowMark, newrm);
+                               }
+                               break;
+                       }
+                       i++;
+               }
+               if (l2 == NULL)
+                       elog(ERROR, "FOR UPDATE: relation %s not found in FROM clause", lfirst(l));
        }
 
-       /*
-        * the expression specified in the HAVING clause has the same
-        * restriction as those in the target list.
-        */
-/*
- * Need to change here when we get HAVING works. Currently
- * qry->havingQual is NULL.            - vadim 04/05/97
-       if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause))
-               elog(WARN,
-                        "parser: illegal use of aggregates or non-group column in HAVING clause");
- */
+       qry->rowMark = rowMark;
        return;
 }