Separate parse-analysis for utility commands out of parser/analyze.c
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 23 Jun 2007 22:12:52 +0000 (22:12 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 <subtable> portion of a CREATE TABLE statement into
- * column definitions which recreate the user defined column portions of
- * <subtable>.
- *
- * 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 <subtable> portion of a CREATE TABLE statement into
+ * column definitions which recreate the user defined column portions of
+ * <subtable>.
+ */
+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,
-                                                                                                &paramTypes,
-                                                                                                &numParams);
+               query = parse_analyze_varparams(copyObject(raw_parse_tree),
+                                                                               query_string,
+                                                                               &paramTypes,
+                                                                               &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 */