From 46379d6e60f0f95e127a5045ca1fa74dfdc48a85 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 23 Jun 2007 22:12:52 +0000
Subject: [PATCH] Separate parse-analysis for utility commands out of
 parser/analyze.c (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).
---
 src/backend/commands/indexcmds.c     |    4 +-
 src/backend/commands/prepare.c       |   18 +-
 src/backend/commands/schemacmds.c    |   50 +-
 src/backend/commands/tablecmds.c     |   57 +-
 src/backend/commands/view.c          |   10 +-
 src/backend/nodes/makefuncs.c        |   16 +-
 src/backend/optimizer/util/clauses.c |   14 +-
 src/backend/parser/Makefile          |    4 +-
 src/backend/parser/README            |   11 +-
 src/backend/parser/analyze.c         | 2038 ++------------------------
 src/backend/parser/gram.y            |   11 +-
 src/backend/parser/parse_clause.c    |   22 +-
 src/backend/parser/parse_expr.c      |   10 +-
 src/backend/parser/parse_node.c      |   35 +-
 src/backend/parser/parse_utilcmd.c   | 1800 +++++++++++++++++++++++
 src/backend/rewrite/rewriteDefine.c  |    6 +-
 src/backend/tcop/postgres.c          |   88 +-
 src/backend/tcop/utility.c           |   89 +-
 src/include/nodes/makefuncs.h        |    4 +-
 src/include/nodes/parsenodes.h       |   18 +-
 src/include/parser/analyze.h         |   15 +-
 src/include/parser/parse_node.h      |    3 +-
 src/include/parser/parse_utilcmd.h   |   28 +
 23 files changed, 2199 insertions(+), 2152 deletions(-)
 create mode 100644 src/backend/parser/parse_utilcmd.c
 create mode 100644 src/include/parser/parse_utilcmd.h

diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 24cb898b6a0..98dad737133 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -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),
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index de999a36376..38055997faa 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -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:
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 5a03c7780f3..b103667935f 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -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 */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b9bebde8f10..f50b59d0d8f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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));
 		}
 	}
 }
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 83f26f73ffb..f8dac126439 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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");
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 9b7f42e4631..d9d61d4f0bb 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -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
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 2cf0ffd28b0..41215446a35 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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 */
 
 	/*
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index 3099f77ca64..2296dcb6206 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -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
 
diff --git a/src/backend/parser/README b/src/backend/parser/README
index effa9008fa9..35a7a687636 100644
--- a/src/backend/parser/README
+++ b/src/backend/parser/README
@@ -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)
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a676bf11025..3135d852467 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -10,92 +10,34 @@
  * 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
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6e894c1799a..d9e981bb572 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 4c1fb0cc4c1..28717020e34 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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");
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index da4bcf208f9..754e18d687c 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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)
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index a8dfa2666bb..a5e6026ebab 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -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"
@@ -27,9 +28,11 @@
 #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
index 00000000000..37822251943
--- /dev/null
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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)));
+}
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index e57e2ab046c..540f3403680 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 724f2e028d0..94fd65e5d44 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index baa203a2d19..c334743adaf 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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
 				 */
 
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index b500024e932..97ee524a0b2 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -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,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 179d7a765db..50bb6c2048f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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).
  * ----------
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 033dce60462..5087d20a60a 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,16 +17,14 @@
 #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);
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 7382a4f531c..a1d84801fc5 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -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
index 00000000000..f9ca398e253
--- /dev/null
+++ b/src/include/parser/parse_utilcmd.h
@@ -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 */
-- 
GitLab