Separate parse-analysis for utility commands out of parser/analyze.c
authorTom Lane
Sat, 23 Jun 2007 22:12:52 +0000 (22:12 +0000)
committerTom Lane
Sat, 23 Jun 2007 22:12:52 +0000 (22:12 +0000)
(which now deals only in optimizable statements), and put that code
into a new file parser/parse_utilcmd.c.  This helps clarify and enforce
the design rule that utility statements shouldn't be processed during
the regular parse analysis phase; all interpretation of their meaning
should happen after they are given to ProcessUtility to execute.
(We need this because we don't retain any locks for a utility statement
that's in a plan cache, nor have any way to detect that it's stale.)

We are also able to simplify the API for parse_analyze() and related
routines, because they will now always return exactly one Query structure.

In passing, fix bug #3403 concerning trying to add a serial column to
an existing temp table (this is largely Heikki's work, but we needed
all that restructuring to make it safe).

23 files changed:
src/backend/commands/indexcmds.c
src/backend/commands/prepare.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/view.c
src/backend/nodes/makefuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/Makefile
src/backend/parser/README
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/parser/parse_utilcmd.c [new file with mode: 0644]
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/include/nodes/makefuncs.h
src/include/nodes/parsenodes.h
src/include/parser/analyze.h
src/include/parser/parse_node.h
src/include/parser/parse_utilcmd.h [new file with mode: 0644]

index 24cb898b6a0795fd5c2ceb230a34d300dee99ee3..98dad7371334ba93ca74ea51eb60846155108c7a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.159 2007/06/03 17:06:16 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.160 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -367,7 +367,7 @@ DefineIndex(RangeVar *heapRelation,
                /*
                 * This shouldn't happen during CREATE TABLE, but can happen
                 * during ALTER TABLE.  Keep message in sync with
-                * transformIndexConstraints() in parser/analyze.c.
+                * transformIndexConstraints() in parser/parse_utilcmd.c.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_COLUMN),
index de999a3637656f98017668a9fbba17f2876c615a..38055997faaf936ad36dec42106df312adc54702 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.76 2007/05/25 17:54:25 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.77 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,6 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
 {
    Oid        *argtypes = NULL;
    int         nargs;
-   List       *queries;
    Query      *query;
    List       *query_list,
               *plan_list;
@@ -105,9 +104,9 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
     * Because parse analysis scribbles on the raw querytree, we must make
     * a copy to ensure we have a pristine raw tree to cache.  FIXME someday.
     */
-   queries = parse_analyze_varparams((Node *) copyObject(stmt->query),
-                                     queryString,
-                                     &argtypes, &nargs);
+   query = parse_analyze_varparams((Node *) copyObject(stmt->query),
+                                   queryString,
+                                   &argtypes, &nargs);
 
    /*
     * Check that all parameter types were determined.
@@ -124,15 +123,8 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
    }
 
    /*
-    * Shouldn't get any extra statements, since grammar only allows
-    * OptimizableStmt
+    * grammar only allows OptimizableStmt, so this check should be redundant
     */
-   if (list_length(queries) != 1)
-       elog(ERROR, "unexpected extra stuff in prepared statement");
-
-   query = (Query *) linitial(queries);
-   Assert(IsA(query, Query));
-
    switch (query->commandType)
    {
        case CMD_SELECT:
index 5a03c7780f397e646f17d4614df2ca94b7e50adb..b103667935f6e31155893496d28e6e0ea72cd6bd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.45 2007/03/23 19:53:51 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.46 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,7 @@
 #include "commands/dbcommands.h"
 #include "commands/schemacmds.h"
 #include "miscadmin.h"
-#include "parser/analyze.h"
+#include "parser/parse_utilcmd.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -111,39 +111,31 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
    /*
     * Examine the list of commands embedded in the CREATE SCHEMA command, and
     * reorganize them into a sequentially executable order with no forward
-    * references.  Note that the result is still a list of raw parsetrees in
-    * need of parse analysis --- we cannot, in general, run analyze.c on one
-    * statement until we have actually executed the prior ones.
+    * references.  Note that the result is still a list of raw parsetrees
+    * --- we cannot, in general, run parse analysis on one statement until
+    * we have actually executed the prior ones.
     */
-   parsetree_list = analyzeCreateSchemaStmt(stmt);
+   parsetree_list = transformCreateSchemaStmt(stmt);
 
    /*
-    * Analyze and execute each command contained in the CREATE SCHEMA
+    * Execute each command contained in the CREATE SCHEMA.  Since the
+    * grammar allows only utility commands in CREATE SCHEMA, there is
+    * no need to pass them through parse_analyze() or the rewriter;
+    * we can just hand them straight to ProcessUtility.
     */
    foreach(parsetree_item, parsetree_list)
    {
-       Node       *parsetree = (Node *) lfirst(parsetree_item);
-       List       *querytree_list;
-       ListCell   *querytree_item;
-
-       querytree_list = parse_analyze(parsetree, queryString, NULL, 0);
-
-       foreach(querytree_item, querytree_list)
-       {
-           Query      *querytree = (Query *) lfirst(querytree_item);
-
-           /* schemas should contain only utility stmts */
-           Assert(querytree->commandType == CMD_UTILITY);
-           /* do this step */
-           ProcessUtility(querytree->utilityStmt,
-                          queryString,
-                          NULL,
-                          false,               /* not top level */
-                          None_Receiver,
-                          NULL);
-           /* make sure later steps can see the object created here */
-           CommandCounterIncrement();
-       }
+       Node       *stmt = (Node *) lfirst(parsetree_item);
+
+       /* do this step */
+       ProcessUtility(stmt,
+                      queryString,
+                      NULL,
+                      false,               /* not top level */
+                      None_Receiver,
+                      NULL);
+       /* make sure later steps can see the object created here */
+       CommandCounterIncrement();
    }
 
    /* Reset search path to normal state */
index b9bebde8f10f58906765eac2efa72f60d2ffeaef..f50b59d0d8fa8dab7d4d9886fdc9f997c49c94cc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.227 2007/06/03 22:16:03 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.228 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,6 @@
 #include "optimizer/clauses.h"
 #include "optimizer/plancat.h"
 #include "optimizer/prep.h"
-#include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
@@ -52,6 +51,7 @@
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
+#include "parser/parse_utilcmd.h"
 #include "parser/parser.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
@@ -394,7 +394,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
                add_nonduplicate_constraint(cdef, check, &ncheck);
        }
        /*
-        * analyze.c might have passed some precooked constraints too,
+        * parse_utilcmd.c might have passed some precooked constraints too,
         * due to LIKE tab INCLUDING CONSTRAINTS
         */
        foreach(listptr, stmt->constraints)
@@ -2922,7 +2922,7 @@ find_composite_type_dependencies(Oid typeOid,
  *
  * Adds an additional attribute to a relation making the assumption that
  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
- * AT_AddColumn AlterTableCmd by analyze.c and added as independent
+ * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
  * AlterTableCmd's.
  */
 static void
@@ -3745,9 +3745,9 @@ ATExecDropColumn(Relation rel, const char *colName,
 /*
  * ALTER TABLE ADD INDEX
  *
- * There is no such command in the grammar, but the parser converts UNIQUE
- * and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets us
- * schedule creation of the index at the appropriate time during ALTER.
+ * There is no such command in the grammar, but parse_utilcmd.c converts
+ * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
+ * us schedule creation of the index at the appropriate time during ALTER.
  */
 static void
 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
@@ -3766,13 +3766,8 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    /* suppress notices when rebuilding existing index */
    quiet = is_rebuild;
 
-   /*
-    * Run parse analysis.  We don't have convenient access to the query text
-    * here, but it's probably not worth worrying about.
-    */
-   stmt = analyzeIndexStmt(stmt, NULL);
+   /* The IndexStmt has already been through transformIndexStmt */
 
-   /* ... and do it */
    DefineIndex(stmt->relation, /* relation */
                stmt->idxname,  /* index name */
                InvalidOid,     /* no predefined OID */
@@ -3806,7 +3801,7 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
                /*
                 * Currently, we only expect to see CONSTR_CHECK nodes
                 * arriving here (see the preprocessing done in
-                * parser/analyze.c).  Use a switch anyway to make it easier
+                * parse_utilcmd.c).  Use a switch anyway to make it easier
                 * to add more code later.
                 */
                switch (constr->contype)
@@ -5239,17 +5234,27 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
    ListCell   *list_item;
 
    /*
-    * We expect that we only have to do raw parsing and parse analysis, not
-    * any rule rewriting, since these will all be utility statements.
+    * We expect that we will get only ALTER TABLE and CREATE INDEX statements.
+    * Hence, there is no need to pass them through parse_analyze() or the
+    * rewriter, but instead we need to pass them through parse_utilcmd.c
+    * to make them ready for execution.
     */
    raw_parsetree_list = raw_parser(cmd);
    querytree_list = NIL;
    foreach(list_item, raw_parsetree_list)
    {
-       Node       *parsetree = (Node *) lfirst(list_item);
-
-       querytree_list = list_concat(querytree_list,
-                                    parse_analyze(parsetree, cmd, NULL, 0));
+       Node       *stmt = (Node *) lfirst(list_item);
+
+       if (IsA(stmt, IndexStmt))
+           querytree_list = lappend(querytree_list,
+                                    transformIndexStmt((IndexStmt *) stmt,
+                                                       cmd));
+       else if (IsA(stmt, AlterTableStmt))
+           querytree_list = list_concat(querytree_list,
+                           transformAlterTableStmt((AlterTableStmt *) stmt,
+                                                   cmd));
+       else
+           querytree_list = lappend(querytree_list, stmt);
    }
 
    /*
@@ -5258,17 +5263,15 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
     */
    foreach(list_item, querytree_list)
    {
-       Query      *query = (Query *) lfirst(list_item);
+       Node       *stm = (Node *) lfirst(list_item);
        Relation    rel;
        AlteredTableInfo *tab;
 
-       Assert(IsA(query, Query));
-       Assert(query->commandType == CMD_UTILITY);
-       switch (nodeTag(query->utilityStmt))
+       switch (nodeTag(stm))
        {
            case T_IndexStmt:
                {
-                   IndexStmt  *stmt = (IndexStmt *) query->utilityStmt;
+                   IndexStmt  *stmt = (IndexStmt *) stm;
                    AlterTableCmd *newcmd;
 
                    rel = relation_openrv(stmt->relation, AccessExclusiveLock);
@@ -5283,7 +5286,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
                }
            case T_AlterTableStmt:
                {
-                   AlterTableStmt *stmt = (AlterTableStmt *) query->utilityStmt;
+                   AlterTableStmt *stmt = (AlterTableStmt *) stm;
                    ListCell   *lcmd;
 
                    rel = relation_openrv(stmt->relation, AccessExclusiveLock);
@@ -5313,7 +5316,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
                }
            default:
                elog(ERROR, "unexpected statement type: %d",
-                    (int) nodeTag(query->utilityStmt));
+                    (int) nodeTag(stm));
        }
    }
 }
index 83f26f73ffb20b9ea69085e4efad74d1d283af5c..f8dac126439ac6f1857c8389471e84b352a68bb9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.100 2007/03/13 00:33:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.101 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -351,7 +351,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 void
 DefineView(ViewStmt *stmt, const char *queryString)
 {
-   List       *stmts;
    Query      *viewParse;
    Oid         viewOid;
    RangeVar   *view;
@@ -363,15 +362,12 @@ DefineView(ViewStmt *stmt, const char *queryString)
     * Since parse analysis scribbles on its input, copy the raw parse tree;
     * this ensures we don't corrupt a prepared statement, for example.
     */
-   stmts = parse_analyze((Node *) copyObject(stmt->query),
-                         queryString, NULL, 0);
+   viewParse = parse_analyze((Node *) copyObject(stmt->query),
+                             queryString, NULL, 0);
 
    /*
     * The grammar should ensure that the result is a single SELECT Query.
     */
-   if (list_length(stmts) != 1)
-       elog(ERROR, "unexpected parse analysis result");
-   viewParse = (Query *) linitial(stmts);
    if (!IsA(viewParse, Query) ||
        viewParse->commandType != CMD_SELECT)
        elog(ERROR, "unexpected parse analysis result");
index 9b7f42e463175b35aea9c1dc414339d3fcfebc8f..d9d61d4f0bbff614df963b64980b4843d5eec432 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.55 2007/03/17 00:11:03 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.56 2007/06/23 22:12:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,6 +134,20 @@ flatCopyTargetEntry(TargetEntry *src_tle)
    return tle;
 }
 
+/*
+ * makeFromExpr -
+ *   creates a FromExpr node
+ */
+FromExpr *
+makeFromExpr(List *fromlist, Node *quals)
+{
+   FromExpr   *f = makeNode(FromExpr);
+
+   f->fromlist = fromlist;
+   f->quals = quals;
+   return f;
+}
+
 /*
  * makeConst -
  *   creates a Const node
index 2cf0ffd28b05e98f77e0f0a8e0b0190390acf37b..41215446a353a2f1fd3b617ec5b32c54b1a946db 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.246 2007/06/11 01:16:23 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.247 2007/06/23 22:12:50 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -2910,7 +2910,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
    MemoryContext mycxt;
    ErrorContextCallback sqlerrcontext;
    List       *raw_parsetree_list;
-   List       *querytree_list;
    Query      *querytree;
    Node       *newexpr;
    int        *usecounts;
@@ -2986,13 +2985,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
    if (list_length(raw_parsetree_list) != 1)
        goto fail;
 
-   querytree_list = parse_analyze(linitial(raw_parsetree_list), src,
-                                  argtypes, funcform->pronargs);
-
-   if (list_length(querytree_list) != 1)
-       goto fail;
-
-   querytree = (Query *) linitial(querytree_list);
+   querytree = parse_analyze(linitial(raw_parsetree_list), src,
+                             argtypes, funcform->pronargs);
 
    /*
     * The single command must be a simple "SELECT expression".
@@ -3025,7 +3019,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
     * no rewriting was needed; that's probably not important, but let's be
     * careful.
     */
-   if (check_sql_fn_retval(funcid, result_type, querytree_list, NULL))
+   if (check_sql_fn_retval(funcid, result_type, list_make1(querytree), NULL))
        goto fail;              /* reject whole-tuple-result cases */
 
    /*
index 3099f77ca64faeb9978ef575f173f59770a2864e..2296dcb6206fce559e5729dbc81ea0977363e68e 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for parser
 #
-# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.44 2006/05/27 17:38:45 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.45 2007/06/23 22:12:51 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,7 +14,7 @@ override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
 
 OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_clause.o \
       parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \
-      parse_type.o parse_coerce.o parse_target.o scansup.o
+      parse_type.o parse_coerce.o parse_target.o parse_utilcmd.o scansup.o
 
 FLEXFLAGS = -CF
 
index effa9008fa9c923a8423f04a21701307e9c645b7..35a7a687636c0d58d3c3bffcf0010033be3f1f08 100644 (file)
@@ -1,20 +1,21 @@
 This directory does more than tokenize and parse SQL queries.  It also
-creates Query structures for the various complex queries that is passed
+creates Query structures for the various complex queries that are passed
 to the optimizer and then executor.
 
 parser.c   things start here
 scan.l     break query into tokens
-scansup.c  handle escapes in input
+scansup.c  handle escapes in input strings
 keywords.c turn keywords into specific tokens
 gram.y     parse the tokens and fill query-type-specific structures
-analyze.c  handle post-parse processing for each query type
+analyze.c  top level of parse analysis for optimizable queries
 parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...
-parse_coerce.c used for coercing expressions of different types
+parse_coerce.c handle coercing expressions to different types
 parse_expr.c   handle expressions like col, col + 3, x = 3 or x = 4
-parse_oper.c   handle operations in expressions
+parse_oper.c   handle operators in expressions
 parse_agg.c    handle aggregates, like SUM(col1),  AVG(col2), ...
 parse_func.c   handle functions, table.column and column identifiers
 parse_node.c   create nodes for various structures
 parse_target.c handle the result list of the query
 parse_relation.c support routines for tables and column handling
 parse_type.c   support routines for type handling
+parse_utilcmd.c    parse analysis for utility commands (done at execution time)
index a676bf11025425a529cabbc9be18cb11c3a43144..3135d8524675545d3d0cc5542b9538065fc890db 100644 (file)
  * utility commands, no locks are obtained here (and if they were, we could
  * not be sure we'd still have them at execution).  Hence the general rule
  * for utility commands is to just dump them into a Query node untransformed.
- * parse_analyze does do some purely syntactic transformations on CREATE TABLE
- * and ALTER TABLE, but that's about it.  In cases where this module contains
- * mechanisms that are useful for utility statements, we provide separate
- * subroutines that should be called at the beginning of utility execution;
- * an example is analyzeIndexStmt.
+ * DECLARE CURSOR and EXPLAIN are exceptions because they contain
+ * optimizable statements.
  *
  *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.366 2007/06/20 18:21:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.367 2007/06/23 22:12:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "access/heapam.h"
-#include "catalog/heap.h"
-#include "catalog/index.h"
-#include "catalog/namespace.h"
 #include "catalog/pg_type.h"
-#include "commands/defrem.h"
-#include "commands/prepare.h"
-#include "commands/tablecmds.h"
-#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/var.h"
 #include "parser/analyze.h"
-#include "parser/gramparse.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
-#include "parser/parse_type.h"
 #include "parser/parsetree.h"
-#include "rewrite/rewriteManip.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
 
 
-/* State shared by transformCreateSchemaStmt and its subroutines */
-typedef struct
-{
-   const char *stmtType;       /* "CREATE SCHEMA" or "ALTER SCHEMA" */
-   char       *schemaname;     /* name of schema */
-   char       *authid;         /* owner of schema */
-   List       *sequences;      /* CREATE SEQUENCE items */
-   List       *tables;         /* CREATE TABLE items */
-   List       *views;          /* CREATE VIEW items */
-   List       *indexes;        /* CREATE INDEX items */
-   List       *triggers;       /* CREATE TRIGGER items */
-   List       *grants;         /* GRANT items */
-   List       *fwconstraints;  /* Forward referencing FOREIGN KEY constraints */
-   List       *alters;         /* Generated ALTER items (from the above) */
-   List       *ixconstraints;  /* index-creating constraints */
-   List       *blist;          /* "before list" of things to do before
-                                * creating the schema */
-   List       *alist;          /* "after list" of things to do after creating
-                                * the schema */
-} CreateSchemaStmtContext;
-
-/* State shared by transformCreateStmt and its subroutines */
-typedef struct
-{
-   const char *stmtType;       /* "CREATE TABLE" or "ALTER TABLE" */
-   RangeVar   *relation;       /* relation to create */
-   List       *inhRelations;   /* relations to inherit from */
-   bool        hasoids;        /* does relation have an OID column? */
-   bool        isalter;        /* true if altering existing table */
-   List       *columns;        /* ColumnDef items */
-   List       *ckconstraints;  /* CHECK constraints */
-   List       *fkconstraints;  /* FOREIGN KEY constraints */
-   List       *ixconstraints;  /* index-creating constraints */
-   List       *blist;          /* "before list" of things to do before
-                                * creating the table */
-   List       *alist;          /* "after list" of things to do after creating
-                                * the table */
-   IndexStmt  *pkey;           /* PRIMARY KEY index, if any */
-} CreateStmtContext;
-
 typedef struct
 {
    Oid        *paramTypes;
@@ -103,50 +45,24 @@ typedef struct
 } check_parameter_resolution_context;
 
 
-static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
-static Query *transformStmt(ParseState *pstate, Node *stmt,
-             List **extras_before, List **extras_after);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
-static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
-                   List **extras_before, List **extras_after);
+static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static List *transformInsertRow(ParseState *pstate, List *exprlist,
                   List *stmtcols, List *icolumns, List *attrnos);
-static List *transformReturningList(ParseState *pstate, List *returningList);
 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
 static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
+static void getSetColTypes(ParseState *pstate, Node *node,
+              List **colTypes, List **colTypmods);
+static void applyColumnNames(List *dst, List *src);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static List *transformReturningList(ParseState *pstate, List *returningList);
 static Query *transformDeclareCursorStmt(ParseState *pstate,
                           DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
                           ExplainStmt *stmt);
-static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
-                   List **extras_before, List **extras_after);
-static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
-                       List **extras_before, List **extras_after);
-static void transformColumnDefinition(ParseState *pstate,
-                         CreateStmtContext *cxt,
-                         ColumnDef *column);
-static void transformTableConstraint(ParseState *pstate,
-                        CreateStmtContext *cxt,
-                        Constraint *constraint);
-static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
-                    InhRelation *inhrelation);
-static void transformIndexConstraints(ParseState *pstate,
-                         CreateStmtContext *cxt);
-static void transformFKConstraints(ParseState *pstate,
-                      CreateStmtContext *cxt,
-                      bool skipValidation,
-                      bool isAddConstraint);
-static void applyColumnNames(List *dst, List *src);
-static void getSetColTypes(ParseState *pstate, Node *node,
-              List **colTypes, List **colTypmods);
 static void transformLockingClause(Query *qry, LockingClause *lc);
-static void transformConstraintAttrs(List *constraintList);
-static void transformColumnType(ParseState *pstate, ColumnDef *column);
-static void release_pstate_resources(ParseState *pstate);
-static FromExpr *makeFromExpr(List *fromlist, Node *quals);
 static bool check_parameter_resolution_walker(Node *node,
                                check_parameter_resolution_context *context);
 
@@ -161,28 +77,27 @@ static bool check_parameter_resolution_walker(Node *node,
  * Optionally, information about $n parameter types can be supplied.
  * References to $n indexes not defined by paramTypes[] are disallowed.
  *
- * The result is a List of Query nodes (we need a list since some commands
- * produce multiple Queries).  Optimizable statements require considerable
- * transformation, while most utility-type statements are simply hung off
+ * The result is a Query node.  Optimizable statements require considerable
+ * transformation, while utility-type statements are simply hung off
  * a dummy CMD_UTILITY Query node.
  */
-List *
+Query *
 parse_analyze(Node *parseTree, const char *sourceText,
              Oid *paramTypes, int numParams)
 {
    ParseState *pstate = make_parsestate(NULL);
-   List       *result;
+   Query      *query;
 
    pstate->p_sourcetext = sourceText;
    pstate->p_paramtypes = paramTypes;
    pstate->p_numparams = numParams;
    pstate->p_variableparams = false;
 
-   result = do_parse_analyze(parseTree, pstate);
+   query = transformStmt(pstate, parseTree);
 
-   pfree(pstate);
+   free_parsestate(pstate);
 
-   return result;
+   return query;
 }
 
 /*
@@ -192,24 +107,24 @@ parse_analyze(Node *parseTree, const char *sourceText,
  * symbol datatypes from context.  The passed-in paramTypes[] array can
  * be modified or enlarged (via repalloc).
  */
-List *
+Query *
 parse_analyze_varparams(Node *parseTree, const char *sourceText,
                        Oid **paramTypes, int *numParams)
 {
    ParseState *pstate = make_parsestate(NULL);
-   List       *result;
+   Query      *query;
 
    pstate->p_sourcetext = sourceText;
    pstate->p_paramtypes = *paramTypes;
    pstate->p_numparams = *numParams;
    pstate->p_variableparams = true;
 
-   result = do_parse_analyze(parseTree, pstate);
+   query = transformStmt(pstate, parseTree);
 
    *paramTypes = pstate->p_paramtypes;
    *numParams = pstate->p_numparams;
 
-   pfree(pstate);
+   free_parsestate(pstate);
 
    /* make sure all is well with parameter types */
    if (*numParams > 0)
@@ -218,100 +133,37 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
 
        context.paramTypes = *paramTypes;
        context.numParams = *numParams;
-       check_parameter_resolution_walker((Node *) result, &context);
+       check_parameter_resolution_walker((Node *) query, &context);
    }
 
-   return result;
+   return query;
 }
 
 /*
  * parse_sub_analyze
  *     Entry point for recursively analyzing a sub-statement.
  */
-List *
+Query *
 parse_sub_analyze(Node *parseTree, ParseState *parentParseState)
 {
    ParseState *pstate = make_parsestate(parentParseState);
-   List       *result;
-
-   result = do_parse_analyze(parseTree, pstate);
-
-   pfree(pstate);
-
-   return result;
-}
-
-/*
- * do_parse_analyze
- *     Workhorse code shared by the above variants of parse_analyze.
- */
-static List *
-do_parse_analyze(Node *parseTree, ParseState *pstate)
-{
-   List       *result = NIL;
-
-   /* Lists to return extra commands from transformation */
-   List       *extras_before = NIL;
-   List       *extras_after = NIL;
    Query      *query;
-   ListCell   *l;
-
-   query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
 
-   /* don't need to access result relation any more */
-   release_pstate_resources(pstate);
+   query = transformStmt(pstate, parseTree);
 
-   foreach(l, extras_before)
-       result = list_concat(result, parse_sub_analyze(lfirst(l), pstate));
+   free_parsestate(pstate);
 
-   result = lappend(result, query);
-
-   foreach(l, extras_after)
-       result = list_concat(result, parse_sub_analyze(lfirst(l), pstate));
-
-   /*
-    * Make sure that only the original query is marked original. We have to
-    * do this explicitly since recursive calls of do_parse_analyze will have
-    * marked some of the added-on queries as "original".  Also mark only the
-    * original query as allowed to set the command-result tag.
-    */
-   foreach(l, result)
-   {
-       Query      *q = lfirst(l);
-
-       if (q == query)
-       {
-           q->querySource = QSRC_ORIGINAL;
-           q->canSetTag = true;
-       }
-       else
-       {
-           q->querySource = QSRC_PARSER;
-           q->canSetTag = false;
-       }
-   }
-
-   return result;
-}
-
-static void
-release_pstate_resources(ParseState *pstate)
-{
-   if (pstate->p_target_relation != NULL)
-       heap_close(pstate->p_target_relation, NoLock);
-   pstate->p_target_relation = NULL;
-   pstate->p_target_rangetblentry = NULL;
+   return query;
 }
 
 /*
  * transformStmt -
  *   transform a Parse tree into a Query tree.
  */
-static Query *
-transformStmt(ParseState *pstate, Node *parseTree,
-             List **extras_before, List **extras_after)
+Query *
+transformStmt(ParseState *pstate, Node *parseTree)
 {
-   Query      *result = NULL;
+   Query      *result;
 
    switch (nodeTag(parseTree))
    {
@@ -319,8 +171,7 @@ transformStmt(ParseState *pstate, Node *parseTree,
             * Optimizable statements
             */
        case T_InsertStmt:
-           result = transformInsertStmt(pstate, (InsertStmt *) parseTree,
-                                        extras_before, extras_after);
+           result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
            break;
 
        case T_DeleteStmt:
@@ -344,20 +195,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
            }
            break;
 
-           /*
-            * Non-optimizable statements
-            */
-       case T_CreateStmt:
-           result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
-                                        extras_before, extras_after);
-           break;
-
-       case T_AlterTableStmt:
-           result = transformAlterTableStmt(pstate,
-                                            (AlterTableStmt *) parseTree,
-                                            extras_before, extras_after);
-           break;
-
            /*
             * Special cases
             */
@@ -387,17 +224,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
    result->querySource = QSRC_ORIGINAL;
    result->canSetTag = true;
 
-   /*
-    * Check that we did not produce too many resnos; at the very least we
-    * cannot allow more than 2^16, since that would exceed the range of a
-    * AttrNumber. It seems safest to use MaxTupleAttributeNumber.
-    */
-   if (pstate->p_next_resno - 1 > MaxTupleAttributeNumber)
-       ereport(ERROR,
-               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                errmsg("target lists can have at most %d entries",
-                       MaxTupleAttributeNumber)));
-
    return result;
 }
 
@@ -450,8 +276,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
  *   transform an Insert Statement
  */
 static Query *
-transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
-                   List **extras_before, List **extras_after)
+transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
    Query      *qry = makeNode(Query);
    SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
@@ -552,15 +377,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
        sub_pstate->p_relnamespace = sub_relnamespace;
        sub_pstate->p_varnamespace = sub_varnamespace;
 
-       /*
-        * Note: we are not expecting that extras_before and extras_after are
-        * going to be used by the transformation of the SELECT statement.
-        */
-       selectQuery = transformStmt(sub_pstate, stmt->selectStmt,
-                                   extras_before, extras_after);
+       selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
 
-       release_pstate_resources(sub_pstate);
-       pfree(sub_pstate);
+       free_parsestate(sub_pstate);
 
        /* The grammar should have produced a SELECT, but it might have INTO */
        Assert(IsA(selectQuery, Query));
@@ -776,1275 +595,77 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 
    /* done building the range table and jointree */
    qry->rtable = pstate->p_rtable;
-   qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-
-   qry->hasSubLinks = pstate->p_hasSubLinks;
-   /* aggregates not allowed (but subselects are okay) */
-   if (pstate->p_hasAggs)
-       ereport(ERROR,
-               (errcode(ERRCODE_GROUPING_ERROR),
-                errmsg("cannot use aggregate function in VALUES")));
-
-   return qry;
-}
-
-/*
- * Prepare an INSERT row for assignment to the target table.
- *
- * The row might be either a VALUES row, or variables referencing a
- * sub-SELECT output.
- */
-static List *
-transformInsertRow(ParseState *pstate, List *exprlist,
-                  List *stmtcols, List *icolumns, List *attrnos)
-{
-   List       *result;
-   ListCell   *lc;
-   ListCell   *icols;
-   ListCell   *attnos;
-
-   /*
-    * Check length of expr list.  It must not have more expressions than
-    * there are target columns.  We allow fewer, but only if no explicit
-    * columns list was given (the remaining columns are implicitly
-    * defaulted).  Note we must check this *after* transformation because
-    * that could expand '*' into multiple items.
-    */
-   if (list_length(exprlist) > list_length(icolumns))
-       ereport(ERROR,
-               (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("INSERT has more expressions than target columns")));
-   if (stmtcols != NIL &&
-       list_length(exprlist) < list_length(icolumns))
-       ereport(ERROR,
-               (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("INSERT has more target columns than expressions")));
-
-   /*
-    * Prepare columns for assignment to target table.
-    */
-   result = NIL;
-   icols = list_head(icolumns);
-   attnos = list_head(attrnos);
-   foreach(lc, exprlist)
-   {
-       Expr       *expr = (Expr *) lfirst(lc);
-       ResTarget  *col;
-
-       col = (ResTarget *) lfirst(icols);
-       Assert(IsA(col, ResTarget));
-
-       expr = transformAssignedExpr(pstate, expr,
-                                    col->name,
-                                    lfirst_int(attnos),
-                                    col->indirection,
-                                    col->location);
-
-       result = lappend(result, expr);
-
-       icols = lnext(icols);
-       attnos = lnext(attnos);
-   }
-
-   return result;
-}
-
-/*
- * 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 *
-transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
-                   List **extras_before, List **extras_after)
-{
-   CreateStmtContext cxt;
-   Query      *q;
-   ListCell   *elements;
-
-   cxt.stmtType = "CREATE TABLE";
-   cxt.relation = stmt->relation;
-   cxt.inhRelations = stmt->inhRelations;
-   cxt.isalter = false;
-   cxt.columns = NIL;
-   cxt.ckconstraints = NIL;
-   cxt.fkconstraints = NIL;
-   cxt.ixconstraints = NIL;
-   cxt.blist = NIL;
-   cxt.alist = NIL;
-   cxt.pkey = NULL;
-   cxt.hasoids = interpretOidsOption(stmt->options);
-
-   /*
-    * Run through each primary element in the table creation clause. Separate
-    * column defs from constraints, and do preliminary analysis.
-    */
-   foreach(elements, stmt->tableElts)
-   {
-       Node       *element = lfirst(elements);
-
-       switch (nodeTag(element))
-       {
-           case T_ColumnDef:
-               transformColumnDefinition(pstate, &cxt,
-                                         (ColumnDef *) element);
-               break;
-
-           case T_Constraint:
-               transformTableConstraint(pstate, &cxt,
-                                        (Constraint *) element);
-               break;
-
-           case T_FkConstraint:
-               /* No pre-transformation needed */
-               cxt.fkconstraints = lappend(cxt.fkconstraints, element);
-               break;
-
-           case T_InhRelation:
-               transformInhRelation(pstate, &cxt,
-                                    (InhRelation *) element);
-               break;
-
-           default:
-               elog(ERROR, "unrecognized node type: %d",
-                    (int) nodeTag(element));
-               break;
-       }
-   }
-
-   /*
-    * transformIndexConstraints wants cxt.alist to contain only index
-    * statements, so transfer anything we already have into extras_after
-    * immediately.
-    */
-   *extras_after = list_concat(cxt.alist, *extras_after);
-   cxt.alist = NIL;
-
-   Assert(stmt->constraints == NIL);
-
-   /*
-    * Postprocess constraints that give rise to index definitions.
-    */
-   transformIndexConstraints(pstate, &cxt);
-
-   /*
-    * Postprocess foreign-key constraints.
-    */
-   transformFKConstraints(pstate, &cxt, true, false);
-
-   /*
-    * Output results.
-    */
-   q = makeNode(Query);
-   q->commandType = CMD_UTILITY;
-   q->utilityStmt = (Node *) stmt;
-   stmt->tableElts = cxt.columns;
-   stmt->constraints = cxt.ckconstraints;
-   *extras_before = list_concat(*extras_before, cxt.blist);
-   *extras_after = list_concat(cxt.alist, *extras_after);
-
-   return q;
-}
-
-static void
-transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
-                         ColumnDef *column)
-{
-   bool        is_serial;
-   bool        saw_nullable;
-   bool        saw_default;
-   Constraint *constraint;
-   ListCell   *clist;
-
-   cxt->columns = lappend(cxt->columns, column);
-
-   /* Check for SERIAL pseudo-types */
-   is_serial = false;
-   if (list_length(column->typename->names) == 1)
-   {
-       char       *typname = strVal(linitial(column->typename->names));
-
-       if (strcmp(typname, "serial") == 0 ||
-           strcmp(typname, "serial4") == 0)
-       {
-           is_serial = true;
-           column->typename->names = NIL;
-           column->typename->typeid = INT4OID;
-       }
-       else if (strcmp(typname, "bigserial") == 0 ||
-                strcmp(typname, "serial8") == 0)
-       {
-           is_serial = true;
-           column->typename->names = NIL;
-           column->typename->typeid = INT8OID;
-       }
-   }
-
-   /* Do necessary work on the column type declaration */
-   transformColumnType(pstate, column);
-
-   /* Special actions for SERIAL pseudo-types */
-   if (is_serial)
-   {
-       Oid         snamespaceid;
-       char       *snamespace;
-       char       *sname;
-       char       *qstring;
-       A_Const    *snamenode;
-       FuncCall   *funccallnode;
-       CreateSeqStmt *seqstmt;
-       AlterSeqStmt *altseqstmt;
-       List       *attnamelist;
-
-       /*
-        * Determine namespace and name to use for the sequence.
-        *
-        * Although we use ChooseRelationName, it's not guaranteed that the
-        * selected sequence name won't conflict; given sufficiently long
-        * field names, two different serial columns in the same table could
-        * be assigned the same sequence name, and we'd not notice since we
-        * aren't creating the sequence quite yet.  In practice this seems
-        * quite unlikely to be a problem, especially since few people would
-        * need two serial columns in one table.
-        */
-       snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
-       snamespace = get_namespace_name(snamespaceid);
-       sname = ChooseRelationName(cxt->relation->relname,
-                                  column->colname,
-                                  "seq",
-                                  snamespaceid);
-
-       ereport(NOTICE,
-               (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
-                       cxt->stmtType, sname,
-                       cxt->relation->relname, column->colname)));
-
-       /*
-        * Build a CREATE SEQUENCE command to create the sequence object, and
-        * add it to the list of things to be done before this CREATE/ALTER
-        * TABLE.
-        */
-       seqstmt = makeNode(CreateSeqStmt);
-       seqstmt->sequence = makeRangeVar(snamespace, sname);
-       seqstmt->options = NIL;
-
-       cxt->blist = lappend(cxt->blist, seqstmt);
-
-       /*
-        * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
-        * as owned by this column, and add it to the list of things to be
-        * done after this CREATE/ALTER TABLE.
-        */
-       altseqstmt = makeNode(AlterSeqStmt);
-       altseqstmt->sequence = makeRangeVar(snamespace, sname);
-       attnamelist = list_make3(makeString(snamespace),
-                                makeString(cxt->relation->relname),
-                                makeString(column->colname));
-       altseqstmt->options = list_make1(makeDefElem("owned_by",
-                                                    (Node *) attnamelist));
-
-       cxt->alist = lappend(cxt->alist, altseqstmt);
-
-       /*
-        * Create appropriate constraints for SERIAL.  We do this in full,
-        * rather than shortcutting, so that we will detect any conflicting
-        * constraints the user wrote (like a different DEFAULT).
-        *
-        * Create an expression tree representing the function call
-        * nextval('sequencename').  We cannot reduce the raw tree to cooked
-        * form until after the sequence is created, but there's no need to do
-        * so.
-        */
-       qstring = quote_qualified_identifier(snamespace, sname);
-       snamenode = makeNode(A_Const);
-       snamenode->val.type = T_String;
-       snamenode->val.val.str = qstring;
-       snamenode->typename = SystemTypeName("regclass");
-       funccallnode = makeNode(FuncCall);
-       funccallnode->funcname = SystemFuncName("nextval");
-       funccallnode->args = list_make1(snamenode);
-       funccallnode->agg_star = false;
-       funccallnode->agg_distinct = false;
-       funccallnode->location = -1;
-
-       constraint = makeNode(Constraint);
-       constraint->contype = CONSTR_DEFAULT;
-       constraint->raw_expr = (Node *) funccallnode;
-       constraint->cooked_expr = NULL;
-       constraint->keys = NIL;
-       column->constraints = lappend(column->constraints, constraint);
-
-       constraint = makeNode(Constraint);
-       constraint->contype = CONSTR_NOTNULL;
-       column->constraints = lappend(column->constraints, constraint);
-   }
-
-   /* Process column constraints, if any... */
-   transformConstraintAttrs(column->constraints);
-
-   saw_nullable = false;
-   saw_default = false;
-
-   foreach(clist, column->constraints)
-   {
-       constraint = lfirst(clist);
-
-       /*
-        * If this column constraint is a FOREIGN KEY constraint, then we fill
-        * in the current attribute's name and throw it into the list of FK
-        * constraints to be processed later.
-        */
-       if (IsA(constraint, FkConstraint))
-       {
-           FkConstraint *fkconstraint = (FkConstraint *) constraint;
-
-           fkconstraint->fk_attrs = list_make1(makeString(column->colname));
-           cxt->fkconstraints = lappend(cxt->fkconstraints, fkconstraint);
-           continue;
-       }
-
-       Assert(IsA(constraint, Constraint));
-
-       switch (constraint->contype)
-       {
-           case CONSTR_NULL:
-               if (saw_nullable && column->is_not_null)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_SYNTAX_ERROR),
-                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
-                                 column->colname, cxt->relation->relname)));
-               column->is_not_null = FALSE;
-               saw_nullable = true;
-               break;
-
-           case CONSTR_NOTNULL:
-               if (saw_nullable && !column->is_not_null)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_SYNTAX_ERROR),
-                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
-                                 column->colname, cxt->relation->relname)));
-               column->is_not_null = TRUE;
-               saw_nullable = true;
-               break;
-
-           case CONSTR_DEFAULT:
-               if (saw_default)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_SYNTAX_ERROR),
-                            errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
-                                 column->colname, cxt->relation->relname)));
-               /* Note: DEFAULT NULL maps to constraint->raw_expr == NULL */
-               column->raw_default = constraint->raw_expr;
-               Assert(constraint->cooked_expr == NULL);
-               saw_default = true;
-               break;
-
-           case CONSTR_PRIMARY:
-           case CONSTR_UNIQUE:
-               if (constraint->keys == NIL)
-                   constraint->keys = list_make1(makeString(column->colname));
-               cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
-               break;
-
-           case CONSTR_CHECK:
-               cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
-               break;
-
-           case CONSTR_ATTR_DEFERRABLE:
-           case CONSTR_ATTR_NOT_DEFERRABLE:
-           case CONSTR_ATTR_DEFERRED:
-           case CONSTR_ATTR_IMMEDIATE:
-               /* transformConstraintAttrs took care of these */
-               break;
-
-           default:
-               elog(ERROR, "unrecognized constraint type: %d",
-                    constraint->contype);
-               break;
-       }
-   }
-}
-
-static void
-transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
-                        Constraint *constraint)
-{
-   switch (constraint->contype)
-   {
-       case CONSTR_PRIMARY:
-       case CONSTR_UNIQUE:
-           cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
-           break;
-
-       case CONSTR_CHECK:
-           cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
-           break;
-
-       case CONSTR_NULL:
-       case CONSTR_NOTNULL:
-       case CONSTR_DEFAULT:
-       case CONSTR_ATTR_DEFERRABLE:
-       case CONSTR_ATTR_NOT_DEFERRABLE:
-       case CONSTR_ATTR_DEFERRED:
-       case CONSTR_ATTR_IMMEDIATE:
-           elog(ERROR, "invalid context for constraint type %d",
-                constraint->contype);
-           break;
-
-       default:
-           elog(ERROR, "unrecognized constraint type: %d",
-                constraint->contype);
-           break;
-   }
-}
-
-/*
- * transformInhRelation
- *
- * Change the LIKE  portion of a CREATE TABLE statement into
- * column definitions which recreate the user defined column portions of
- * .
- *
- * Note: because we do this at parse analysis time, any change in the
- * referenced table between parse analysis and execution won't be reflected
- * into the new table.  Is this OK?
- */
-static void
-transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
-                    InhRelation *inhRelation)
-{
-   AttrNumber  parent_attno;
-   Relation    relation;
-   TupleDesc   tupleDesc;
-   TupleConstr *constr;
-   AclResult   aclresult;
-   bool        including_defaults = false;
-   bool        including_constraints = false;
-   bool        including_indexes = false;
-   ListCell   *elem;
-
-   relation = heap_openrv(inhRelation->relation, AccessShareLock);
-
-   if (relation->rd_rel->relkind != RELKIND_RELATION)
-       ereport(ERROR,
-               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("inherited relation \"%s\" is not a table",
-                       inhRelation->relation->relname)));
-
-   /*
-    * Check for SELECT privilages
-    */
-   aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(),
-                                 ACL_SELECT);
-   if (aclresult != ACLCHECK_OK)
-       aclcheck_error(aclresult, ACL_KIND_CLASS,
-                      RelationGetRelationName(relation));
-
-   tupleDesc = RelationGetDescr(relation);
-   constr = tupleDesc->constr;
-
-   foreach(elem, inhRelation->options)
-   {
-       int         option = lfirst_int(elem);
-
-       switch (option)
-       {
-           case CREATE_TABLE_LIKE_INCLUDING_DEFAULTS:
-               including_defaults = true;
-               break;
-           case CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS:
-               including_defaults = false;
-               break;
-           case CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS:
-               including_constraints = true;
-               break;
-           case CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS:
-               including_constraints = false;
-               break;
-           case CREATE_TABLE_LIKE_INCLUDING_INDEXES:
-               including_indexes = true;
-               break;
-           case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
-               including_indexes = false;
-               break;
-           default:
-               elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
-                    option);
-       }
-   }
-
-   if (including_indexes)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("LIKE INCLUDING INDEXES is not implemented")));
-
-   /*
-    * Insert the copied attributes into the cxt for the new table
-    * definition.
-    */
-   for (parent_attno = 1; parent_attno <= tupleDesc->natts;
-        parent_attno++)
-   {
-       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
-       char       *attributeName = NameStr(attribute->attname);
-       ColumnDef  *def;
-
-       /*
-        * Ignore dropped columns in the parent.
-        */
-       if (attribute->attisdropped)
-           continue;
-
-       /*
-        * Create a new column, which is marked as NOT inherited.
-        *
-        * For constraints, ONLY the NOT NULL constraint is inherited by the
-        * new column definition per SQL99.
-        */
-       def = makeNode(ColumnDef);
-       def->colname = pstrdup(attributeName);
-       def->typename = makeTypeNameFromOid(attribute->atttypid,
-                                           attribute->atttypmod);
-       def->inhcount = 0;
-       def->is_local = true;
-       def->is_not_null = attribute->attnotnull;
-       def->raw_default = NULL;
-       def->cooked_default = NULL;
-       def->constraints = NIL;
-
-       /*
-        * Add to column list
-        */
-       cxt->columns = lappend(cxt->columns, def);
-
-       /*
-        * Copy default, if present and the default has been requested
-        */
-       if (attribute->atthasdef && including_defaults)
-       {
-           char       *this_default = NULL;
-           AttrDefault *attrdef;
-           int         i;
-
-           /* Find default in constraint structure */
-           Assert(constr != NULL);
-           attrdef = constr->defval;
-           for (i = 0; i < constr->num_defval; i++)
-           {
-               if (attrdef[i].adnum == parent_attno)
-               {
-                   this_default = attrdef[i].adbin;
-                   break;
-               }
-           }
-           Assert(this_default != NULL);
-
-           /*
-            * If default expr could contain any vars, we'd need to fix 'em,
-            * but it can't; so default is ready to apply to child.
-            */
-
-           def->cooked_default = pstrdup(this_default);
-       }
-   }
-
-   /*
-    * Copy CHECK constraints if requested, being careful to adjust
-    * attribute numbers
-    */
-   if (including_constraints && tupleDesc->constr)
-   {
-       AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
-       int         ccnum;
-
-       for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
-       {
-           char       *ccname = tupleDesc->constr->check[ccnum].ccname;
-           char       *ccbin = tupleDesc->constr->check[ccnum].ccbin;
-           Node       *ccbin_node = stringToNode(ccbin);
-           Constraint *n = makeNode(Constraint);
-
-           change_varattnos_of_a_node(ccbin_node, attmap);
-
-           n->contype = CONSTR_CHECK;
-           n->name = pstrdup(ccname);
-           n->raw_expr = NULL;
-           n->cooked_expr = nodeToString(ccbin_node);
-           n->indexspace = NULL;
-           cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
-       }
-   }
-
-   /*
-    * Close the parent rel, but keep our AccessShareLock on it until xact
-    * commit.  That will prevent someone else from deleting or ALTERing the
-    * parent before the child is committed.
-    */
-   heap_close(relation, NoLock);
-}
-
-static void
-transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
-{
-   IndexStmt  *index;
-   List       *indexlist = NIL;
-   ListCell   *listptr;
-   ListCell   *l;
-
-   /*
-    * Run through the constraints that need to generate an index. For PRIMARY
-    * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
-    * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
-    */
-   foreach(listptr, cxt->ixconstraints)
-   {
-       Constraint *constraint = lfirst(listptr);
-       ListCell   *keys;
-       IndexElem  *iparam;
-
-       Assert(IsA(constraint, Constraint));
-       Assert((constraint->contype == CONSTR_PRIMARY)
-              || (constraint->contype == CONSTR_UNIQUE));
-
-       index = makeNode(IndexStmt);
-
-       index->unique = true;
-       index->primary = (constraint->contype == CONSTR_PRIMARY);
-       if (index->primary)
-       {
-           if (cxt->pkey != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                        errmsg("multiple primary keys for table \"%s\" are not allowed",
-                               cxt->relation->relname)));
-           cxt->pkey = index;
-
-           /*
-            * In ALTER TABLE case, a primary index might already exist, but
-            * DefineIndex will check for it.
-            */
-       }
-       index->isconstraint = true;
-
-       if (constraint->name != NULL)
-           index->idxname = pstrdup(constraint->name);
-       else
-           index->idxname = NULL;      /* DefineIndex will choose name */
-
-       index->relation = cxt->relation;
-       index->accessMethod = DEFAULT_INDEX_TYPE;
-       index->options = constraint->options;
-       index->tableSpace = constraint->indexspace;
-       index->indexParams = NIL;
-       index->whereClause = NULL;
-       index->concurrent = false;
-
-       /*
-        * Make sure referenced keys exist.  If we are making a PRIMARY KEY
-        * index, also make sure they are NOT NULL, if possible. (Although we
-        * could leave it to DefineIndex to mark the columns NOT NULL, it's
-        * more efficient to get it right the first time.)
-        */
-       foreach(keys, constraint->keys)
-       {
-           char       *key = strVal(lfirst(keys));
-           bool        found = false;
-           ColumnDef  *column = NULL;
-           ListCell   *columns;
-
-           foreach(columns, cxt->columns)
-           {
-               column = (ColumnDef *) lfirst(columns);
-               Assert(IsA(column, ColumnDef));
-               if (strcmp(column->colname, key) == 0)
-               {
-                   found = true;
-                   break;
-               }
-           }
-           if (found)
-           {
-               /* found column in the new table; force it to be NOT NULL */
-               if (constraint->contype == CONSTR_PRIMARY)
-                   column->is_not_null = TRUE;
-           }
-           else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
-           {
-               /*
-                * column will be a system column in the new table, so accept
-                * it.  System columns can't ever be null, so no need to worry
-                * about PRIMARY/NOT NULL constraint.
-                */
-               found = true;
-           }
-           else if (cxt->inhRelations)
-           {
-               /* try inherited tables */
-               ListCell   *inher;
-
-               foreach(inher, cxt->inhRelations)
-               {
-                   RangeVar   *inh = (RangeVar *) lfirst(inher);
-                   Relation    rel;
-                   int         count;
-
-                   Assert(IsA(inh, RangeVar));
-                   rel = heap_openrv(inh, AccessShareLock);
-                   if (rel->rd_rel->relkind != RELKIND_RELATION)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                          errmsg("inherited relation \"%s\" is not a table",
-                                 inh->relname)));
-                   for (count = 0; count < rel->rd_att->natts; count++)
-                   {
-                       Form_pg_attribute inhattr = rel->rd_att->attrs[count];
-                       char       *inhname = NameStr(inhattr->attname);
-
-                       if (inhattr->attisdropped)
-                           continue;
-                       if (strcmp(key, inhname) == 0)
-                       {
-                           found = true;
-
-                           /*
-                            * We currently have no easy way to force an
-                            * inherited column to be NOT NULL at creation, if
-                            * its parent wasn't so already. We leave it to
-                            * DefineIndex to fix things up in this case.
-                            */
-                           break;
-                       }
-                   }
-                   heap_close(rel, NoLock);
-                   if (found)
-                       break;
-               }
-           }
-
-           /*
-            * In the ALTER TABLE case, don't complain about index keys not
-            * created in the command; they may well exist already.
-            * DefineIndex will complain about them if not, and will also take
-            * care of marking them NOT NULL.
-            */
-           if (!found && !cxt->isalter)
-               ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                        errmsg("column \"%s\" named in key does not exist",
-                               key)));
-
-           /* Check for PRIMARY KEY(foo, foo) */
-           foreach(columns, index->indexParams)
-           {
-               iparam = (IndexElem *) lfirst(columns);
-               if (iparam->name && strcmp(key, iparam->name) == 0)
-               {
-                   if (index->primary)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                errmsg("column \"%s\" appears twice in primary key constraint",
-                                       key)));
-                   else
-                       ereport(ERROR,
-                               (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                errmsg("column \"%s\" appears twice in unique constraint",
-                                       key)));
-               }
-           }
-
-           /* OK, add it to the index definition */
-           iparam = makeNode(IndexElem);
-           iparam->name = pstrdup(key);
-           iparam->expr = NULL;
-           iparam->opclass = NIL;
-           iparam->ordering = SORTBY_DEFAULT;
-           iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
-           index->indexParams = lappend(index->indexParams, iparam);
-       }
-
-       indexlist = lappend(indexlist, index);
-   }
-
-   /*
-    * Scan the index list and remove any redundant index specifications. This
-    * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
-    * strict reading of SQL92 would suggest raising an error instead, but
-    * that strikes me as too anal-retentive. - tgl 2001-02-14
-    *
-    * XXX in ALTER TABLE case, it'd be nice to look for duplicate
-    * pre-existing indexes, too.  However, that seems to risk race
-    * conditions since we can't be sure the command will be executed
-    * immediately.
-    */
-   Assert(cxt->alist == NIL);
-   if (cxt->pkey != NULL)
-   {
-       /* Make sure we keep the PKEY index in preference to others... */
-       cxt->alist = list_make1(cxt->pkey);
-   }
-
-   foreach(l, indexlist)
-   {
-       bool        keep = true;
-       ListCell   *k;
-
-       index = lfirst(l);
-
-       /* if it's pkey, it's already in cxt->alist */
-       if (index == cxt->pkey)
-           continue;
-
-       foreach(k, cxt->alist)
-       {
-           IndexStmt  *priorindex = lfirst(k);
-
-           if (equal(index->indexParams, priorindex->indexParams))
-           {
-               /*
-                * If the prior index is as yet unnamed, and this one is
-                * named, then transfer the name to the prior index. This
-                * ensures that if we have named and unnamed constraints,
-                * we'll use (at least one of) the names for the index.
-                */
-               if (priorindex->idxname == NULL)
-                   priorindex->idxname = index->idxname;
-               keep = false;
-               break;
-           }
-       }
-
-       if (keep)
-           cxt->alist = lappend(cxt->alist, index);
-   }
-}
-
-static void
-transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
-                      bool skipValidation, bool isAddConstraint)
-{
-   ListCell   *fkclist;
-
-   if (cxt->fkconstraints == NIL)
-       return;
-
-   /*
-    * If CREATE TABLE or adding a column with NULL default, we can safely
-    * skip validation of the constraint.
-    */
-   if (skipValidation)
-   {
-       foreach(fkclist, cxt->fkconstraints)
-       {
-           FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
-
-           fkconstraint->skip_validation = true;
-       }
-   }
-
-   /*
-    * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD
-    * CONSTRAINT command to execute after the basic command is complete. (If
-    * called from ADD CONSTRAINT, that routine will add the FK constraints to
-    * its own subcommand list.)
-    *
-    * Note: the ADD CONSTRAINT command must also execute after any index
-    * creation commands.  Thus, this should run after
-    * transformIndexConstraints, so that the CREATE INDEX commands are
-    * already in cxt->alist.
-    */
-   if (!isAddConstraint)
-   {
-       AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
-
-       alterstmt->relation = cxt->relation;
-       alterstmt->cmds = NIL;
-       alterstmt->relkind = OBJECT_TABLE;
-
-       foreach(fkclist, cxt->fkconstraints)
-       {
-           FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
-           AlterTableCmd *altercmd = makeNode(AlterTableCmd);
-
-           altercmd->subtype = AT_ProcessedConstraint;
-           altercmd->name = NULL;
-           altercmd->def = (Node *) fkconstraint;
-           alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
-       }
-
-       cxt->alist = lappend(cxt->alist, alterstmt);
-   }
-}
-
-/*
- * analyzeIndexStmt - perform parse analysis for CREATE INDEX
- *
- * Note that this has to be performed during execution not parse analysis, so
- * it's called by ProcessUtility.  (Most other callers don't need to bother,
- * because this is a no-op for an index not using either index expressions or
- * a predicate expression.)
- */
-IndexStmt *
-analyzeIndexStmt(IndexStmt *stmt, const char *queryString)
-{
-   Relation    rel;
-   ParseState *pstate;
-   RangeTblEntry *rte;
-   ListCell   *l;
-
-   /*
-    * We must not scribble on the passed-in IndexStmt, so copy it.  (This
-    * is overkill, but easy.)
-    */
-   stmt = (IndexStmt *) copyObject(stmt);
-
-   /*
-    * Open the parent table with appropriate locking.  We must do this
-    * because addRangeTableEntry() would acquire only AccessShareLock,
-    * leaving DefineIndex() needing to do a lock upgrade with consequent
-    * risk of deadlock.  Make sure this stays in sync with the type of
-    * lock DefineIndex() wants.
-    */
-   rel = heap_openrv(stmt->relation,
-               (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
-
-   /* Set up pstate */
-   pstate = make_parsestate(NULL);
-   pstate->p_sourcetext = queryString;
-
-   /*
-    * Put the parent table into the rtable so that the expressions can
-    * refer to its fields without qualification.
-    */
-   rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
-
-   /* no to join list, yes to namespaces */
-   addRTEtoQuery(pstate, rte, false, true, true);
-
-   /* take care of the where clause */
-   if (stmt->whereClause)
-       stmt->whereClause = transformWhereClause(pstate,
-                                                stmt->whereClause,
-                                                "WHERE");
-
-   /* take care of any index expressions */
-   foreach(l, stmt->indexParams)
-   {
-       IndexElem  *ielem = (IndexElem *) lfirst(l);
-
-       if (ielem->expr)
-       {
-           ielem->expr = transformExpr(pstate, ielem->expr);
-
-           /*
-            * We check only that the result type is legitimate; this is for
-            * consistency with what transformWhereClause() checks for the
-            * predicate.  DefineIndex() will make more checks.
-            */
-           if (expression_returns_set(ielem->expr))
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("index expression cannot return a set")));
-       }
-   }
-
-   /*
-    * Check that only the base rel is mentioned.
-    */
-   if (list_length(pstate->p_rtable) != 1)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-                errmsg("index expressions and predicates can refer only to the table being indexed")));
-
-   release_pstate_resources(pstate);
-   pfree(pstate);
-
-   /* Close relation, but keep the lock */
-   heap_close(rel, NoLock);
-
-   return stmt;
-}
-
-
-/*
- * analyzeRuleStmt -
- *   transform a Create Rule Statement. The action is a list of parse
- *   trees which is transformed into a list of query trees, and we also
- *   transform the WHERE clause if any.
- *
- * Note that this has to be performed during execution not parse analysis,
- * so it's called by DefineRule.  Also note that we must not scribble on
- * the passed-in RuleStmt, so we do copyObject() on the actions and WHERE
- * clause.
- */
-void
-analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
-               List **actions, Node **whereClause)
-{
-   Relation    rel;
-   ParseState *pstate;
-   RangeTblEntry *oldrte;
-   RangeTblEntry *newrte;
-
-   /*
-    * To avoid deadlock, make sure the first thing we do is grab
-    * AccessExclusiveLock on the target relation.  This will be needed by
-    * DefineQueryRewrite(), and we don't want to grab a lesser lock
-    * beforehand.
-    */
-   rel = heap_openrv(stmt->relation, AccessExclusiveLock);
-
-   /* Set up pstate */
-   pstate = make_parsestate(NULL);
-   pstate->p_sourcetext = queryString;
-
-   /*
-    * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
-    * Set up their RTEs in the main pstate for use in parsing the rule
-    * qualification.
-    */
-   oldrte = addRangeTableEntryForRelation(pstate, rel,
-                                          makeAlias("*OLD*", NIL),
-                                          false, false);
-   newrte = addRangeTableEntryForRelation(pstate, rel,
-                                          makeAlias("*NEW*", NIL),
-                                          false, false);
-   /* Must override addRangeTableEntry's default access-check flags */
-   oldrte->requiredPerms = 0;
-   newrte->requiredPerms = 0;
-
-   /*
-    * They must be in the namespace too for lookup purposes, but only add the
-    * one(s) that are relevant for the current kind of rule.  In an UPDATE
-    * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
-    * there's no need to be so picky for INSERT & DELETE.  We do not add them
-    * to the joinlist.
-    */
-   switch (stmt->event)
-   {
-       case CMD_SELECT:
-           addRTEtoQuery(pstate, oldrte, false, true, true);
-           break;
-       case CMD_UPDATE:
-           addRTEtoQuery(pstate, oldrte, false, true, true);
-           addRTEtoQuery(pstate, newrte, false, true, true);
-           break;
-       case CMD_INSERT:
-           addRTEtoQuery(pstate, newrte, false, true, true);
-           break;
-       case CMD_DELETE:
-           addRTEtoQuery(pstate, oldrte, false, true, true);
-           break;
-       default:
-           elog(ERROR, "unrecognized event type: %d",
-                (int) stmt->event);
-           break;
-   }
-
-   /* take care of the where clause */
-   *whereClause = transformWhereClause(pstate,
-                                       (Node *) copyObject(stmt->whereClause),
-                                       "WHERE");
-
-   if (list_length(pstate->p_rtable) != 2)     /* naughty, naughty... */
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                errmsg("rule WHERE condition cannot contain references to other relations")));
+   qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
+   qry->hasSubLinks = pstate->p_hasSubLinks;
    /* aggregates not allowed (but subselects are okay) */
    if (pstate->p_hasAggs)
        ereport(ERROR,
                (errcode(ERRCODE_GROUPING_ERROR),
-          errmsg("cannot use aggregate function in rule WHERE condition")));
+                errmsg("cannot use aggregate function in VALUES")));
+
+   return qry;
+}
+
+/*
+ * Prepare an INSERT row for assignment to the target table.
+ *
+ * The row might be either a VALUES row, or variables referencing a
+ * sub-SELECT output.
+ */
+static List *
+transformInsertRow(ParseState *pstate, List *exprlist,
+                  List *stmtcols, List *icolumns, List *attrnos)
+{
+   List       *result;
+   ListCell   *lc;
+   ListCell   *icols;
+   ListCell   *attnos;
 
    /*
-    * 'instead nothing' rules with a qualification need a query 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 specially by the rewrite system.
+    * Check length of expr list.  It must not have more expressions than
+    * there are target columns.  We allow fewer, but only if no explicit
+    * columns list was given (the remaining columns are implicitly
+    * defaulted).  Note we must check this *after* transformation because
+    * that could expand '*' into multiple items.
     */
-   if (stmt->actions == NIL)
-   {
-       Query      *nothing_qry = makeNode(Query);
-
-       nothing_qry->commandType = CMD_NOTHING;
-       nothing_qry->rtable = pstate->p_rtable;
-       nothing_qry->jointree = makeFromExpr(NIL, NULL);        /* no join wanted */
+   if (list_length(exprlist) > list_length(icolumns))
+       ereport(ERROR,
+               (errcode(ERRCODE_SYNTAX_ERROR),
+                errmsg("INSERT has more expressions than target columns")));
+   if (stmtcols != NIL &&
+       list_length(exprlist) < list_length(icolumns))
+       ereport(ERROR,
+               (errcode(ERRCODE_SYNTAX_ERROR),
+                errmsg("INSERT has more target columns than expressions")));
 
-       *actions = list_make1(nothing_qry);
-   }
-   else
+   /*
+    * Prepare columns for assignment to target table.
+    */
+   result = NIL;
+   icols = list_head(icolumns);
+   attnos = list_head(attrnos);
+   foreach(lc, exprlist)
    {
-       ListCell   *l;
-       List       *newactions = NIL;
-
-       /*
-        * transform each statement, like parse_sub_analyze()
-        */
-       foreach(l, stmt->actions)
-       {
-           Node       *action = (Node *) lfirst(l);
-           ParseState *sub_pstate = make_parsestate(NULL);
-           Query      *sub_qry,
-                      *top_subqry;
-           List       *extras_before = NIL;
-           List       *extras_after = NIL;
-           bool        has_old,
-                       has_new;
-
-           /*
-            * Since outer ParseState isn't parent of inner, have to pass
-            * down the query text by hand.
-            */
-           sub_pstate->p_sourcetext = queryString;
-
-           /*
-            * Set up OLD/NEW in the rtable for this statement.  The entries
-            * are added only to relnamespace, not varnamespace, because we
-            * don't want them to be referred to by unqualified field names
-            * nor "*" in the rule actions.  We decide later whether to put
-            * them in the joinlist.
-            */
-           oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                  makeAlias("*OLD*", NIL),
-                                                  false, false);
-           newrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                  makeAlias("*NEW*", NIL),
-                                                  false, false);
-           oldrte->requiredPerms = 0;
-           newrte->requiredPerms = 0;
-           addRTEtoQuery(sub_pstate, oldrte, false, true, false);
-           addRTEtoQuery(sub_pstate, newrte, false, true, false);
-
-           /* Transform the rule action statement */
-           top_subqry = transformStmt(sub_pstate,
-                                      (Node *) copyObject(action),
-                                      &extras_before, &extras_after);
-
-           /*
-            * We cannot support utility-statement actions (eg NOTIFY) with
-            * nonempty rule WHERE conditions, because there's no way to make
-            * the utility action execute conditionally.
-            */
-           if (top_subqry->commandType == CMD_UTILITY &&
-               *whereClause != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                        errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
-
-           /*
-            * If the action is INSERT...SELECT, OLD/NEW have been pushed down
-            * into the SELECT, and that's what we need to look at. (Ugly
-            * kluge ... try to fix this when we redesign querytrees.)
-            */
-           sub_qry = getInsertSelectQuery(top_subqry, NULL);
-
-           /*
-            * If the sub_qry is a setop, we cannot attach any qualifications
-            * to it, because the planner won't notice them.  This could
-            * perhaps be relaxed someday, but for now, we may as well reject
-            * such a rule immediately.
-            */
-           if (sub_qry->setOperations != NULL && *whereClause != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
-
-           /*
-            * Validate action's use of OLD/NEW, qual too
-            */
-           has_old =
-               rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
-               rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
-           has_new =
-               rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
-               rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
-
-           switch (stmt->event)
-           {
-               case CMD_SELECT:
-                   if (has_old)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("ON SELECT rule cannot use OLD")));
-                   if (has_new)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("ON SELECT rule cannot use NEW")));
-                   break;
-               case CMD_UPDATE:
-                   /* both are OK */
-                   break;
-               case CMD_INSERT:
-                   if (has_old)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("ON INSERT rule cannot use OLD")));
-                   break;
-               case CMD_DELETE:
-                   if (has_new)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("ON DELETE rule cannot use NEW")));
-                   break;
-               default:
-                   elog(ERROR, "unrecognized event type: %d",
-                        (int) stmt->event);
-                   break;
-           }
+       Expr       *expr = (Expr *) lfirst(lc);
+       ResTarget  *col;
 
-           /*
-            * For efficiency's sake, add OLD to the rule action's jointree
-            * only if it was actually referenced in the statement or qual.
-            *
-            * For INSERT, NEW is not really a relation (only a reference to
-            * the to-be-inserted tuple) and should never be added to the
-            * jointree.
-            *
-            * For UPDATE, we treat NEW as being another kind of reference to
-            * OLD, because it represents references to *transformed* tuples
-            * of the existing relation.  It would be wrong to enter NEW
-            * separately in the jointree, since that would cause a double
-            * join of the updated relation.  It's also wrong to fail to make
-            * a jointree entry if only NEW and not OLD is mentioned.
-            */
-           if (has_old || (has_new && stmt->event == CMD_UPDATE))
-           {
-               /*
-                * If sub_qry is a setop, manipulating its jointree will do no
-                * good at all, because the jointree is dummy. (This should be
-                * a can't-happen case because of prior tests.)
-                */
-               if (sub_qry->setOperations != NULL)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                            errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
-               /* hack so we can use addRTEtoQuery() */
-               sub_pstate->p_rtable = sub_qry->rtable;
-               sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
-               addRTEtoQuery(sub_pstate, oldrte, true, false, false);
-               sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
-           }
+       col = (ResTarget *) lfirst(icols);
+       Assert(IsA(col, ResTarget));
 
-           newactions = list_concat(newactions, extras_before);
-           newactions = lappend(newactions, top_subqry);
-           newactions = list_concat(newactions, extras_after);
+       expr = transformAssignedExpr(pstate, expr,
+                                    col->name,
+                                    lfirst_int(attnos),
+                                    col->indirection,
+                                    col->location);
 
-           release_pstate_resources(sub_pstate);
-           pfree(sub_pstate);
-       }
+       result = lappend(result, expr);
 
-       *actions = newactions;
+       icols = lnext(icols);
+       attnos = lnext(attnos);
    }
 
-   release_pstate_resources(pstate);
-   pfree(pstate);
-
-   /* Close relation, but keep the exclusive lock */
-   heap_close(rel, NoLock);
+   return result;
 }
 
 
@@ -2052,7 +673,8 @@ analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
  * transformSelectStmt -
  *   transforms a Select Statement
  *
- * Note: this is also used for DECLARE CURSOR statements.
+ * Note: this covers only cases with no set operations and no VALUES lists;
+ * see below for the other cases.
  */
 static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
@@ -2323,7 +945,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 }
 
 /*
- * transformSetOperationsStmt -
+ * transformSetOperationStmt -
  *   transforms a set-operations tree
  *
  * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT
@@ -2591,7 +1213,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
    if (isLeaf)
    {
        /* Process leaf SELECT */
-       List       *selectList;
        Query      *selectQuery;
        char        selectName[32];
        RangeTblEntry *rte;
@@ -2604,11 +1225,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
         * of this sub-query, because they are not in the toplevel pstate's
         * namespace list.
         */
-       selectList = parse_sub_analyze((Node *) stmt, pstate);
-
-       Assert(list_length(selectList) == 1);
-       selectQuery = (Query *) linitial(selectList);
-       Assert(IsA(selectQuery, Query));
+       selectQuery = parse_sub_analyze((Node *) stmt, pstate);
 
        /*
         * Check for bogus references to Vars on the current query level (but
@@ -2761,7 +1378,10 @@ getSetColTypes(ParseState *pstate, Node *node,
        elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 }
 
-/* Attach column names from a ColumnDef list to a TargetEntry list */
+/*
+ * Attach column names from a ColumnDef list to a TargetEntry list
+ * (for CREATE TABLE AS)
+ */
 static void
 applyColumnNames(List *dst, List *src)
 {
@@ -2957,185 +1577,6 @@ transformReturningList(ParseState *pstate, List *returningList)
    return rlist;
 }
 
-/*
- * transformAlterTableStmt -
- * transform an Alter Table Statement
- *
- * CAUTION: resist the temptation to do any work here that depends on the
- * current state of the table.  Actual execution of the command might not
- * occur till some future transaction.  Hence, we do only purely syntactic
- * transformations here, comparable to the processing of CREATE TABLE.
- */
-static Query *
-transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
-                       List **extras_before, List **extras_after)
-{
-   CreateStmtContext cxt;
-   Query      *qry;
-   ListCell   *lcmd,
-              *l;
-   List       *newcmds = NIL;
-   bool        skipValidation = true;
-   AlterTableCmd *newcmd;
-
-   cxt.stmtType = "ALTER TABLE";
-   cxt.relation = stmt->relation;
-   cxt.inhRelations = NIL;
-   cxt.isalter = true;
-   cxt.hasoids = false;        /* need not be right */
-   cxt.columns = NIL;
-   cxt.ckconstraints = NIL;
-   cxt.fkconstraints = NIL;
-   cxt.ixconstraints = NIL;
-   cxt.blist = NIL;
-   cxt.alist = NIL;
-   cxt.pkey = NULL;
-
-   /*
-    * The only subtypes that currently require parse transformation handling
-    * are ADD COLUMN and ADD CONSTRAINT.  These largely re-use code from
-    * CREATE TABLE.
-    */
-   foreach(lcmd, stmt->cmds)
-   {
-       AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
-
-       switch (cmd->subtype)
-       {
-           case AT_AddColumn:
-               {
-                   ColumnDef  *def = (ColumnDef *) cmd->def;
-
-                   Assert(IsA(cmd->def, ColumnDef));
-                   transformColumnDefinition(pstate, &cxt,
-                                             (ColumnDef *) cmd->def);
-
-                   /*
-                    * If the column has a non-null default, we can't skip
-                    * validation of foreign keys.
-                    */
-                   if (((ColumnDef *) cmd->def)->raw_default != NULL)
-                       skipValidation = false;
-
-                   newcmds = lappend(newcmds, cmd);
-
-                   /*
-                    * Convert an ADD COLUMN ... NOT NULL constraint to a
-                    * separate command
-                    */
-                   if (def->is_not_null)
-                   {
-                       /* Remove NOT NULL from AddColumn */
-                       def->is_not_null = false;
-
-                       /* Add as a separate AlterTableCmd */
-                       newcmd = makeNode(AlterTableCmd);
-                       newcmd->subtype = AT_SetNotNull;
-                       newcmd->name = pstrdup(def->colname);
-                       newcmds = lappend(newcmds, newcmd);
-                   }
-
-                   /*
-                    * All constraints are processed in other ways. Remove the
-                    * original list
-                    */
-                   def->constraints = NIL;
-
-                   break;
-               }
-           case AT_AddConstraint:
-
-               /*
-                * The original AddConstraint cmd node doesn't go to newcmds
-                */
-
-               if (IsA(cmd->def, Constraint))
-                   transformTableConstraint(pstate, &cxt,
-                                            (Constraint *) cmd->def);
-               else if (IsA(cmd->def, FkConstraint))
-               {
-                   cxt.fkconstraints = lappend(cxt.fkconstraints, cmd->def);
-                   skipValidation = false;
-               }
-               else
-                   elog(ERROR, "unrecognized node type: %d",
-                        (int) nodeTag(cmd->def));
-               break;
-
-           case AT_ProcessedConstraint:
-
-               /*
-                * Already-transformed ADD CONSTRAINT, so just make it look
-                * like the standard case.
-                */
-               cmd->subtype = AT_AddConstraint;
-               newcmds = lappend(newcmds, cmd);
-               break;
-
-           default:
-               newcmds = lappend(newcmds, cmd);
-               break;
-       }
-   }
-
-   /*
-    * transformIndexConstraints wants cxt.alist to contain only index
-    * statements, so transfer anything we already have into extras_after
-    * immediately.
-    */
-   *extras_after = list_concat(cxt.alist, *extras_after);
-   cxt.alist = NIL;
-
-   /* Postprocess index and FK constraints */
-   transformIndexConstraints(pstate, &cxt);
-
-   transformFKConstraints(pstate, &cxt, skipValidation, true);
-
-   /*
-    * Push any index-creation commands into the ALTER, so that they can be
-    * scheduled nicely by tablecmds.c.
-    */
-   foreach(l, cxt.alist)
-   {
-       Node       *idxstmt = (Node *) lfirst(l);
-
-       Assert(IsA(idxstmt, IndexStmt));
-       newcmd = makeNode(AlterTableCmd);
-       newcmd->subtype = AT_AddIndex;
-       newcmd->def = idxstmt;
-       newcmds = lappend(newcmds, newcmd);
-   }
-   cxt.alist = NIL;
-
-   /* Append any CHECK or FK constraints to the commands list */
-   foreach(l, cxt.ckconstraints)
-   {
-       newcmd = makeNode(AlterTableCmd);
-       newcmd->subtype = AT_AddConstraint;
-       newcmd->def = (Node *) lfirst(l);
-       newcmds = lappend(newcmds, newcmd);
-   }
-   foreach(l, cxt.fkconstraints)
-   {
-       newcmd = makeNode(AlterTableCmd);
-       newcmd->subtype = AT_AddConstraint;
-       newcmd->def = (Node *) lfirst(l);
-       newcmds = lappend(newcmds, newcmd);
-   }
-
-   /* Update statement's commands list */
-   stmt->cmds = newcmds;
-
-   qry = makeNode(Query);
-   qry->commandType = CMD_UTILITY;
-   qry->utilityStmt = (Node *) stmt;
-
-   *extras_before = list_concat(*extras_before, cxt.blist);
-   *extras_after = list_concat(cxt.alist, *extras_after);
-
-   return qry;
-}
-
 
 /*
  * transformDeclareCursorStmt -
@@ -3152,8 +1593,6 @@ static Query *
 transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 {
    Query      *result;
-   List       *extras_before = NIL,
-              *extras_after = NIL;
 
    /*
     * Don't allow both SCROLL and NO SCROLL to be specified
@@ -3164,12 +1603,8 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                 errmsg("cannot specify both SCROLL and NO SCROLL")));
 
-   result = transformStmt(pstate, stmt->query,
-                          &extras_before, &extras_after);
+   result = transformStmt(pstate, stmt->query);
 
-   /* Shouldn't get any extras, since grammar only allows SelectStmt */
-   if (extras_before || extras_after)
-       elog(ERROR, "unexpected extra stuff in cursor statement");
    if (!IsA(result, Query) ||
        result->commandType != CMD_SELECT ||
        result->utilityStmt != NULL)
@@ -3219,12 +1654,8 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 
    if (pstate->p_variableparams)
    {
-       List       *extras_before = NIL,
-                  *extras_after = NIL;
-
        /* Since parse analysis scribbles on its input, copy the tree first! */
-       (void) transformStmt(pstate, copyObject(stmt->query),
-                            &extras_before, &extras_after);
+       (void) transformStmt(pstate, copyObject(stmt->query));
    }
 
    /* Now return the untransformed command as a utility Query */
@@ -3419,281 +1850,6 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
 }
 
 
-/*
- * Preprocess a list of column constraint clauses
- * to attach constraint attributes to their primary constraint nodes
- * and detect inconsistent/misplaced constraint attributes.
- *
- * NOTE: currently, attributes are only supported for FOREIGN KEY primary
- * constraints, but someday they ought to be supported for other constraints.
- */
-static void
-transformConstraintAttrs(List *constraintList)
-{
-   Node       *lastprimarynode = NULL;
-   bool        saw_deferrability = false;
-   bool        saw_initially = false;
-   ListCell   *clist;
-
-   foreach(clist, constraintList)
-   {
-       Node       *node = lfirst(clist);
-
-       if (!IsA(node, Constraint))
-       {
-           lastprimarynode = node;
-           /* reset flags for new primary node */
-           saw_deferrability = false;
-           saw_initially = false;
-       }
-       else
-       {
-           Constraint *con = (Constraint *) node;
-
-           switch (con->contype)
-           {
-               case CONSTR_ATTR_DEFERRABLE:
-                   if (lastprimarynode == NULL ||
-                       !IsA(lastprimarynode, FkConstraint))
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("misplaced DEFERRABLE clause")));
-                   if (saw_deferrability)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
-                   saw_deferrability = true;
-                   ((FkConstraint *) lastprimarynode)->deferrable = true;
-                   break;
-               case CONSTR_ATTR_NOT_DEFERRABLE:
-                   if (lastprimarynode == NULL ||
-                       !IsA(lastprimarynode, FkConstraint))
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("misplaced NOT DEFERRABLE clause")));
-                   if (saw_deferrability)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
-                   saw_deferrability = true;
-                   ((FkConstraint *) lastprimarynode)->deferrable = false;
-                   if (saw_initially &&
-                       ((FkConstraint *) lastprimarynode)->initdeferred)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
-                   break;
-               case CONSTR_ATTR_DEFERRED:
-                   if (lastprimarynode == NULL ||
-                       !IsA(lastprimarynode, FkConstraint))
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                            errmsg("misplaced INITIALLY DEFERRED clause")));
-                   if (saw_initially)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
-                   saw_initially = true;
-                   ((FkConstraint *) lastprimarynode)->initdeferred = true;
-
-                   /*
-                    * If only INITIALLY DEFERRED appears, assume DEFERRABLE
-                    */
-                   if (!saw_deferrability)
-                       ((FkConstraint *) lastprimarynode)->deferrable = true;
-                   else if (!((FkConstraint *) lastprimarynode)->deferrable)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
-                   break;
-               case CONSTR_ATTR_IMMEDIATE:
-                   if (lastprimarynode == NULL ||
-                       !IsA(lastprimarynode, FkConstraint))
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                           errmsg("misplaced INITIALLY IMMEDIATE clause")));
-                   if (saw_initially)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
-                   saw_initially = true;
-                   ((FkConstraint *) lastprimarynode)->initdeferred = false;
-                   break;
-               default:
-                   /* Otherwise it's not an attribute */
-                   lastprimarynode = node;
-                   /* reset flags for new primary node */
-                   saw_deferrability = false;
-                   saw_initially = false;
-                   break;
-           }
-       }
-   }
-}
-
-/* Build a FromExpr node */
-static FromExpr *
-makeFromExpr(List *fromlist, Node *quals)
-{
-   FromExpr   *f = makeNode(FromExpr);
-
-   f->fromlist = fromlist;
-   f->quals = quals;
-   return f;
-}
-
-/*
- * Special handling of type definition for a column
- */
-static void
-transformColumnType(ParseState *pstate, ColumnDef *column)
-{
-   /*
-    * All we really need to do here is verify that the type is valid.
-    */
-   Type        ctype = typenameType(pstate, column->typename);
-
-   ReleaseSysCache(ctype);
-}
-
-static void
-setSchemaName(char *context_schema, char **stmt_schema_name)
-{
-   if (*stmt_schema_name == NULL)
-       *stmt_schema_name = context_schema;
-   else if (strcmp(context_schema, *stmt_schema_name) != 0)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
-                errmsg("CREATE specifies a schema (%s) "
-                       "different from the one being created (%s)",
-                       *stmt_schema_name, context_schema)));
-}
-
-/*
- * analyzeCreateSchemaStmt -
- *   analyzes the "create schema" statement
- *
- * Split the schema element list into individual commands and place
- * them in the result list in an order such that there are no forward
- * references (e.g. GRANT to a table created later in the list). Note
- * that the logic we use for determining forward references is
- * presently quite incomplete.
- *
- * SQL92 also allows constraints to make forward references, so thumb through
- * the table columns and move forward references to a posterior alter-table
- * command.
- *
- * The result is a list of parse nodes that still need to be analyzed ---
- * but we can't analyze the later commands until we've executed the earlier
- * ones, because of possible inter-object references.
- *
- * Note: Called from commands/schemacmds.c
- */
-List *
-analyzeCreateSchemaStmt(CreateSchemaStmt *stmt)
-{
-   CreateSchemaStmtContext cxt;
-   List       *result;
-   ListCell   *elements;
-
-   cxt.stmtType = "CREATE SCHEMA";
-   cxt.schemaname = stmt->schemaname;
-   cxt.authid = stmt->authid;
-   cxt.sequences = NIL;
-   cxt.tables = NIL;
-   cxt.views = NIL;
-   cxt.indexes = NIL;
-   cxt.grants = NIL;
-   cxt.triggers = NIL;
-   cxt.fwconstraints = NIL;
-   cxt.alters = NIL;
-   cxt.blist = NIL;
-   cxt.alist = NIL;
-
-   /*
-    * Run through each schema element in the schema element list. Separate
-    * statements by type, and do preliminary analysis.
-    */
-   foreach(elements, stmt->schemaElts)
-   {
-       Node       *element = lfirst(elements);
-
-       switch (nodeTag(element))
-       {
-           case T_CreateSeqStmt:
-               {
-                   CreateSeqStmt *elp = (CreateSeqStmt *) element;
-
-                   setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
-                   cxt.sequences = lappend(cxt.sequences, element);
-               }
-               break;
-
-           case T_CreateStmt:
-               {
-                   CreateStmt *elp = (CreateStmt *) element;
-
-                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-
-                   /*
-                    * XXX todo: deal with constraints
-                    */
-                   cxt.tables = lappend(cxt.tables, element);
-               }
-               break;
-
-           case T_ViewStmt:
-               {
-                   ViewStmt   *elp = (ViewStmt *) element;
-
-                   setSchemaName(cxt.schemaname, &elp->view->schemaname);
-
-                   /*
-                    * XXX todo: deal with references between views
-                    */
-                   cxt.views = lappend(cxt.views, element);
-               }
-               break;
-
-           case T_IndexStmt:
-               {
-                   IndexStmt  *elp = (IndexStmt *) element;
-
-                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-                   cxt.indexes = lappend(cxt.indexes, element);
-               }
-               break;
-
-           case T_CreateTrigStmt:
-               {
-                   CreateTrigStmt *elp = (CreateTrigStmt *) element;
-
-                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-                   cxt.triggers = lappend(cxt.triggers, element);
-               }
-               break;
-
-           case T_GrantStmt:
-               cxt.grants = lappend(cxt.grants, element);
-               break;
-
-           default:
-               elog(ERROR, "unrecognized node type: %d",
-                    (int) nodeTag(element));
-       }
-   }
-
-   result = NIL;
-   result = list_concat(result, cxt.sequences);
-   result = list_concat(result, cxt.tables);
-   result = list_concat(result, cxt.views);
-   result = list_concat(result, cxt.indexes);
-   result = list_concat(result, cxt.triggers);
-   result = list_concat(result, cxt.grants);
-
-   return result;
-}
-
 /*
  * Traverse a fully-analyzed tree to verify that parameter symbols
  * match their types.  We need this because some Params might still
index 6e894c1799a5858902826386ab3ea6811510920d..d9e981bb5724b99440c4945e7606e3132048bb64 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.595 2007/06/18 21:40:57 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.596 2007/06/23 22:12:51 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -33,7 +33,7 @@
  *         SET SQL_inheritance TO off; SELECT * FROM foo;
  *   because the entire string is parsed by gram.y before the SET gets
  *   executed.  Anything that depends on the database or changeable state
- *   should be handled inside parse_analyze() so that it happens at the
+ *   should be handled during parse analysis so that it happens at the
  *   right time not the wrong time.  The handling of SQL_inheritance is
  *   a good example.
  *
@@ -2093,9 +2093,10 @@ ColConstraintElem:
  * ConstraintAttr represents constraint attributes, which we parse as if
  * they were independent constraint clauses, in order to avoid shift/reduce
  * conflicts (since NOT might start either an independent NOT NULL clause
- * or an attribute).  analyze.c is responsible for attaching the attribute
- * information to the preceding "real" constraint node, and for complaining
- * if attribute clauses appear in the wrong place or wrong combinations.
+ * or an attribute).  parse_utilcmd.c is responsible for attaching the
+ * attribute information to the preceding "real" constraint node, and for
+ * complaining if attribute clauses appear in the wrong place or wrong
+ * combinations.
  *
  * See also ConstraintAttributeSpec, which can be used in places where
  * there is no parsing conflict.
index 4c1fb0cc4c12295c596193a9efeafe8478775edd..28717020e349f237d0cd57e6eec23a39768f05b3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.165 2007/04/27 22:05:48 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.166 2007/06/23 22:12:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,8 +152,8 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
     * Open target rel and grab suitable lock (which we will hold till end of
     * transaction).
     *
-    * analyze.c will eventually do the corresponding heap_close(), but *not*
-    * release the lock.
+    * free_parsestate() will eventually do the corresponding
+    * heap_close(), but *not* release the lock.
     */
    pstate->p_target_relation = heap_openrv(relation, RowExclusiveLock);
 
@@ -193,7 +193,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
  * Simplify InhOption (yes/no/default) into boolean yes/no.
  *
  * The reason we do things this way is that we don't want to examine the
- * SQL_inheritance option flag until parse_analyze is run. Otherwise,
+ * SQL_inheritance option flag until parse_analyze() is run.   Otherwise,
  * we'd do the wrong thing with query strings that intermix SET commands
  * with queries.
  */
@@ -417,7 +417,6 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
 static RangeTblEntry *
 transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 {
-   List       *parsetrees;
    Query      *query;
    RangeTblEntry *rte;
 
@@ -434,19 +433,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
    /*
     * Analyze and transform the subquery.
     */
-   parsetrees = parse_sub_analyze(r->subquery, pstate);
+   query = parse_sub_analyze(r->subquery, pstate);
 
    /*
-    * Check that we got something reasonable.  Most of these conditions are
-    * probably impossible given restrictions of the grammar, but check 'em
-    * anyway.
+    * Check that we got something reasonable.  Many of these conditions are
+    * impossible given restrictions of the grammar, but check 'em anyway.
     */
-   if (list_length(parsetrees) != 1)
-       elog(ERROR, "unexpected parse analysis result for subquery in FROM");
-   query = (Query *) linitial(parsetrees);
-   if (query == NULL || !IsA(query, Query))
-       elog(ERROR, "unexpected parse analysis result for subquery in FROM");
-
    if (query->commandType != CMD_SELECT ||
        query->utilityStmt != NULL)
        elog(ERROR, "expected SELECT query from subquery in FROM");
index da4bcf208f93c73dba3b132357f2eddfbb562b1e..754e18d687cc4ec78588d7d2c960a9cddd079f7d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.220 2007/06/11 22:22:42 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.221 2007/06/23 22:12:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1120,19 +1120,15 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
 static Node *
 transformSubLink(ParseState *pstate, SubLink *sublink)
 {
-   List       *qtrees;
-   Query      *qtree;
    Node       *result = (Node *) sublink;
+   Query      *qtree;
 
    /* If we already transformed this node, do nothing */
    if (IsA(sublink->subselect, Query))
        return result;
 
    pstate->p_hasSubLinks = true;
-   qtrees = parse_sub_analyze(sublink->subselect, pstate);
-   if (list_length(qtrees) != 1)
-       elog(ERROR, "bad query in sub-select");
-   qtree = (Query *) linitial(qtrees);
+   qtree = parse_sub_analyze(sublink->subselect, pstate);
    if (qtree->commandType != CMD_SELECT ||
        qtree->utilityStmt != NULL ||
        qtree->intoClause != NULL)
index a8dfa2666bb193f74077603dbc4f9c1fd9af3f11..a5e6026ebaba83378fbae54b5732227e0e31b5ff 100644 (file)
@@ -8,12 +8,13 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.97 2007/03/17 00:11:04 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.98 2007/06/23 22:12:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "catalog/pg_type.h"
 #include "mb/pg_wchar.h"
 #include "nodes/makefuncs.h"
 #include "utils/varbit.h"
 
 
-/* make_parsestate()
- * Allocate and initialize a new ParseState.
- * The CALLER is responsible for freeing the ParseState* returned.
+/*
+ * make_parsestate
+ *     Allocate and initialize a new ParseState.
+ *
+ * Caller should eventually release the ParseState via free_parsestate().
  */
 ParseState *
 make_parsestate(ParseState *parentParseState)
@@ -52,6 +55,30 @@ make_parsestate(ParseState *parentParseState)
    return pstate;
 }
 
+/*
+ * free_parsestate
+ *     Release a ParseState and any subsidiary resources.
+ */
+void
+free_parsestate(ParseState *pstate)
+{
+   /*
+    * Check that we did not produce too many resnos; at the very least we
+    * cannot allow more than 2^16, since that would exceed the range of a
+    * AttrNumber. It seems safest to use MaxTupleAttributeNumber.
+    */
+   if (pstate->p_next_resno - 1 > MaxTupleAttributeNumber)
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                errmsg("target lists can have at most %d entries",
+                       MaxTupleAttributeNumber)));
+
+   if (pstate->p_target_relation != NULL)
+       heap_close(pstate->p_target_relation, NoLock);
+
+   pfree(pstate);
+}
+
 
 /*
  * parser_errposition
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644 (file)
index 0000000..3782225
--- /dev/null
@@ -0,0 +1,1800 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_utilcmd.c
+ *   Perform parse analysis work for various utility commands
+ *
+ * Formerly we did this work during parse_analyze() in analyze.c.  However
+ * that is fairly unsafe in the presence of querytree caching, since any
+ * database state that we depend on in making the transformations might be
+ * obsolete by the time the utility command is executed; and utility commands
+ * have no infrastructure for holding locks or rechecking plan validity.
+ * Hence these functions are now called at the start of execution of their
+ * respective utility commands.
+ *
+ * NOTE: in general we must avoid scribbling on the passed-in raw parse
+ * tree, since it might be in a plan cache.  The simplest solution is
+ * a quick copyObject() call before manipulating the query tree.
+ *
+ *
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.1 2007/06/23 22:12:51 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/heap.h"
+#include "catalog/index.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "commands/tablecmds.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
+#include "parser/analyze.h"
+#include "parser/gramparse.h"
+#include "parser/parse_clause.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_type.h"
+#include "parser/parse_utilcmd.h"
+#include "rewrite/rewriteManip.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/* State shared by transformCreateStmt and its subroutines */
+typedef struct
+{
+   const char *stmtType;       /* "CREATE TABLE" or "ALTER TABLE" */
+   RangeVar   *relation;       /* relation to create */
+   Relation    rel;            /* opened/locked rel, if ALTER */
+   List       *inhRelations;   /* relations to inherit from */
+   bool        isalter;        /* true if altering existing table */
+   bool        hasoids;        /* does relation have an OID column? */
+   List       *columns;        /* ColumnDef items */
+   List       *ckconstraints;  /* CHECK constraints */
+   List       *fkconstraints;  /* FOREIGN KEY constraints */
+   List       *ixconstraints;  /* index-creating constraints */
+   List       *blist;          /* "before list" of things to do before
+                                * creating the table */
+   List       *alist;          /* "after list" of things to do after creating
+                                * the table */
+   IndexStmt  *pkey;           /* PRIMARY KEY index, if any */
+} CreateStmtContext;
+
+/* State shared by transformCreateSchemaStmt and its subroutines */
+typedef struct
+{
+   const char *stmtType;       /* "CREATE SCHEMA" or "ALTER SCHEMA" */
+   char       *schemaname;     /* name of schema */
+   char       *authid;         /* owner of schema */
+   List       *sequences;      /* CREATE SEQUENCE items */
+   List       *tables;         /* CREATE TABLE items */
+   List       *views;          /* CREATE VIEW items */
+   List       *indexes;        /* CREATE INDEX items */
+   List       *triggers;       /* CREATE TRIGGER items */
+   List       *grants;         /* GRANT items */
+} CreateSchemaStmtContext;
+
+
+static void transformColumnDefinition(ParseState *pstate,
+                         CreateStmtContext *cxt,
+                         ColumnDef *column);
+static void transformTableConstraint(ParseState *pstate,
+                        CreateStmtContext *cxt,
+                        Constraint *constraint);
+static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
+                    InhRelation *inhrelation);
+static void transformIndexConstraints(ParseState *pstate,
+                         CreateStmtContext *cxt);
+static void transformFKConstraints(ParseState *pstate,
+                      CreateStmtContext *cxt,
+                      bool skipValidation,
+                      bool isAddConstraint);
+static void transformConstraintAttrs(List *constraintList);
+static void transformColumnType(ParseState *pstate, ColumnDef *column);
+static void setSchemaName(char *context_schema, char **stmt_schema_name);
+
+
+/*
+ * transformCreateStmt -
+ *   parse analysis for CREATE TABLE
+ *
+ * Returns a List of utility commands to be done in sequence.  One of these
+ * will be the transformed CreateStmt, but there may be additional actions
+ * to be done before and after the actual DefineRelation() call.
+ *
+ * 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
+ */
+List *
+transformCreateStmt(CreateStmt *stmt, const char *queryString)
+{
+   ParseState *pstate;
+   CreateStmtContext cxt;
+   List       *result;
+   List       *save_alist;
+   ListCell   *elements;
+
+   /*
+    * We must not scribble on the passed-in CreateStmt, so copy it.  (This
+    * is overkill, but easy.)
+    */
+   stmt = (CreateStmt *) copyObject(stmt);
+
+   /* Set up pstate */
+   pstate = make_parsestate(NULL);
+   pstate->p_sourcetext = queryString;
+
+   cxt.stmtType = "CREATE TABLE";
+   cxt.relation = stmt->relation;
+   cxt.rel = NULL;
+   cxt.inhRelations = stmt->inhRelations;
+   cxt.isalter = false;
+   cxt.columns = NIL;
+   cxt.ckconstraints = NIL;
+   cxt.fkconstraints = NIL;
+   cxt.ixconstraints = NIL;
+   cxt.blist = NIL;
+   cxt.alist = NIL;
+   cxt.pkey = NULL;
+   cxt.hasoids = interpretOidsOption(stmt->options);
+
+   /*
+    * Run through each primary element in the table creation clause. Separate
+    * column defs from constraints, and do preliminary analysis.
+    */
+   foreach(elements, stmt->tableElts)
+   {
+       Node       *element = lfirst(elements);
+
+       switch (nodeTag(element))
+       {
+           case T_ColumnDef:
+               transformColumnDefinition(pstate, &cxt,
+                                         (ColumnDef *) element);
+               break;
+
+           case T_Constraint:
+               transformTableConstraint(pstate, &cxt,
+                                        (Constraint *) element);
+               break;
+
+           case T_FkConstraint:
+               /* No pre-transformation needed */
+               cxt.fkconstraints = lappend(cxt.fkconstraints, element);
+               break;
+
+           case T_InhRelation:
+               transformInhRelation(pstate, &cxt,
+                                    (InhRelation *) element);
+               break;
+
+           default:
+               elog(ERROR, "unrecognized node type: %d",
+                    (int) nodeTag(element));
+               break;
+       }
+   }
+
+   /*
+    * transformIndexConstraints wants cxt.alist to contain only index
+    * statements, so transfer anything we already have into save_alist.
+    */
+   save_alist = cxt.alist;
+   cxt.alist = NIL;
+
+   Assert(stmt->constraints == NIL);
+
+   /*
+    * Postprocess constraints that give rise to index definitions.
+    */
+   transformIndexConstraints(pstate, &cxt);
+
+   /*
+    * Postprocess foreign-key constraints.
+    */
+   transformFKConstraints(pstate, &cxt, true, false);
+
+   /*
+    * Output results.
+    */
+   stmt->tableElts = cxt.columns;
+   stmt->constraints = cxt.ckconstraints;
+
+   result = lappend(cxt.blist, stmt);
+   result = list_concat(result, cxt.alist);
+   result = list_concat(result, save_alist);
+
+   return result;
+}
+
+/*
+ * transformColumnDefinition -
+ *     transform a single ColumnDef within CREATE TABLE
+ *     Also used in ALTER TABLE ADD COLUMN
+ */
+static void
+transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
+                         ColumnDef *column)
+{
+   bool        is_serial;
+   bool        saw_nullable;
+   bool        saw_default;
+   Constraint *constraint;
+   ListCell   *clist;
+
+   cxt->columns = lappend(cxt->columns, column);
+
+   /* Check for SERIAL pseudo-types */
+   is_serial = false;
+   if (list_length(column->typename->names) == 1)
+   {
+       char       *typname = strVal(linitial(column->typename->names));
+
+       if (strcmp(typname, "serial") == 0 ||
+           strcmp(typname, "serial4") == 0)
+       {
+           is_serial = true;
+           column->typename->names = NIL;
+           column->typename->typeid = INT4OID;
+       }
+       else if (strcmp(typname, "bigserial") == 0 ||
+                strcmp(typname, "serial8") == 0)
+       {
+           is_serial = true;
+           column->typename->names = NIL;
+           column->typename->typeid = INT8OID;
+       }
+   }
+
+   /* Do necessary work on the column type declaration */
+   transformColumnType(pstate, column);
+
+   /* Special actions for SERIAL pseudo-types */
+   if (is_serial)
+   {
+       Oid         snamespaceid;
+       char       *snamespace;
+       char       *sname;
+       char       *qstring;
+       A_Const    *snamenode;
+       FuncCall   *funccallnode;
+       CreateSeqStmt *seqstmt;
+       AlterSeqStmt *altseqstmt;
+       List       *attnamelist;
+
+       /*
+        * Determine namespace and name to use for the sequence.
+        *
+        * Although we use ChooseRelationName, it's not guaranteed that the
+        * selected sequence name won't conflict; given sufficiently long
+        * field names, two different serial columns in the same table could
+        * be assigned the same sequence name, and we'd not notice since we
+        * aren't creating the sequence quite yet.  In practice this seems
+        * quite unlikely to be a problem, especially since few people would
+        * need two serial columns in one table.
+        */
+       if (cxt->rel)
+           snamespaceid = RelationGetNamespace(cxt->rel);
+       else
+           snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
+       snamespace = get_namespace_name(snamespaceid);
+       sname = ChooseRelationName(cxt->relation->relname,
+                                  column->colname,
+                                  "seq",
+                                  snamespaceid);
+
+       ereport(NOTICE,
+               (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
+                       cxt->stmtType, sname,
+                       cxt->relation->relname, column->colname)));
+
+       /*
+        * Build a CREATE SEQUENCE command to create the sequence object, and
+        * add it to the list of things to be done before this CREATE/ALTER
+        * TABLE.
+        */
+       seqstmt = makeNode(CreateSeqStmt);
+       seqstmt->sequence = makeRangeVar(snamespace, sname);
+       seqstmt->options = NIL;
+
+       cxt->blist = lappend(cxt->blist, seqstmt);
+
+       /*
+        * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
+        * as owned by this column, and add it to the list of things to be
+        * done after this CREATE/ALTER TABLE.
+        */
+       altseqstmt = makeNode(AlterSeqStmt);
+       altseqstmt->sequence = makeRangeVar(snamespace, sname);
+       attnamelist = list_make3(makeString(snamespace),
+                                makeString(cxt->relation->relname),
+                                makeString(column->colname));
+       altseqstmt->options = list_make1(makeDefElem("owned_by",
+                                                    (Node *) attnamelist));
+
+       cxt->alist = lappend(cxt->alist, altseqstmt);
+
+       /*
+        * Create appropriate constraints for SERIAL.  We do this in full,
+        * rather than shortcutting, so that we will detect any conflicting
+        * constraints the user wrote (like a different DEFAULT).
+        *
+        * Create an expression tree representing the function call
+        * nextval('sequencename').  We cannot reduce the raw tree to cooked
+        * form until after the sequence is created, but there's no need to do
+        * so.
+        */
+       qstring = quote_qualified_identifier(snamespace, sname);
+       snamenode = makeNode(A_Const);
+       snamenode->val.type = T_String;
+       snamenode->val.val.str = qstring;
+       snamenode->typename = SystemTypeName("regclass");
+       funccallnode = makeNode(FuncCall);
+       funccallnode->funcname = SystemFuncName("nextval");
+       funccallnode->args = list_make1(snamenode);
+       funccallnode->agg_star = false;
+       funccallnode->agg_distinct = false;
+       funccallnode->location = -1;
+
+       constraint = makeNode(Constraint);
+       constraint->contype = CONSTR_DEFAULT;
+       constraint->raw_expr = (Node *) funccallnode;
+       constraint->cooked_expr = NULL;
+       constraint->keys = NIL;
+       column->constraints = lappend(column->constraints, constraint);
+
+       constraint = makeNode(Constraint);
+       constraint->contype = CONSTR_NOTNULL;
+       column->constraints = lappend(column->constraints, constraint);
+   }
+
+   /* Process column constraints, if any... */
+   transformConstraintAttrs(column->constraints);
+
+   saw_nullable = false;
+   saw_default = false;
+
+   foreach(clist, column->constraints)
+   {
+       constraint = lfirst(clist);
+
+       /*
+        * If this column constraint is a FOREIGN KEY constraint, then we fill
+        * in the current attribute's name and throw it into the list of FK
+        * constraints to be processed later.
+        */
+       if (IsA(constraint, FkConstraint))
+       {
+           FkConstraint *fkconstraint = (FkConstraint *) constraint;
+
+           fkconstraint->fk_attrs = list_make1(makeString(column->colname));
+           cxt->fkconstraints = lappend(cxt->fkconstraints, fkconstraint);
+           continue;
+       }
+
+       Assert(IsA(constraint, Constraint));
+
+       switch (constraint->contype)
+       {
+           case CONSTR_NULL:
+               if (saw_nullable && column->is_not_null)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
+                                 column->colname, cxt->relation->relname)));
+               column->is_not_null = FALSE;
+               saw_nullable = true;
+               break;
+
+           case CONSTR_NOTNULL:
+               if (saw_nullable && !column->is_not_null)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
+                                 column->colname, cxt->relation->relname)));
+               column->is_not_null = TRUE;
+               saw_nullable = true;
+               break;
+
+           case CONSTR_DEFAULT:
+               if (saw_default)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
+                                 column->colname, cxt->relation->relname)));
+               /* Note: DEFAULT NULL maps to constraint->raw_expr == NULL */
+               column->raw_default = constraint->raw_expr;
+               Assert(constraint->cooked_expr == NULL);
+               saw_default = true;
+               break;
+
+           case CONSTR_PRIMARY:
+           case CONSTR_UNIQUE:
+               if (constraint->keys == NIL)
+                   constraint->keys = list_make1(makeString(column->colname));
+               cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
+               break;
+
+           case CONSTR_CHECK:
+               cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+               break;
+
+           case CONSTR_ATTR_DEFERRABLE:
+           case CONSTR_ATTR_NOT_DEFERRABLE:
+           case CONSTR_ATTR_DEFERRED:
+           case CONSTR_ATTR_IMMEDIATE:
+               /* transformConstraintAttrs took care of these */
+               break;
+
+           default:
+               elog(ERROR, "unrecognized constraint type: %d",
+                    constraint->contype);
+               break;
+       }
+   }
+}
+
+/*
+ * transformTableConstraint
+ *     transform a Constraint node within CREATE TABLE or ALTER TABLE
+ */
+static void
+transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
+                        Constraint *constraint)
+{
+   switch (constraint->contype)
+   {
+       case CONSTR_PRIMARY:
+       case CONSTR_UNIQUE:
+           cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
+           break;
+
+       case CONSTR_CHECK:
+           cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+           break;
+
+       case CONSTR_NULL:
+       case CONSTR_NOTNULL:
+       case CONSTR_DEFAULT:
+       case CONSTR_ATTR_DEFERRABLE:
+       case CONSTR_ATTR_NOT_DEFERRABLE:
+       case CONSTR_ATTR_DEFERRED:
+       case CONSTR_ATTR_IMMEDIATE:
+           elog(ERROR, "invalid context for constraint type %d",
+                constraint->contype);
+           break;
+
+       default:
+           elog(ERROR, "unrecognized constraint type: %d",
+                constraint->contype);
+           break;
+   }
+}
+
+/*
+ * transformInhRelation
+ *
+ * Change the LIKE  portion of a CREATE TABLE statement into
+ * column definitions which recreate the user defined column portions of
+ * .
+ */
+static void
+transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
+                    InhRelation *inhRelation)
+{
+   AttrNumber  parent_attno;
+   Relation    relation;
+   TupleDesc   tupleDesc;
+   TupleConstr *constr;
+   AclResult   aclresult;
+   bool        including_defaults = false;
+   bool        including_constraints = false;
+   bool        including_indexes = false;
+   ListCell   *elem;
+
+   relation = heap_openrv(inhRelation->relation, AccessShareLock);
+
+   if (relation->rd_rel->relkind != RELKIND_RELATION)
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("inherited relation \"%s\" is not a table",
+                       inhRelation->relation->relname)));
+
+   /*
+    * Check for SELECT privilages
+    */
+   aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(),
+                                 ACL_SELECT);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_CLASS,
+                      RelationGetRelationName(relation));
+
+   tupleDesc = RelationGetDescr(relation);
+   constr = tupleDesc->constr;
+
+   foreach(elem, inhRelation->options)
+   {
+       int         option = lfirst_int(elem);
+
+       switch (option)
+       {
+           case CREATE_TABLE_LIKE_INCLUDING_DEFAULTS:
+               including_defaults = true;
+               break;
+           case CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS:
+               including_defaults = false;
+               break;
+           case CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS:
+               including_constraints = true;
+               break;
+           case CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS:
+               including_constraints = false;
+               break;
+           case CREATE_TABLE_LIKE_INCLUDING_INDEXES:
+               including_indexes = true;
+               break;
+           case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
+               including_indexes = false;
+               break;
+           default:
+               elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
+                    option);
+       }
+   }
+
+   if (including_indexes)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("LIKE INCLUDING INDEXES is not implemented")));
+
+   /*
+    * Insert the copied attributes into the cxt for the new table
+    * definition.
+    */
+   for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+        parent_attno++)
+   {
+       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
+       char       *attributeName = NameStr(attribute->attname);
+       ColumnDef  *def;
+
+       /*
+        * Ignore dropped columns in the parent.
+        */
+       if (attribute->attisdropped)
+           continue;
+
+       /*
+        * Create a new column, which is marked as NOT inherited.
+        *
+        * For constraints, ONLY the NOT NULL constraint is inherited by the
+        * new column definition per SQL99.
+        */
+       def = makeNode(ColumnDef);
+       def->colname = pstrdup(attributeName);
+       def->typename = makeTypeNameFromOid(attribute->atttypid,
+                                           attribute->atttypmod);
+       def->inhcount = 0;
+       def->is_local = true;
+       def->is_not_null = attribute->attnotnull;
+       def->raw_default = NULL;
+       def->cooked_default = NULL;
+       def->constraints = NIL;
+
+       /*
+        * Add to column list
+        */
+       cxt->columns = lappend(cxt->columns, def);
+
+       /*
+        * Copy default, if present and the default has been requested
+        */
+       if (attribute->atthasdef && including_defaults)
+       {
+           char       *this_default = NULL;
+           AttrDefault *attrdef;
+           int         i;
+
+           /* Find default in constraint structure */
+           Assert(constr != NULL);
+           attrdef = constr->defval;
+           for (i = 0; i < constr->num_defval; i++)
+           {
+               if (attrdef[i].adnum == parent_attno)
+               {
+                   this_default = attrdef[i].adbin;
+                   break;
+               }
+           }
+           Assert(this_default != NULL);
+
+           /*
+            * If default expr could contain any vars, we'd need to fix 'em,
+            * but it can't; so default is ready to apply to child.
+            */
+
+           def->cooked_default = pstrdup(this_default);
+       }
+   }
+
+   /*
+    * Copy CHECK constraints if requested, being careful to adjust
+    * attribute numbers
+    */
+   if (including_constraints && tupleDesc->constr)
+   {
+       AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
+       int         ccnum;
+
+       for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
+       {
+           char       *ccname = tupleDesc->constr->check[ccnum].ccname;
+           char       *ccbin = tupleDesc->constr->check[ccnum].ccbin;
+           Node       *ccbin_node = stringToNode(ccbin);
+           Constraint *n = makeNode(Constraint);
+
+           change_varattnos_of_a_node(ccbin_node, attmap);
+
+           n->contype = CONSTR_CHECK;
+           n->name = pstrdup(ccname);
+           n->raw_expr = NULL;
+           n->cooked_expr = nodeToString(ccbin_node);
+           n->indexspace = NULL;
+           cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+       }
+   }
+
+   /*
+    * Close the parent rel, but keep our AccessShareLock on it until xact
+    * commit.  That will prevent someone else from deleting or ALTERing the
+    * parent before the child is committed.
+    */
+   heap_close(relation, NoLock);
+}
+
+/*
+ * transformIndexConstraints
+ *     Handle UNIQUE and PRIMARY KEY constraints, which create indexes
+ */
+static void
+transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
+{
+   IndexStmt  *index;
+   List       *indexlist = NIL;
+   ListCell   *listptr;
+   ListCell   *l;
+
+   /*
+    * Run through the constraints that need to generate an index. For PRIMARY
+    * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
+    * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
+    */
+   foreach(listptr, cxt->ixconstraints)
+   {
+       Constraint *constraint = lfirst(listptr);
+       ListCell   *keys;
+       IndexElem  *iparam;
+
+       Assert(IsA(constraint, Constraint));
+       Assert((constraint->contype == CONSTR_PRIMARY)
+              || (constraint->contype == CONSTR_UNIQUE));
+
+       index = makeNode(IndexStmt);
+
+       index->unique = true;
+       index->primary = (constraint->contype == CONSTR_PRIMARY);
+       if (index->primary)
+       {
+           if (cxt->pkey != NULL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                        errmsg("multiple primary keys for table \"%s\" are not allowed",
+                               cxt->relation->relname)));
+           cxt->pkey = index;
+
+           /*
+            * In ALTER TABLE case, a primary index might already exist, but
+            * DefineIndex will check for it.
+            */
+       }
+       index->isconstraint = true;
+
+       if (constraint->name != NULL)
+           index->idxname = pstrdup(constraint->name);
+       else
+           index->idxname = NULL;      /* DefineIndex will choose name */
+
+       index->relation = cxt->relation;
+       index->accessMethod = DEFAULT_INDEX_TYPE;
+       index->options = constraint->options;
+       index->tableSpace = constraint->indexspace;
+       index->indexParams = NIL;
+       index->whereClause = NULL;
+       index->concurrent = false;
+
+       /*
+        * Make sure referenced keys exist.  If we are making a PRIMARY KEY
+        * index, also make sure they are NOT NULL, if possible. (Although we
+        * could leave it to DefineIndex to mark the columns NOT NULL, it's
+        * more efficient to get it right the first time.)
+        */
+       foreach(keys, constraint->keys)
+       {
+           char       *key = strVal(lfirst(keys));
+           bool        found = false;
+           ColumnDef  *column = NULL;
+           ListCell   *columns;
+
+           foreach(columns, cxt->columns)
+           {
+               column = (ColumnDef *) lfirst(columns);
+               Assert(IsA(column, ColumnDef));
+               if (strcmp(column->colname, key) == 0)
+               {
+                   found = true;
+                   break;
+               }
+           }
+           if (found)
+           {
+               /* found column in the new table; force it to be NOT NULL */
+               if (constraint->contype == CONSTR_PRIMARY)
+                   column->is_not_null = TRUE;
+           }
+           else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
+           {
+               /*
+                * column will be a system column in the new table, so accept
+                * it.  System columns can't ever be null, so no need to worry
+                * about PRIMARY/NOT NULL constraint.
+                */
+               found = true;
+           }
+           else if (cxt->inhRelations)
+           {
+               /* try inherited tables */
+               ListCell   *inher;
+
+               foreach(inher, cxt->inhRelations)
+               {
+                   RangeVar   *inh = (RangeVar *) lfirst(inher);
+                   Relation    rel;
+                   int         count;
+
+                   Assert(IsA(inh, RangeVar));
+                   rel = heap_openrv(inh, AccessShareLock);
+                   if (rel->rd_rel->relkind != RELKIND_RELATION)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                          errmsg("inherited relation \"%s\" is not a table",
+                                 inh->relname)));
+                   for (count = 0; count < rel->rd_att->natts; count++)
+                   {
+                       Form_pg_attribute inhattr = rel->rd_att->attrs[count];
+                       char       *inhname = NameStr(inhattr->attname);
+
+                       if (inhattr->attisdropped)
+                           continue;
+                       if (strcmp(key, inhname) == 0)
+                       {
+                           found = true;
+
+                           /*
+                            * We currently have no easy way to force an
+                            * inherited column to be NOT NULL at creation, if
+                            * its parent wasn't so already. We leave it to
+                            * DefineIndex to fix things up in this case.
+                            */
+                           break;
+                       }
+                   }
+                   heap_close(rel, NoLock);
+                   if (found)
+                       break;
+               }
+           }
+
+           /*
+            * In the ALTER TABLE case, don't complain about index keys not
+            * created in the command; they may well exist already.
+            * DefineIndex will complain about them if not, and will also take
+            * care of marking them NOT NULL.
+            */
+           if (!found && !cxt->isalter)
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                        errmsg("column \"%s\" named in key does not exist",
+                               key)));
+
+           /* Check for PRIMARY KEY(foo, foo) */
+           foreach(columns, index->indexParams)
+           {
+               iparam = (IndexElem *) lfirst(columns);
+               if (iparam->name && strcmp(key, iparam->name) == 0)
+               {
+                   if (index->primary)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                errmsg("column \"%s\" appears twice in primary key constraint",
+                                       key)));
+                   else
+                       ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                errmsg("column \"%s\" appears twice in unique constraint",
+                                       key)));
+               }
+           }
+
+           /* OK, add it to the index definition */
+           iparam = makeNode(IndexElem);
+           iparam->name = pstrdup(key);
+           iparam->expr = NULL;
+           iparam->opclass = NIL;
+           iparam->ordering = SORTBY_DEFAULT;
+           iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+           index->indexParams = lappend(index->indexParams, iparam);
+       }
+
+       indexlist = lappend(indexlist, index);
+   }
+
+   /*
+    * Scan the index list and remove any redundant index specifications. This
+    * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
+    * strict reading of SQL92 would suggest raising an error instead, but
+    * that strikes me as too anal-retentive. - tgl 2001-02-14
+    *
+    * XXX in ALTER TABLE case, it'd be nice to look for duplicate
+    * pre-existing indexes, too.
+    */
+   Assert(cxt->alist == NIL);
+   if (cxt->pkey != NULL)
+   {
+       /* Make sure we keep the PKEY index in preference to others... */
+       cxt->alist = list_make1(cxt->pkey);
+   }
+
+   foreach(l, indexlist)
+   {
+       bool        keep = true;
+       ListCell   *k;
+
+       index = lfirst(l);
+
+       /* if it's pkey, it's already in cxt->alist */
+       if (index == cxt->pkey)
+           continue;
+
+       foreach(k, cxt->alist)
+       {
+           IndexStmt  *priorindex = lfirst(k);
+
+           if (equal(index->indexParams, priorindex->indexParams))
+           {
+               /*
+                * If the prior index is as yet unnamed, and this one is
+                * named, then transfer the name to the prior index. This
+                * ensures that if we have named and unnamed constraints,
+                * we'll use (at least one of) the names for the index.
+                */
+               if (priorindex->idxname == NULL)
+                   priorindex->idxname = index->idxname;
+               keep = false;
+               break;
+           }
+       }
+
+       if (keep)
+           cxt->alist = lappend(cxt->alist, index);
+   }
+}
+
+/*
+ * transformFKConstraints
+ *     handle FOREIGN KEY constraints
+ */
+static void
+transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
+                      bool skipValidation, bool isAddConstraint)
+{
+   ListCell   *fkclist;
+
+   if (cxt->fkconstraints == NIL)
+       return;
+
+   /*
+    * If CREATE TABLE or adding a column with NULL default, we can safely
+    * skip validation of the constraint.
+    */
+   if (skipValidation)
+   {
+       foreach(fkclist, cxt->fkconstraints)
+       {
+           FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+
+           fkconstraint->skip_validation = true;
+       }
+   }
+
+   /*
+    * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD
+    * CONSTRAINT command to execute after the basic command is complete. (If
+    * called from ADD CONSTRAINT, that routine will add the FK constraints to
+    * its own subcommand list.)
+    *
+    * Note: the ADD CONSTRAINT command must also execute after any index
+    * creation commands.  Thus, this should run after
+    * transformIndexConstraints, so that the CREATE INDEX commands are
+    * already in cxt->alist.
+    */
+   if (!isAddConstraint)
+   {
+       AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
+
+       alterstmt->relation = cxt->relation;
+       alterstmt->cmds = NIL;
+       alterstmt->relkind = OBJECT_TABLE;
+
+       foreach(fkclist, cxt->fkconstraints)
+       {
+           FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+           AlterTableCmd *altercmd = makeNode(AlterTableCmd);
+
+           altercmd->subtype = AT_ProcessedConstraint;
+           altercmd->name = NULL;
+           altercmd->def = (Node *) fkconstraint;
+           alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
+       }
+
+       cxt->alist = lappend(cxt->alist, alterstmt);
+   }
+}
+
+/*
+ * transformIndexStmt - parse analysis for CREATE INDEX
+ *
+ * Note: this is a no-op for an index not using either index expressions or
+ * a predicate expression.  There are several code paths that create indexes
+ * without bothering to call this, because they know they don't have any
+ * such expressions to deal with.
+ */
+IndexStmt *
+transformIndexStmt(IndexStmt *stmt, const char *queryString)
+{
+   Relation    rel;
+   ParseState *pstate;
+   RangeTblEntry *rte;
+   ListCell   *l;
+
+   /*
+    * We must not scribble on the passed-in IndexStmt, so copy it.  (This
+    * is overkill, but easy.)
+    */
+   stmt = (IndexStmt *) copyObject(stmt);
+
+   /*
+    * Open the parent table with appropriate locking.  We must do this
+    * because addRangeTableEntry() would acquire only AccessShareLock,
+    * leaving DefineIndex() needing to do a lock upgrade with consequent
+    * risk of deadlock.  Make sure this stays in sync with the type of
+    * lock DefineIndex() wants.
+    */
+   rel = heap_openrv(stmt->relation,
+               (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
+
+   /* Set up pstate */
+   pstate = make_parsestate(NULL);
+   pstate->p_sourcetext = queryString;
+
+   /*
+    * Put the parent table into the rtable so that the expressions can
+    * refer to its fields without qualification.
+    */
+   rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
+
+   /* no to join list, yes to namespaces */
+   addRTEtoQuery(pstate, rte, false, true, true);
+
+   /* take care of the where clause */
+   if (stmt->whereClause)
+       stmt->whereClause = transformWhereClause(pstate,
+                                                stmt->whereClause,
+                                                "WHERE");
+
+   /* take care of any index expressions */
+   foreach(l, stmt->indexParams)
+   {
+       IndexElem  *ielem = (IndexElem *) lfirst(l);
+
+       if (ielem->expr)
+       {
+           ielem->expr = transformExpr(pstate, ielem->expr);
+
+           /*
+            * We check only that the result type is legitimate; this is for
+            * consistency with what transformWhereClause() checks for the
+            * predicate.  DefineIndex() will make more checks.
+            */
+           if (expression_returns_set(ielem->expr))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("index expression cannot return a set")));
+       }
+   }
+
+   /*
+    * Check that only the base rel is mentioned.
+    */
+   if (list_length(pstate->p_rtable) != 1)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                errmsg("index expressions and predicates can refer only to the table being indexed")));
+
+   free_parsestate(pstate);
+
+   /* Close relation, but keep the lock */
+   heap_close(rel, NoLock);
+
+   return stmt;
+}
+
+
+/*
+ * transformRuleStmt -
+ *   transform a CREATE RULE Statement. The action is a list of parse
+ *   trees which is transformed into a list of query trees, and we also
+ *   transform the WHERE clause if any.
+ *
+ * actions and whereClause are output parameters that receive the
+ * transformed results.
+ *
+ * Note that we must not scribble on the passed-in RuleStmt, so we do
+ * copyObject() on the actions and WHERE clause.
+ */
+void
+transformRuleStmt(RuleStmt *stmt, const char *queryString,
+                 List **actions, Node **whereClause)
+{
+   Relation    rel;
+   ParseState *pstate;
+   RangeTblEntry *oldrte;
+   RangeTblEntry *newrte;
+
+   /*
+    * To avoid deadlock, make sure the first thing we do is grab
+    * AccessExclusiveLock on the target relation.  This will be needed by
+    * DefineQueryRewrite(), and we don't want to grab a lesser lock
+    * beforehand.
+    */
+   rel = heap_openrv(stmt->relation, AccessExclusiveLock);
+
+   /* Set up pstate */
+   pstate = make_parsestate(NULL);
+   pstate->p_sourcetext = queryString;
+
+   /*
+    * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
+    * Set up their RTEs in the main pstate for use in parsing the rule
+    * qualification.
+    */
+   oldrte = addRangeTableEntryForRelation(pstate, rel,
+                                          makeAlias("*OLD*", NIL),
+                                          false, false);
+   newrte = addRangeTableEntryForRelation(pstate, rel,
+                                          makeAlias("*NEW*", NIL),
+                                          false, false);
+   /* Must override addRangeTableEntry's default access-check flags */
+   oldrte->requiredPerms = 0;
+   newrte->requiredPerms = 0;
+
+   /*
+    * They must be in the namespace too for lookup purposes, but only add the
+    * one(s) that are relevant for the current kind of rule.  In an UPDATE
+    * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
+    * there's no need to be so picky for INSERT & DELETE.  We do not add them
+    * to the joinlist.
+    */
+   switch (stmt->event)
+   {
+       case CMD_SELECT:
+           addRTEtoQuery(pstate, oldrte, false, true, true);
+           break;
+       case CMD_UPDATE:
+           addRTEtoQuery(pstate, oldrte, false, true, true);
+           addRTEtoQuery(pstate, newrte, false, true, true);
+           break;
+       case CMD_INSERT:
+           addRTEtoQuery(pstate, newrte, false, true, true);
+           break;
+       case CMD_DELETE:
+           addRTEtoQuery(pstate, oldrte, false, true, true);
+           break;
+       default:
+           elog(ERROR, "unrecognized event type: %d",
+                (int) stmt->event);
+           break;
+   }
+
+   /* take care of the where clause */
+   *whereClause = transformWhereClause(pstate,
+                                       (Node *) copyObject(stmt->whereClause),
+                                       "WHERE");
+
+   if (list_length(pstate->p_rtable) != 2)     /* naughty, naughty... */
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                errmsg("rule WHERE condition cannot contain references to other relations")));
+
+   /* aggregates not allowed (but subselects are okay) */
+   if (pstate->p_hasAggs)
+       ereport(ERROR,
+               (errcode(ERRCODE_GROUPING_ERROR),
+          errmsg("cannot use aggregate function in rule WHERE condition")));
+
+   /*
+    * 'instead nothing' rules with a qualification need a query 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 specially by the rewrite system.
+    */
+   if (stmt->actions == NIL)
+   {
+       Query      *nothing_qry = makeNode(Query);
+
+       nothing_qry->commandType = CMD_NOTHING;
+       nothing_qry->rtable = pstate->p_rtable;
+       nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
+
+       *actions = list_make1(nothing_qry);
+   }
+   else
+   {
+       ListCell   *l;
+       List       *newactions = NIL;
+
+       /*
+        * transform each statement, like parse_sub_analyze()
+        */
+       foreach(l, stmt->actions)
+       {
+           Node       *action = (Node *) lfirst(l);
+           ParseState *sub_pstate = make_parsestate(NULL);
+           Query      *sub_qry,
+                      *top_subqry;
+           bool        has_old,
+                       has_new;
+
+           /*
+            * Since outer ParseState isn't parent of inner, have to pass
+            * down the query text by hand.
+            */
+           sub_pstate->p_sourcetext = queryString;
+
+           /*
+            * Set up OLD/NEW in the rtable for this statement.  The entries
+            * are added only to relnamespace, not varnamespace, because we
+            * don't want them to be referred to by unqualified field names
+            * nor "*" in the rule actions.  We decide later whether to put
+            * them in the joinlist.
+            */
+           oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
+                                                  makeAlias("*OLD*", NIL),
+                                                  false, false);
+           newrte = addRangeTableEntryForRelation(sub_pstate, rel,
+                                                  makeAlias("*NEW*", NIL),
+                                                  false, false);
+           oldrte->requiredPerms = 0;
+           newrte->requiredPerms = 0;
+           addRTEtoQuery(sub_pstate, oldrte, false, true, false);
+           addRTEtoQuery(sub_pstate, newrte, false, true, false);
+
+           /* Transform the rule action statement */
+           top_subqry = transformStmt(sub_pstate,
+                                      (Node *) copyObject(action));
+
+           /*
+            * We cannot support utility-statement actions (eg NOTIFY) with
+            * nonempty rule WHERE conditions, because there's no way to make
+            * the utility action execute conditionally.
+            */
+           if (top_subqry->commandType == CMD_UTILITY &&
+               *whereClause != NULL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                        errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
+
+           /*
+            * If the action is INSERT...SELECT, OLD/NEW have been pushed down
+            * into the SELECT, and that's what we need to look at. (Ugly
+            * kluge ... try to fix this when we redesign querytrees.)
+            */
+           sub_qry = getInsertSelectQuery(top_subqry, NULL);
+
+           /*
+            * If the sub_qry is a setop, we cannot attach any qualifications
+            * to it, because the planner won't notice them.  This could
+            * perhaps be relaxed someday, but for now, we may as well reject
+            * such a rule immediately.
+            */
+           if (sub_qry->setOperations != NULL && *whereClause != NULL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
+
+           /*
+            * Validate action's use of OLD/NEW, qual too
+            */
+           has_old =
+               rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
+               rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
+           has_new =
+               rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
+               rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
+
+           switch (stmt->event)
+           {
+               case CMD_SELECT:
+                   if (has_old)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("ON SELECT rule cannot use OLD")));
+                   if (has_new)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("ON SELECT rule cannot use NEW")));
+                   break;
+               case CMD_UPDATE:
+                   /* both are OK */
+                   break;
+               case CMD_INSERT:
+                   if (has_old)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("ON INSERT rule cannot use OLD")));
+                   break;
+               case CMD_DELETE:
+                   if (has_new)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("ON DELETE rule cannot use NEW")));
+                   break;
+               default:
+                   elog(ERROR, "unrecognized event type: %d",
+                        (int) stmt->event);
+                   break;
+           }
+
+           /*
+            * For efficiency's sake, add OLD to the rule action's jointree
+            * only if it was actually referenced in the statement or qual.
+            *
+            * For INSERT, NEW is not really a relation (only a reference to
+            * the to-be-inserted tuple) and should never be added to the
+            * jointree.
+            *
+            * For UPDATE, we treat NEW as being another kind of reference to
+            * OLD, because it represents references to *transformed* tuples
+            * of the existing relation.  It would be wrong to enter NEW
+            * separately in the jointree, since that would cause a double
+            * join of the updated relation.  It's also wrong to fail to make
+            * a jointree entry if only NEW and not OLD is mentioned.
+            */
+           if (has_old || (has_new && stmt->event == CMD_UPDATE))
+           {
+               /*
+                * If sub_qry is a setop, manipulating its jointree will do no
+                * good at all, because the jointree is dummy. (This should be
+                * a can't-happen case because of prior tests.)
+                */
+               if (sub_qry->setOperations != NULL)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                            errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
+               /* hack so we can use addRTEtoQuery() */
+               sub_pstate->p_rtable = sub_qry->rtable;
+               sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
+               addRTEtoQuery(sub_pstate, oldrte, true, false, false);
+               sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
+           }
+
+           newactions = lappend(newactions, top_subqry);
+
+           free_parsestate(sub_pstate);
+       }
+
+       *actions = newactions;
+   }
+
+   free_parsestate(pstate);
+
+   /* Close relation, but keep the exclusive lock */
+   heap_close(rel, NoLock);
+}
+
+
+/*
+ * transformAlterTableStmt -
+ *     parse analysis for ALTER TABLE
+ *
+ * Returns a List of utility commands to be done in sequence.  One of these
+ * will be the transformed AlterTableStmt, but there may be additional actions
+ * to be done before and after the actual AlterTable() call.
+ */
+List *
+transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
+{
+   Relation    rel;
+   ParseState *pstate;
+   CreateStmtContext cxt;
+   List       *result;
+   List       *save_alist;
+   ListCell   *lcmd,
+              *l;
+   List       *newcmds = NIL;
+   bool        skipValidation = true;
+   AlterTableCmd *newcmd;
+
+   /*
+    * We must not scribble on the passed-in AlterTableStmt, so copy it.
+    * (This is overkill, but easy.)
+    */
+   stmt = (AlterTableStmt *) copyObject(stmt);
+
+   /*
+    * Acquire exclusive lock on the target relation, which will be held
+    * until end of transaction.  This ensures any decisions we make here
+    * based on the state of the relation will still be good at execution.
+    * We must get exclusive lock now because execution will; taking a lower
+    * grade lock now and trying to upgrade later risks deadlock.
+    */
+   rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+
+   /* Set up pstate */
+   pstate = make_parsestate(NULL);
+   pstate->p_sourcetext = queryString;
+
+   cxt.stmtType = "ALTER TABLE";
+   cxt.relation = stmt->relation;
+   cxt.rel = rel;
+   cxt.inhRelations = NIL;
+   cxt.isalter = true;
+   cxt.hasoids = false;        /* need not be right */
+   cxt.columns = NIL;
+   cxt.ckconstraints = NIL;
+   cxt.fkconstraints = NIL;
+   cxt.ixconstraints = NIL;
+   cxt.blist = NIL;
+   cxt.alist = NIL;
+   cxt.pkey = NULL;
+
+   /*
+    * The only subtypes that currently require parse transformation handling
+    * are ADD COLUMN and ADD CONSTRAINT.  These largely re-use code from
+    * CREATE TABLE.
+    */
+   foreach(lcmd, stmt->cmds)
+   {
+       AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+
+       switch (cmd->subtype)
+       {
+           case AT_AddColumn:
+               {
+                   ColumnDef  *def = (ColumnDef *) cmd->def;
+
+                   Assert(IsA(cmd->def, ColumnDef));
+                   transformColumnDefinition(pstate, &cxt,
+                                             (ColumnDef *) cmd->def);
+
+                   /*
+                    * If the column has a non-null default, we can't skip
+                    * validation of foreign keys.
+                    */
+                   if (((ColumnDef *) cmd->def)->raw_default != NULL)
+                       skipValidation = false;
+
+                   newcmds = lappend(newcmds, cmd);
+
+                   /*
+                    * Convert an ADD COLUMN ... NOT NULL constraint to a
+                    * separate command
+                    */
+                   if (def->is_not_null)
+                   {
+                       /* Remove NOT NULL from AddColumn */
+                       def->is_not_null = false;
+
+                       /* Add as a separate AlterTableCmd */
+                       newcmd = makeNode(AlterTableCmd);
+                       newcmd->subtype = AT_SetNotNull;
+                       newcmd->name = pstrdup(def->colname);
+                       newcmds = lappend(newcmds, newcmd);
+                   }
+
+                   /*
+                    * All constraints are processed in other ways. Remove the
+                    * original list
+                    */
+                   def->constraints = NIL;
+
+                   break;
+               }
+           case AT_AddConstraint:
+
+               /*
+                * The original AddConstraint cmd node doesn't go to newcmds
+                */
+
+               if (IsA(cmd->def, Constraint))
+                   transformTableConstraint(pstate, &cxt,
+                                            (Constraint *) cmd->def);
+               else if (IsA(cmd->def, FkConstraint))
+               {
+                   cxt.fkconstraints = lappend(cxt.fkconstraints, cmd->def);
+                   skipValidation = false;
+               }
+               else
+                   elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(cmd->def));
+               break;
+
+           case AT_ProcessedConstraint:
+
+               /*
+                * Already-transformed ADD CONSTRAINT, so just make it look
+                * like the standard case.
+                */
+               cmd->subtype = AT_AddConstraint;
+               newcmds = lappend(newcmds, cmd);
+               break;
+
+           default:
+               newcmds = lappend(newcmds, cmd);
+               break;
+       }
+   }
+
+   /*
+    * transformIndexConstraints wants cxt.alist to contain only index
+    * statements, so transfer anything we already have into save_alist.
+    * immediately.
+    */
+   save_alist = cxt.alist;
+   cxt.alist = NIL;
+
+   /* Postprocess index and FK constraints */
+   transformIndexConstraints(pstate, &cxt);
+
+   transformFKConstraints(pstate, &cxt, skipValidation, true);
+
+   /*
+    * Push any index-creation commands into the ALTER, so that they can be
+    * scheduled nicely by tablecmds.c.  Note that tablecmds.c assumes that
+    * the IndexStmt attached to an AT_AddIndex subcommand has already been
+    * through transformIndexStmt.
+    */
+   foreach(l, cxt.alist)
+   {
+       Node       *idxstmt = (Node *) lfirst(l);
+
+       Assert(IsA(idxstmt, IndexStmt));
+       newcmd = makeNode(AlterTableCmd);
+       newcmd->subtype = AT_AddIndex;
+       newcmd->def = (Node *) transformIndexStmt((IndexStmt *) idxstmt,
+                                                 queryString);
+       newcmds = lappend(newcmds, newcmd);
+   }
+   cxt.alist = NIL;
+
+   /* Append any CHECK or FK constraints to the commands list */
+   foreach(l, cxt.ckconstraints)
+   {
+       newcmd = makeNode(AlterTableCmd);
+       newcmd->subtype = AT_AddConstraint;
+       newcmd->def = (Node *) lfirst(l);
+       newcmds = lappend(newcmds, newcmd);
+   }
+   foreach(l, cxt.fkconstraints)
+   {
+       newcmd = makeNode(AlterTableCmd);
+       newcmd->subtype = AT_AddConstraint;
+       newcmd->def = (Node *) lfirst(l);
+       newcmds = lappend(newcmds, newcmd);
+   }
+
+   /* Close rel but keep lock */
+   relation_close(rel, NoLock);
+
+   /*
+    * Output results.
+    */
+   stmt->cmds = newcmds;
+
+   result = lappend(cxt.blist, stmt);
+   result = list_concat(result, cxt.alist);
+   result = list_concat(result, save_alist);
+
+   return result;
+}
+
+
+/*
+ * Preprocess a list of column constraint clauses
+ * to attach constraint attributes to their primary constraint nodes
+ * and detect inconsistent/misplaced constraint attributes.
+ *
+ * NOTE: currently, attributes are only supported for FOREIGN KEY primary
+ * constraints, but someday they ought to be supported for other constraints.
+ */
+static void
+transformConstraintAttrs(List *constraintList)
+{
+   Node       *lastprimarynode = NULL;
+   bool        saw_deferrability = false;
+   bool        saw_initially = false;
+   ListCell   *clist;
+
+   foreach(clist, constraintList)
+   {
+       Node       *node = lfirst(clist);
+
+       if (!IsA(node, Constraint))
+       {
+           lastprimarynode = node;
+           /* reset flags for new primary node */
+           saw_deferrability = false;
+           saw_initially = false;
+       }
+       else
+       {
+           Constraint *con = (Constraint *) node;
+
+           switch (con->contype)
+           {
+               case CONSTR_ATTR_DEFERRABLE:
+                   if (lastprimarynode == NULL ||
+                       !IsA(lastprimarynode, FkConstraint))
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("misplaced DEFERRABLE clause")));
+                   if (saw_deferrability)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
+                   saw_deferrability = true;
+                   ((FkConstraint *) lastprimarynode)->deferrable = true;
+                   break;
+               case CONSTR_ATTR_NOT_DEFERRABLE:
+                   if (lastprimarynode == NULL ||
+                       !IsA(lastprimarynode, FkConstraint))
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("misplaced NOT DEFERRABLE clause")));
+                   if (saw_deferrability)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
+                   saw_deferrability = true;
+                   ((FkConstraint *) lastprimarynode)->deferrable = false;
+                   if (saw_initially &&
+                       ((FkConstraint *) lastprimarynode)->initdeferred)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+                   break;
+               case CONSTR_ATTR_DEFERRED:
+                   if (lastprimarynode == NULL ||
+                       !IsA(lastprimarynode, FkConstraint))
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("misplaced INITIALLY DEFERRED clause")));
+                   if (saw_initially)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
+                   saw_initially = true;
+                   ((FkConstraint *) lastprimarynode)->initdeferred = true;
+
+                   /*
+                    * If only INITIALLY DEFERRED appears, assume DEFERRABLE
+                    */
+                   if (!saw_deferrability)
+                       ((FkConstraint *) lastprimarynode)->deferrable = true;
+                   else if (!((FkConstraint *) lastprimarynode)->deferrable)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+                   break;
+               case CONSTR_ATTR_IMMEDIATE:
+                   if (lastprimarynode == NULL ||
+                       !IsA(lastprimarynode, FkConstraint))
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                           errmsg("misplaced INITIALLY IMMEDIATE clause")));
+                   if (saw_initially)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
+                   saw_initially = true;
+                   ((FkConstraint *) lastprimarynode)->initdeferred = false;
+                   break;
+               default:
+                   /* Otherwise it's not an attribute */
+                   lastprimarynode = node;
+                   /* reset flags for new primary node */
+                   saw_deferrability = false;
+                   saw_initially = false;
+                   break;
+           }
+       }
+   }
+}
+
+/*
+ * Special handling of type definition for a column
+ */
+static void
+transformColumnType(ParseState *pstate, ColumnDef *column)
+{
+   /*
+    * All we really need to do here is verify that the type is valid.
+    */
+   Type        ctype = typenameType(pstate, column->typename);
+
+   ReleaseSysCache(ctype);
+}
+
+
+/*
+ * transformCreateSchemaStmt -
+ *   analyzes the CREATE SCHEMA statement
+ *
+ * Split the schema element list into individual commands and place
+ * them in the result list in an order such that there are no forward
+ * references (e.g. GRANT to a table created later in the list). Note
+ * that the logic we use for determining forward references is
+ * presently quite incomplete.
+ *
+ * SQL92 also allows constraints to make forward references, so thumb through
+ * the table columns and move forward references to a posterior alter-table
+ * command.
+ *
+ * The result is a list of parse nodes that still need to be analyzed ---
+ * but we can't analyze the later commands until we've executed the earlier
+ * ones, because of possible inter-object references.
+ *
+ * Note: this breaks the rules a little bit by modifying schema-name fields
+ * within passed-in structs.  However, the transformation would be the same
+ * if done over, so it should be all right to scribble on the input to this
+ * extent.
+ */
+List *
+transformCreateSchemaStmt(CreateSchemaStmt *stmt)
+{
+   CreateSchemaStmtContext cxt;
+   List       *result;
+   ListCell   *elements;
+
+   cxt.stmtType = "CREATE SCHEMA";
+   cxt.schemaname = stmt->schemaname;
+   cxt.authid = stmt->authid;
+   cxt.sequences = NIL;
+   cxt.tables = NIL;
+   cxt.views = NIL;
+   cxt.indexes = NIL;
+   cxt.triggers = NIL;
+   cxt.grants = NIL;
+
+   /*
+    * Run through each schema element in the schema element list. Separate
+    * statements by type, and do preliminary analysis.
+    */
+   foreach(elements, stmt->schemaElts)
+   {
+       Node       *element = lfirst(elements);
+
+       switch (nodeTag(element))
+       {
+           case T_CreateSeqStmt:
+               {
+                   CreateSeqStmt *elp = (CreateSeqStmt *) element;
+
+                   setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
+                   cxt.sequences = lappend(cxt.sequences, element);
+               }
+               break;
+
+           case T_CreateStmt:
+               {
+                   CreateStmt *elp = (CreateStmt *) element;
+
+                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
+
+                   /*
+                    * XXX todo: deal with constraints
+                    */
+                   cxt.tables = lappend(cxt.tables, element);
+               }
+               break;
+
+           case T_ViewStmt:
+               {
+                   ViewStmt   *elp = (ViewStmt *) element;
+
+                   setSchemaName(cxt.schemaname, &elp->view->schemaname);
+
+                   /*
+                    * XXX todo: deal with references between views
+                    */
+                   cxt.views = lappend(cxt.views, element);
+               }
+               break;
+
+           case T_IndexStmt:
+               {
+                   IndexStmt  *elp = (IndexStmt *) element;
+
+                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
+                   cxt.indexes = lappend(cxt.indexes, element);
+               }
+               break;
+
+           case T_CreateTrigStmt:
+               {
+                   CreateTrigStmt *elp = (CreateTrigStmt *) element;
+
+                   setSchemaName(cxt.schemaname, &elp->relation->schemaname);
+                   cxt.triggers = lappend(cxt.triggers, element);
+               }
+               break;
+
+           case T_GrantStmt:
+               cxt.grants = lappend(cxt.grants, element);
+               break;
+
+           default:
+               elog(ERROR, "unrecognized node type: %d",
+                    (int) nodeTag(element));
+       }
+   }
+
+   result = NIL;
+   result = list_concat(result, cxt.sequences);
+   result = list_concat(result, cxt.tables);
+   result = list_concat(result, cxt.views);
+   result = list_concat(result, cxt.indexes);
+   result = list_concat(result, cxt.triggers);
+   result = list_concat(result, cxt.grants);
+
+   return result;
+}
+
+/*
+ * setSchemaName
+ *     Set or check schema name in an element of a CREATE SCHEMA command
+ */
+static void
+setSchemaName(char *context_schema, char **stmt_schema_name)
+{
+   if (*stmt_schema_name == NULL)
+       *stmt_schema_name = context_schema;
+   else if (strcmp(context_schema, *stmt_schema_name) != 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
+                errmsg("CREATE specifies a schema (%s) "
+                       "different from the one being created (%s)",
+                       *stmt_schema_name, context_schema)));
+}
index e57e2ab046cd1df06c71096d832e6190f342a56f..540f34036801a13f5113eb213d65a9b8e2fa7719 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.120 2007/04/27 22:05:48 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.121 2007/06/23 22:12:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,8 +20,8 @@
 #include "catalog/pg_rewrite.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
-#include "parser/analyze.h"
 #include "parser/parse_expr.h"
+#include "parser/parse_utilcmd.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rewriteSupport.h"
@@ -191,7 +191,7 @@ DefineRule(RuleStmt *stmt, const char *queryString)
    Node       *whereClause;
 
    /* Parse analysis ... */
-   analyzeRuleStmt(stmt, queryString, &actions, &whereClause);
+   transformRuleStmt(stmt, queryString, &actions, &whereClause);
 
    /* ... and execution */
    DefineQueryRewrite(stmt->rulename,
index 724f2e028d0c6c45d66331b368af61682fa1732a..94fd65e5d448cf3e691964c6cd24538565b0d688 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.533 2007/04/30 16:37:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.534 2007/06/23 22:12:52 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -165,7 +165,7 @@ static int  UseNewLine = 0;     /* Use EOF as query delimiters */
 static int InteractiveBackend(StringInfo inBuf);
 static int SocketBackend(StringInfo inBuf);
 static int ReadCommand(StringInfo inBuf);
-static List *pg_rewrite_queries(List *querytree_list);
+static List *pg_rewrite_query(Query *query);
 static bool check_log_statement(List *stmt_list);
 static int errdetail_execute(List *raw_parsetree_list);
 static int errdetail_params(ParamListInfo params);
@@ -567,6 +567,7 @@ List *
 pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
                       Oid *paramTypes, int numParams)
 {
+   Query      *query;
    List       *querytree_list;
 
    /*
@@ -575,8 +576,7 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
    if (log_parser_stats)
        ResetUsage();
 
-   querytree_list = parse_analyze(parsetree, query_string,
-                                  paramTypes, numParams);
+   query = parse_analyze(parsetree, query_string, paramTypes, numParams);
 
    if (log_parser_stats)
        ShowUsage("PARSE ANALYSIS STATISTICS");
@@ -584,68 +584,55 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
    /*
     * (2) Rewrite the queries, as necessary
     */
-   querytree_list = pg_rewrite_queries(querytree_list);
+   querytree_list = pg_rewrite_query(query);
 
    return querytree_list;
 }
 
 /*
- * Perform rewriting of a list of queries produced by parse analysis.
+ * Perform rewriting of a query produced by parse analysis.
  *
- * Note: queries must just have come from the parser, because we do not do
- * AcquireRewriteLocks() on them.
+ * Note: query must just have come from the parser, because we do not do
+ * AcquireRewriteLocks() on it.
  */
 static List *
-pg_rewrite_queries(List *querytree_list)
+pg_rewrite_query(Query *query)
 {
-   List       *new_list = NIL;
-   ListCell   *list_item;
+   List       *querytree_list;
 
    if (log_parser_stats)
        ResetUsage();
 
-   /*
-    * rewritten queries are collected in new_list.  Note there may be more or
-    * fewer than in the original list.
-    */
-   foreach(list_item, querytree_list)
-   {
-       Query      *querytree = (Query *) lfirst(list_item);
-
-       if (Debug_print_parse)
-           elog_node_display(DEBUG1, "parse tree", querytree,
-                             Debug_pretty_print);
-
-       if (querytree->commandType == CMD_UTILITY)
-       {
-           /* don't rewrite utilities, just dump 'em into new_list */
-           new_list = lappend(new_list, querytree);
-       }
-       else
-       {
-           /* rewrite regular queries */
-           List       *rewritten = QueryRewrite(querytree);
+   if (Debug_print_parse)
+       elog_node_display(DEBUG1, "parse tree", query,
+                         Debug_pretty_print);
 
-           new_list = list_concat(new_list, rewritten);
-       }
+   if (query->commandType == CMD_UTILITY)
+   {
+       /* don't rewrite utilities, just dump 'em into result list */
+       querytree_list = list_make1(query);
+   }
+   else
+   {
+       /* rewrite regular queries */
+       querytree_list = QueryRewrite(query);
    }
-
-   querytree_list = new_list;
 
    if (log_parser_stats)
        ShowUsage("REWRITER STATISTICS");
 
 #ifdef COPY_PARSE_PLAN_TREES
+   /* Optional debugging check: pass querytree output through copyObject() */
+   {
+       List       *new_list;
 
-   /*
-    * Optional debugging check: pass querytree output through copyObject()
-    */
-   new_list = (List *) copyObject(querytree_list);
-   /* This checks both copyObject() and the equal() routines... */
-   if (!equal(new_list, querytree_list))
-       elog(WARNING, "copyObject() failed to produce an equal parse tree");
-   else
-       querytree_list = new_list;
+       new_list = (List *) copyObject(querytree_list);
+       /* This checks both copyObject() and the equal() routines... */
+       if (!equal(new_list, querytree_list))
+           elog(WARNING, "copyObject() failed to produce equal parse tree");
+       else
+           querytree_list = new_list;
+   }
 #endif
 
    if (Debug_print_rewritten)
@@ -1139,6 +1126,7 @@ exec_parse_message(const char *query_string,  /* string to execute */
 
    if (parsetree_list != NIL)
    {
+       Query      *query;
        int         i;
 
        raw_parse_tree = (Node *) linitial(parsetree_list);
@@ -1175,10 +1163,10 @@ exec_parse_message(const char *query_string,    /* string to execute */
        if (log_parser_stats)
            ResetUsage();
 
-       querytree_list = parse_analyze_varparams(copyObject(raw_parse_tree),
-                                                query_string,
-                                                ¶mTypes,
-                                                &numParams);
+       query = parse_analyze_varparams(copyObject(raw_parse_tree),
+                                       query_string,
+                                       ¶mTypes,
+                                       &numParams);
 
        /*
         * Check all parameter types got determined.
@@ -1197,7 +1185,7 @@ exec_parse_message(const char *query_string,  /* string to execute */
        if (log_parser_stats)
            ShowUsage("PARSE ANALYSIS STATISTICS");
 
-       querytree_list = pg_rewrite_queries(querytree_list);
+       querytree_list = pg_rewrite_query(query);
 
        /*
         * If this is the unnamed statement and it has parameters, defer query
index baa203a2d1986626810c0a398826ab97476ea849..c334743adaf036bbeb0a1bb533d9c2cdbaf5174d 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.280 2007/05/30 20:12:01 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.281 2007/06/23 22:12:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@
 #include "commands/vacuum.h"
 #include "commands/view.h"
 #include "miscadmin.h"
-#include "parser/analyze.h"
+#include "parser/parse_utilcmd.h"
 #include "postmaster/bgwriter.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteRemove.h"
@@ -544,17 +544,47 @@ ProcessUtility(Node *parsetree,
 
        case T_CreateStmt:
            {
+               List        *stmts;
+               ListCell    *l;
                Oid         relOid;
 
-               relOid = DefineRelation((CreateStmt *) parsetree,
-                                       RELKIND_RELATION);
+               /* Run parse analysis ... */
+               stmts = transformCreateStmt((CreateStmt *) parsetree,
+                                           queryString);
 
-               /*
-                * Let AlterTableCreateToastTable decide if this one needs a
-                * secondary relation too.
-                */
-               CommandCounterIncrement();
-               AlterTableCreateToastTable(relOid);
+               /* ... and do it */
+               foreach(l, stmts)
+               {
+                   Node   *stmt = (Node *) lfirst(l);
+
+                   if (IsA(stmt, CreateStmt))
+                   {
+                       /* Create the table itself */
+                       relOid = DefineRelation((CreateStmt *) stmt,
+                                               RELKIND_RELATION);
+
+                       /*
+                        * Let AlterTableCreateToastTable decide if this one
+                        * needs a secondary relation too.
+                        */
+                       CommandCounterIncrement();
+                       AlterTableCreateToastTable(relOid);
+                   }
+                   else
+                   {
+                       /* Recurse for anything else */
+                       ProcessUtility(stmt,
+                                      queryString,
+                                      params,
+                                      false,
+                                      None_Receiver,
+                                      NULL);
+                   }
+
+                   /* Need CCI between commands */
+                   if (lnext(l) != NULL)
+                       CommandCounterIncrement();
+               }
            }
            break;
 
@@ -693,7 +723,40 @@ ProcessUtility(Node *parsetree,
            break;
 
        case T_AlterTableStmt:
-           AlterTable((AlterTableStmt *) parsetree);
+           {
+               List        *stmts;
+               ListCell    *l;
+
+               /* Run parse analysis ... */
+               stmts = transformAlterTableStmt((AlterTableStmt *) parsetree,
+                                               queryString);
+
+               /* ... and do it */
+               foreach(l, stmts)
+               {
+                   Node   *stmt = (Node *) lfirst(l);
+
+                   if (IsA(stmt, AlterTableStmt))
+                   {
+                       /* Do the table alteration proper */
+                       AlterTable((AlterTableStmt *) stmt);
+                   }
+                   else
+                   {
+                       /* Recurse for anything else */
+                       ProcessUtility(stmt,
+                                      queryString,
+                                      params,
+                                      false,
+                                      None_Receiver,
+                                      NULL);
+                   }
+
+                   /* Need CCI between commands */
+                   if (lnext(l) != NULL)
+                       CommandCounterIncrement();
+               }
+           }
            break;
 
        case T_AlterDomainStmt:
@@ -812,7 +875,7 @@ ProcessUtility(Node *parsetree,
                CheckRelationOwnership(stmt->relation, true);
 
                /* Run parse analysis ... */
-               stmt = analyzeIndexStmt(stmt, queryString);
+               stmt = transformIndexStmt(stmt, queryString);
 
                /* ... and do it */
                DefineIndex(stmt->relation,     /* relation */
@@ -1605,7 +1668,7 @@ CreateCommandTag(Node *parsetree)
 
                /*
                 * We might be supporting ALTER INDEX here, so set the
-                * completion table appropriately. Catch all other
+                * completion tag appropriately. Catch all other
                 * possibilities with ALTER TABLE
                 */
 
index b500024e932880e2677961721cadfbf2fecbf48e..97ee524a0b2e57ee3156c933c4d1be62f60b3972 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.58 2007/03/17 00:11:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.59 2007/06/23 22:12:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,8 @@ extern TargetEntry *makeTargetEntry(Expr *expr,
 
 extern TargetEntry *flatCopyTargetEntry(TargetEntry *src_tle);
 
+extern FromExpr *makeFromExpr(List *fromlist, Node *quals);
+
 extern Const *makeConst(Oid consttype,
          int32 consttypmod,
          int constlen,
index 179d7a765dbf2c2876a9a1e8f97a17aaaf2bd5ba..50bb6c2048fb736c3d37d60415d843e416ef09e3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.348 2007/04/27 22:05:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.349 2007/06/23 22:12:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,7 @@
 typedef enum QuerySource
 {
    QSRC_ORIGINAL,              /* original parsetree (explicit query) */
-   QSRC_PARSER,                /* added by parse analysis */
+   QSRC_PARSER,                /* added by parse analysis (now unused) */
    QSRC_INSTEAD_RULE,          /* added by unconditional INSTEAD rule */
    QSRC_QUAL_INSTEAD_RULE,     /* added by conditional INSTEAD rule */
    QSRC_NON_INSTEAD_RULE       /* added by non-INSTEAD rule */
@@ -799,10 +799,12 @@ typedef struct SetOperationStmt
 /*****************************************************************************
  *     Other Statements (no optimizations required)
  *
- *     Some of them require a little bit of transformation (which is also
- *     done by transformStmt). The whole structure is then passed on to
- *     ProcessUtility (by-passing the optimization step) as the utilityStmt
- *     field in Query.
+ *     These are not touched by parser/analyze.c except to put them into
+ *     the utilityStmt field of a Query.  This is eventually passed to
+ *     ProcessUtility (by-passing rewriting and planning).  Some of the
+ *     statements do need attention from parse analysis, and this is
+ *     done by routines in parser/parse_utilcmd.c after ProcessUtility
+ *     receives the command for execution.
  *****************************************************************************/
 
 /*
@@ -886,7 +888,7 @@ typedef enum AlterTableType
    AT_ReAddIndex,              /* internal to commands/tablecmds.c */
    AT_AddConstraint,           /* add constraint */
    AT_ProcessedConstraint,     /* pre-processed add constraint (local in
-                                * parser/analyze.c) */
+                                * parser/parse_utilcmd.c) */
    AT_DropConstraint,          /* drop constraint */
    AT_DropConstraintQuietly,   /* drop constraint, no error/warning (local in
                                 * commands/tablecmds.c) */
@@ -1083,7 +1085,7 @@ typedef struct CreateStmt
  * relation).  We should never have both in the same node!
  *
  * Constraint attributes (DEFERRABLE etc) are initially represented as
- * separate Constraint nodes for simplicity of parsing.  analyze.c makes
+ * separate Constraint nodes for simplicity of parsing.  parse_utilcmd.c makes
  * a pass through the constraints list to attach the info to the appropriate
  * FkConstraint node (and, perhaps, someday to other kinds of constraints).
  * ----------
index 033dce604620687812a8825c789fa7715c73333c..5087d20a60a99e3cb99d31cc7436b4b40f191c7d 100644 (file)
@@ -1,12 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * analyze.h
+ *     parse analysis for optimizable statements
  *
  *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.36 2007/03/13 00:33:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.37 2007/06/23 22:12:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parse_node.h"
 
 
-extern List *parse_analyze(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze(Node *parseTree, const char *sourceText,
              Oid *paramTypes, int numParams);
-extern List *parse_analyze_varparams(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
                        Oid **paramTypes, int *numParams);
-extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
 
-extern IndexStmt *analyzeIndexStmt(IndexStmt *stmt, const char *queryString);
-extern void analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
-               List **actions, Node **whereClause);
-extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
+extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
+extern Query *transformStmt(ParseState *pstate, Node *parseTree);
+
 extern void CheckSelectLocking(Query *qry);
 extern void applyLockingClause(Query *qry, Index rtindex,
                   bool forUpdate, bool noWait);
index 7382a4f531caceadf48d418a410cd379eae5d504..a1d84801fc5b7380b996e2eb65db56948b889a6a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.51 2007/01/05 22:19:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.52 2007/06/23 22:12:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,6 +83,7 @@ typedef struct ParseState
 } ParseState;
 
 extern ParseState *make_parsestate(ParseState *parentParseState);
+extern void free_parsestate(ParseState *pstate);
 extern int parser_errposition(ParseState *pstate, int location);
 
 extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
new file mode 100644 (file)
index 0000000..f9ca398
--- /dev/null
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_utilcmd.h
+ *     parse analysis for utility commands
+ *
+ *
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/parser/parse_utilcmd.h,v 1.1 2007/06/23 22:12:52 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_UTILCMD_H
+#define PARSE_UTILCMD_H
+
+#include "parser/parse_node.h"
+
+
+extern List *transformCreateStmt(CreateStmt *stmt, const char *queryString);
+extern List *transformAlterTableStmt(AlterTableStmt *stmt,
+                                    const char *queryString);
+extern IndexStmt *transformIndexStmt(IndexStmt *stmt, const char *queryString);
+extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
+                             List **actions, Node **whereClause);
+extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
+
+#endif   /* PARSE_UTILCMD_H */