OSDN Git Service

Fix outdated comment
[pg-rex/syncrep.git] / src / backend / parser / parse_utilcmd.c
index 287e82d..d40a963 100644 (file)
  * a quick copyObject() call before manipulating the query tree.
  *
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.4 2007/10/29 19:40:40 tgl Exp $
+ *     src/backend/parser/parse_utilcmd.c
  *
  *-------------------------------------------------------------------------
  */
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
+#include "commands/comment.h"
 #include "commands/defrem.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
-#include "parser/gramparse.h"
 #include "parser/parse_clause.h"
+#include "parser/parse_collate.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
+#include "parser/parse_target.h"
 #include "parser/parse_type.h"
 #include "parser/parse_utilcmd.h"
+#include "parser/parser.h"
 #include "rewrite/rewriteManip.h"
+#include "storage/lock.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/relcache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /* State shared by transformCreateStmt and its subroutines */
 typedef struct
 {
-       const char *stmtType;           /* "CREATE TABLE" or "ALTER TABLE" */
+       ParseState *pstate;                     /* overall parser state */
+       const char *stmtType;           /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
        RangeVar   *relation;           /* relation to create */
        Relation        rel;                    /* opened/locked rel, if ALTER */
        List       *inhRelations;       /* relations to inherit from */
@@ -90,27 +101,28 @@ typedef struct
 } CreateSchemaStmtContext;
 
 
-static void transformColumnDefinition(ParseState *pstate,
-                                                 CreateStmtContext *cxt,
+static void transformColumnDefinition(CreateStmtContext *cxt,
                                                  ColumnDef *column);
-static void transformTableConstraint(ParseState *pstate,
-                                                CreateStmtContext *cxt,
+static void transformTableConstraint(CreateStmtContext *cxt,
                                                 Constraint *constraint);
-static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
+static void transformInhRelation(CreateStmtContext *cxt,
                                         InhRelation *inhrelation);
-static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt, 
-                                                       Relation parent_index, AttrNumber *attmap);
+static void transformOfType(CreateStmtContext *cxt,
+                               TypeName *ofTypename);
+static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt);
+static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
+                                               Relation parent_index, AttrNumber *attmap);
+static List *get_collation(Oid collation, Oid actual_datatype);
 static List *get_opclass(Oid opclass, Oid actual_datatype);
-static void transformIndexConstraints(ParseState *pstate,
-                                                 CreateStmtContext *cxt);
+static void transformIndexConstraints(CreateStmtContext *cxt);
 static IndexStmt *transformIndexConstraint(Constraint *constraint,
-                                                                                  CreateStmtContext *cxt);
-static void transformFKConstraints(ParseState *pstate,
-                                          CreateStmtContext *cxt,
+                                                CreateStmtContext *cxt);
+static void transformFKConstraints(CreateStmtContext *cxt,
                                           bool skipValidation,
                                           bool isAddConstraint);
-static void transformConstraintAttrs(List *constraintList);
-static void transformColumnType(ParseState *pstate, ColumnDef *column);
+static void transformConstraintAttrs(CreateStmtContext *cxt,
+                                                List *constraintList);
+static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
 static void setSchemaName(char *context_schema, char **stmt_schema_name);
 
 
@@ -136,32 +148,61 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
        List       *result;
        List       *save_alist;
        ListCell   *elements;
+       Oid                     namespaceid;
 
        /*
-        * We must not scribble on the passed-in CreateStmt, so copy it.  (This
-        * is overkill, but easy.)
+        * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
+        * overkill, but easy.)
         */
        stmt = (CreateStmt *) copyObject(stmt);
 
        /*
-        * If the target relation name isn't schema-qualified, make it so.  This
-        * prevents some corner cases in which added-on rewritten commands might
-        * think they should apply to other relations that have the same name
-        * and are earlier in the search path.  "istemp" is equivalent to a
-        * specification of pg_temp, so no need for anything extra in that case.
+        * Look up the creation namespace.      This also checks permissions on the
+        * target namespace, so that we throw any permissions error as early as
+        * possible.
+        */
+       namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation);
+
+       /*
+        * If the relation already exists and the user specified "IF NOT EXISTS",
+        * bail out with a NOTICE.
         */
-       if (stmt->relation->schemaname == NULL && !stmt->relation->istemp)
+       if (stmt->if_not_exists)
        {
-               Oid             namespaceid = RangeVarGetCreationNamespace(stmt->relation);
+               Oid                     existing_relid;
 
-               stmt->relation->schemaname = get_namespace_name(namespaceid);
+               existing_relid = get_relname_relid(stmt->relation->relname,
+                                                                                  namespaceid);
+               if (existing_relid != InvalidOid)
+               {
+                       ereport(NOTICE,
+                                       (errcode(ERRCODE_DUPLICATE_TABLE),
+                                        errmsg("relation \"%s\" already exists, skipping",
+                                                       stmt->relation->relname)));
+                       return NIL;
+               }
        }
 
-       /* Set up pstate */
+       /*
+        * If the target relation name isn't schema-qualified, make it so.  This
+        * prevents some corner cases in which added-on rewritten commands might
+        * think they should apply to other relations that have the same name and
+        * are earlier in the search path.      But a local temp table is effectively
+        * specified to be in pg_temp, so no need for anything extra in that case.
+        */
+       if (stmt->relation->schemaname == NULL
+               && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
+               stmt->relation->schemaname = get_namespace_name(namespaceid);
+
+       /* Set up pstate and CreateStmtContext */
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = queryString;
 
-       cxt.stmtType = "CREATE TABLE";
+       cxt.pstate = pstate;
+       if (IsA(stmt, CreateForeignTableStmt))
+               cxt.stmtType = "CREATE FOREIGN TABLE";
+       else
+               cxt.stmtType = "CREATE TABLE";
        cxt.relation = stmt->relation;
        cxt.rel = NULL;
        cxt.inhRelations = stmt->inhRelations;
@@ -176,6 +217,11 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
        cxt.pkey = NULL;
        cxt.hasoids = interpretOidsOption(stmt->options);
 
+       Assert(!stmt->ofTypename || !stmt->inhRelations);       /* grammar enforces */
+
+       if (stmt->ofTypename)
+               transformOfType(&cxt, stmt->ofTypename);
+
        /*
         * Run through each primary element in the table creation clause. Separate
         * column defs from constraints, and do preliminary analysis.
@@ -187,23 +233,15 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
                switch (nodeTag(element))
                {
                        case T_ColumnDef:
-                               transformColumnDefinition(pstate, &cxt,
-                                                                                 (ColumnDef *) element);
+                               transformColumnDefinition(&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);
+                               transformTableConstraint(&cxt, (Constraint *) element);
                                break;
 
                        case T_InhRelation:
-                               transformInhRelation(pstate, &cxt,
-                                                                        (InhRelation *) element);
+                               transformInhRelation(&cxt, (InhRelation *) element);
                                break;
 
                        default:
@@ -225,12 +263,12 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
        /*
         * Postprocess constraints that give rise to index definitions.
         */
-       transformIndexConstraints(pstate, &cxt);
+       transformIndexConstraints(&cxt);
 
        /*
         * Postprocess foreign-key constraints.
         */
-       transformFKConstraints(pstate, &cxt, true, false);
+       transformFKConstraints(&cxt, true, false);
 
        /*
         * Output results.
@@ -251,8 +289,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
  *             Also used in ALTER TABLE ADD COLUMN
  */
 static void
-transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
-                                                 ColumnDef *column)
+transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 {
        bool            is_serial;
        bool            saw_nullable;
@@ -264,28 +301,50 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
 
        /* Check for SERIAL pseudo-types */
        is_serial = false;
-       if (list_length(column->typename->names) == 1)
+       if (column->typeName
+               && list_length(column->typeName->names) == 1
+               && !column->typeName->pct_type)
        {
-               char       *typname = strVal(linitial(column->typename->names));
+               char       *typname = strVal(linitial(column->typeName->names));
 
-               if (strcmp(typname, "serial") == 0 ||
+               if (strcmp(typname, "smallserial") == 0 ||
+                       strcmp(typname, "serial2") == 0)
+               {
+                       is_serial = true;
+                       column->typeName->names = NIL;
+                       column->typeName->typeOid = INT2OID;
+               }
+               else if (strcmp(typname, "serial") == 0 ||
                        strcmp(typname, "serial4") == 0)
                {
                        is_serial = true;
-                       column->typename->names = NIL;
-                       column->typename->typeid = INT4OID;
+                       column->typeName->names = NIL;
+                       column->typeName->typeOid = INT4OID;
                }
                else if (strcmp(typname, "bigserial") == 0 ||
                                 strcmp(typname, "serial8") == 0)
                {
                        is_serial = true;
-                       column->typename->names = NIL;
-                       column->typename->typeid = INT8OID;
+                       column->typeName->names = NIL;
+                       column->typeName->typeOid = INT8OID;
                }
+
+               /*
+                * We have to reject "serial[]" explicitly, because once we've set
+                * typeid, LookupTypeName won't notice arrayBounds.  We don't need any
+                * special coding for serial(typmod) though.
+                */
+               if (is_serial && column->typeName->arrayBounds != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("array of serial is not implemented"),
+                                        parser_errposition(cxt->pstate,
+                                                                               column->typeName->location)));
        }
 
        /* Do necessary work on the column type declaration */
-       transformColumnType(pstate, column);
+       if (column->typeName)
+               transformColumnType(cxt, column);
 
        /* Special actions for SERIAL pseudo-types */
        if (is_serial)
@@ -295,6 +354,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                char       *sname;
                char       *qstring;
                A_Const    *snamenode;
+               TypeCast   *castnode;
                FuncCall   *funccallnode;
                CreateSeqStmt *seqstmt;
                AlterSeqStmt *altseqstmt;
@@ -332,9 +392,21 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                 * TABLE.
                 */
                seqstmt = makeNode(CreateSeqStmt);
-               seqstmt->sequence = makeRangeVar(snamespace, sname);
+               seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
                seqstmt->options = NIL;
 
+               /*
+                * If this is ALTER ADD COLUMN, make sure the sequence will be owned
+                * by the table's owner.  The current user might be someone else
+                * (perhaps a superuser, or someone who's only a member of the owning
+                * role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
+                * and sequence have exactly the same owning role.
+                */
+               if (cxt->rel)
+                       seqstmt->ownerId = cxt->rel->rd_rel->relowner;
+               else
+                       seqstmt->ownerId = InvalidOid;
+
                cxt->blist = lappend(cxt->blist, seqstmt);
 
                /*
@@ -343,7 +415,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                 * done after this CREATE/ALTER TABLE.
                 */
                altseqstmt = makeNode(AlterSeqStmt);
-               altseqstmt->sequence = makeRangeVar(snamespace, sname);
+               altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
                attnamelist = list_make3(makeString(snamespace),
                                                                 makeString(cxt->relation->relname),
                                                                 makeString(column->colname));
@@ -366,28 +438,36 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                snamenode = makeNode(A_Const);
                snamenode->val.type = T_String;
                snamenode->val.val.str = qstring;
-               snamenode->typename = SystemTypeName("regclass");
+               snamenode->location = -1;
+               castnode = makeNode(TypeCast);
+               castnode->typeName = SystemTypeName("regclass");
+               castnode->arg = (Node *) snamenode;
+               castnode->location = -1;
                funccallnode = makeNode(FuncCall);
                funccallnode->funcname = SystemFuncName("nextval");
-               funccallnode->args = list_make1(snamenode);
+               funccallnode->args = list_make1(castnode);
+               funccallnode->agg_order = NIL;
                funccallnode->agg_star = false;
                funccallnode->agg_distinct = false;
+               funccallnode->func_variadic = false;
+               funccallnode->over = NULL;
                funccallnode->location = -1;
 
                constraint = makeNode(Constraint);
                constraint->contype = CONSTR_DEFAULT;
+               constraint->location = -1;
                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;
+               constraint->location = -1;
                column->constraints = lappend(column->constraints, constraint);
        }
 
        /* Process column constraints, if any... */
-       transformConstraintAttrs(column->constraints);
+       transformConstraintAttrs(cxt, column->constraints);
 
        saw_nullable = false;
        saw_default = false;
@@ -395,21 +475,6 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
        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)
@@ -419,7 +484,9 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                                         errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
-                                                                 column->colname, cxt->relation->relname)));
+                                                                       column->colname, cxt->relation->relname),
+                                                        parser_errposition(cxt->pstate,
+                                                                                               constraint->location)));
                                column->is_not_null = FALSE;
                                saw_nullable = true;
                                break;
@@ -429,7 +496,9 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                                         errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
-                                                                 column->colname, cxt->relation->relname)));
+                                                                       column->colname, cxt->relation->relname),
+                                                        parser_errposition(cxt->pstate,
+                                                                                               constraint->location)));
                                column->is_not_null = TRUE;
                                saw_nullable = true;
                                break;
@@ -439,12 +508,18 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                                         errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
-                                                                 column->colname, cxt->relation->relname)));
+                                                                       column->colname, cxt->relation->relname),
+                                                        parser_errposition(cxt->pstate,
+                                                                                               constraint->location)));
                                column->raw_default = constraint->raw_expr;
                                Assert(constraint->cooked_expr == NULL);
                                saw_default = true;
                                break;
 
+                       case CONSTR_CHECK:
+                               cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+                               break;
+
                        case CONSTR_PRIMARY:
                        case CONSTR_UNIQUE:
                                if (constraint->keys == NIL)
@@ -452,8 +527,19 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                                cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
                                break;
 
-                       case CONSTR_CHECK:
-                               cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+                       case CONSTR_EXCLUSION:
+                               /* grammar does not allow EXCLUDE as a column constraint */
+                               elog(ERROR, "column exclusion constraints are not supported");
+                               break;
+
+                       case CONSTR_FOREIGN:
+
+                               /*
+                                * Fill in the current attribute's name and throw it into the
+                                * list of FK constraints to be processed later.
+                                */
+                               constraint->fk_attrs = list_make1(makeString(column->colname));
+                               cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
                                break;
 
                        case CONSTR_ATTR_DEFERRABLE:
@@ -476,13 +562,13 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
  *             transform a Constraint node within CREATE TABLE or ALTER TABLE
  */
 static void
-transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
-                                                Constraint *constraint)
+transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
 {
        switch (constraint->contype)
        {
                case CONSTR_PRIMARY:
                case CONSTR_UNIQUE:
+               case CONSTR_EXCLUSION:
                        cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
                        break;
 
@@ -490,6 +576,10 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
                        cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
                        break;
 
+               case CONSTR_FOREIGN:
+                       cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
+                       break;
+
                case CONSTR_NULL:
                case CONSTR_NOTNULL:
                case CONSTR_DEFAULT:
@@ -516,20 +606,17 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
  * <subtable>.
  */
 static void
-transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
-                                        InhRelation *inhRelation)
+transformInhRelation(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;
+       char       *comment;
 
-       relation = heap_openrv(inhRelation->relation, AccessShareLock);
+       relation = parserOpenTable(cxt->pstate, inhRelation->relation,
+                                                          AccessShareLock);
 
        if (relation->rd_rel->relkind != RELKIND_RELATION)
                ereport(ERROR,
@@ -549,39 +636,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
        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);
-               }
-       }
-
        /*
-        * Insert the copied attributes into the cxt for the new table
-        * definition.
+        * Insert the copied attributes into the cxt for the new table definition.
         */
        for (parent_attno = 1; parent_attno <= tupleDesc->natts;
                 parent_attno++)
@@ -604,13 +660,17 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                 */
                def = makeNode(ColumnDef);
                def->colname = pstrdup(attributeName);
-               def->typename = makeTypeNameFromOid(attribute->atttypid,
+               def->typeName = makeTypeNameFromOid(attribute->atttypid,
                                                                                        attribute->atttypmod);
                def->inhcount = 0;
                def->is_local = true;
                def->is_not_null = attribute->attnotnull;
+               def->is_from_type = false;
+               def->storage = 0;
                def->raw_default = NULL;
                def->cooked_default = NULL;
+               def->collClause = NULL;
+               def->collOid = attribute->attcollation;
                def->constraints = NIL;
 
                /*
@@ -621,9 +681,10 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                /*
                 * Copy default, if present and the default has been requested
                 */
-               if (attribute->atthasdef && including_defaults)
+               if (attribute->atthasdef &&
+                       (inhRelation->options & CREATE_TABLE_LIKE_DEFAULTS))
                {
-                       char       *this_default = NULL;
+                       Node       *this_default = NULL;
                        AttrDefault *attrdef;
                        int                     i;
 
@@ -634,7 +695,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                        {
                                if (attrdef[i].adnum == parent_attno)
                                {
-                                       this_default = attrdef[i].adbin;
+                                       this_default = stringToNode(attrdef[i].adbin);
                                        break;
                                }
                        }
@@ -645,15 +706,40 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                         * but it can't; so default is ready to apply to child.
                         */
 
-                       def->cooked_default = pstrdup(this_default);
+                       def->cooked_default = this_default;
+               }
+
+               /* Likewise, copy storage if requested */
+               if (inhRelation->options & CREATE_TABLE_LIKE_STORAGE)
+                       def->storage = attribute->attstorage;
+               else
+                       def->storage = 0;
+
+               /* Likewise, copy comment if requested */
+               if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
+                       (comment = GetComment(attribute->attrelid,
+                                                                 RelationRelationId,
+                                                                 attribute->attnum)) != NULL)
+               {
+                       CommentStmt *stmt = makeNode(CommentStmt);
+
+                       stmt->objtype = OBJECT_COLUMN;
+                       stmt->objname = list_make3(makeString(cxt->relation->schemaname),
+                                                                          makeString(cxt->relation->relname),
+                                                                          makeString(def->colname));
+                       stmt->objargs = NIL;
+                       stmt->comment = comment;
+
+                       cxt->alist = lappend(cxt->alist, stmt);
                }
        }
 
        /*
-        * Copy CHECK constraints if requested, being careful to adjust
-        * attribute numbers
+        * Copy CHECK constraints if requested, being careful to adjust attribute
+        * numbers
         */
-       if (including_constraints && tupleDesc->constr)
+       if ((inhRelation->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
+               tupleDesc->constr)
        {
                AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                int                     ccnum;
@@ -668,40 +754,89 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                        change_varattnos_of_a_node(ccbin_node, attmap);
 
                        n->contype = CONSTR_CHECK;
-                       n->name = pstrdup(ccname);
+                       n->location = -1;
+                       n->conname = pstrdup(ccname);
                        n->raw_expr = NULL;
                        n->cooked_expr = nodeToString(ccbin_node);
-                       n->indexspace = NULL;
-                       cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+                       cxt->ckconstraints = lappend(cxt->ckconstraints, n);
+
+                       /* Copy comment on constraint */
+                       if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
+                               (comment = GetComment(get_constraint_oid(RelationGetRelid(relation),
+                                                                                                                n->conname, false),
+                                                                         ConstraintRelationId,
+                                                                         0)) != NULL)
+                       {
+                               CommentStmt *stmt = makeNode(CommentStmt);
+
+                               stmt->objtype = OBJECT_CONSTRAINT;
+                               stmt->objname = list_make3(makeString(cxt->relation->schemaname),
+                                                                                  makeString(cxt->relation->relname),
+                                                                                  makeString(n->conname));
+                               stmt->objargs = NIL;
+                               stmt->comment = comment;
+
+                               cxt->alist = lappend(cxt->alist, stmt);
+                       }
                }
        }
 
-       if (including_indexes && relation->rd_rel->relhasindex)
+       /*
+        * Likewise, copy indexes if requested
+        */
+       if ((inhRelation->options & CREATE_TABLE_LIKE_INDEXES) &&
+               relation->rd_rel->relhasindex)
        {
-               AttrNumber *attmap;
+               AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                List       *parent_indexes;
                ListCell   *l;
 
-               attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                parent_indexes = RelationGetIndexList(relation);
 
                foreach(l, parent_indexes)
                {
-                       Oid              parent_index_oid = lfirst_oid(l);
-                       Relation         parent_index;
-                       IndexStmt       *index_stmt;
+                       Oid                     parent_index_oid = lfirst_oid(l);
+                       Relation        parent_index;
+                       IndexStmt  *index_stmt;
 
                        parent_index = index_open(parent_index_oid, AccessShareLock);
 
                        /* Build CREATE INDEX statement to recreate the parent_index */
-                       index_stmt = generateClonedIndexStmt(cxt, parent_index,
-                                                                                                attmap);
+                       index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
+
+                       /* Copy comment on index */
+                       if (inhRelation->options & CREATE_TABLE_LIKE_COMMENTS)
+                       {
+                               comment = GetComment(parent_index_oid, RelationRelationId, 0);
+
+                               if (comment != NULL)
+                               {
+                                       CommentStmt *stmt;
+
+                                       /*
+                                        * We have to assign the index a name now, so that we can
+                                        * reference it in CommentStmt.
+                                        */
+                                       if (index_stmt->idxname == NULL)
+                                               index_stmt->idxname = chooseIndexName(cxt->relation,
+                                                                                                                         index_stmt);
+
+                                       stmt = makeNode(CommentStmt);
+                                       stmt->objtype = OBJECT_INDEX;
+                                       stmt->objname =
+                                               list_make2(makeString(cxt->relation->schemaname),
+                                                                  makeString(index_stmt->idxname));
+                                       stmt->objargs = NIL;
+                                       stmt->comment = comment;
+
+                                       cxt->alist = lappend(cxt->alist, stmt);
+                               }
+                       }
 
-                       /* Add the new IndexStmt to the create context */
+                       /* Save it in the inh_indexes list for the time being */
                        cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
 
-                       /* Keep our lock on the index till xact commit */
-                       index_close(parent_index, NoLock);
+                       index_close(parent_index, AccessShareLock);
                }
        }
 
@@ -713,55 +848,140 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
        heap_close(relation, NoLock);
 }
 
+static void
+transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
+{
+       HeapTuple       tuple;
+       TupleDesc       tupdesc;
+       int                     i;
+       Oid                     ofTypeId;
+
+       AssertArg(ofTypename);
+
+       tuple = typenameType(NULL, ofTypename, NULL);
+       check_of_type(tuple);
+       ofTypeId = HeapTupleGetOid(tuple);
+       ofTypename->typeOid = ofTypeId;         /* cached for later */
+
+       tupdesc = lookup_rowtype_tupdesc(ofTypeId, -1);
+       for (i = 0; i < tupdesc->natts; i++)
+       {
+               Form_pg_attribute attr = tupdesc->attrs[i];
+               ColumnDef  *n;
+
+               if (attr->attisdropped)
+                       continue;
+
+               n = makeNode(ColumnDef);
+               n->colname = pstrdup(NameStr(attr->attname));
+               n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
+               n->inhcount = 0;
+               n->is_local = true;
+               n->is_not_null = false;
+               n->is_from_type = true;
+               n->storage = 0;
+               n->raw_default = NULL;
+               n->cooked_default = NULL;
+               n->collClause = NULL;
+               n->collOid = attr->attcollation;
+               n->constraints = NIL;
+               cxt->columns = lappend(cxt->columns, n);
+       }
+       DecrTupleDescRefCount(tupdesc);
+
+       ReleaseSysCache(tuple);
+}
+
 /*
- * Generate an IndexStmt entry using information from an already
- * existing index "source_idx".
+ * chooseIndexName
+ *
+ * Compute name for an index.  This must match code in indexcmds.c.
  *
- * Note: Much of this functionality is cribbed from pg_get_indexdef.
+ * XXX this is inherently broken because the indexes aren't created
+ * immediately, so we fail to resolve conflicts when the same name is
+ * derived for multiple indexes.  However, that's a reasonably uncommon
+ * situation, so we'll live with it for now.
+ */
+static char *
+chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
+{
+       Oid                     namespaceId;
+       List       *colnames;
+
+       namespaceId = RangeVarGetCreationNamespace(relation);
+       colnames = ChooseIndexColumnNames(index_stmt->indexParams);
+       return ChooseIndexName(relation->relname, namespaceId,
+                                                  colnames, index_stmt->excludeOpNames,
+                                                  index_stmt->primary, index_stmt->isconstraint);
+}
+
+/*
+ * Generate an IndexStmt node using information from an already existing index
+ * "source_idx".  Attribute numbers should be adjusted according to attmap.
  */
 static IndexStmt *
 generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                                                AttrNumber *attmap)
 {
-       HeapTuple                        ht_idx;
-       HeapTuple                        ht_idxrel;
-       HeapTuple                        ht_am;
-       Form_pg_index            idxrec;
-       Form_pg_class            idxrelrec;
-       Form_pg_am                       amrec;
-       List                            *indexprs = NIL;
-       ListCell                        *indexpr_item;
-       Oid                                      indrelid;
-       Oid                              source_relid;
-       int                                      keyno;
-       Oid                                      keycoltype;
-       Datum                            indclassDatum;
-       Datum                            indoptionDatum;
-       bool                             isnull;
-       oidvector                       *indclass;
-       int2vector                      *indoption;
-       IndexStmt                       *index;
-       Datum                            reloptions;
-
-       source_relid = RelationGetRelid(source_idx);
-
-       /* Fetch pg_index tuple for source index */
-       ht_idx = SearchSysCache(INDEXRELID,
-                                                       ObjectIdGetDatum(source_relid),
-                                                       0, 0, 0);
-       if (!HeapTupleIsValid(ht_idx))
-               elog(ERROR, "cache lookup failed for index %u", source_relid);
-       idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+       Oid                     source_relid = RelationGetRelid(source_idx);
+       Form_pg_attribute *attrs = RelationGetDescr(source_idx)->attrs;
+       HeapTuple       ht_idxrel;
+       HeapTuple       ht_idx;
+       Form_pg_class idxrelrec;
+       Form_pg_index idxrec;
+       Form_pg_am      amrec;
+       oidvector  *indcollation;
+       oidvector  *indclass;
+       IndexStmt  *index;
+       List       *indexprs;
+       ListCell   *indexpr_item;
+       Oid                     indrelid;
+       int                     keyno;
+       Oid                     keycoltype;
+       Datum           datum;
+       bool            isnull;
+
+       /*
+        * Fetch pg_class tuple of source index.  We can't use the copy in the
+        * relcache entry because it doesn't include optional fields.
+        */
+       ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(source_relid));
+       if (!HeapTupleIsValid(ht_idxrel))
+               elog(ERROR, "cache lookup failed for relation %u", source_relid);
+       idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
 
-       Assert(source_relid == idxrec->indexrelid);
+       /* Fetch pg_index tuple for source index from relcache entry */
+       ht_idx = source_idx->rd_indextuple;
+       idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
        indrelid = idxrec->indrelid;
 
+       /* Fetch pg_am tuple for source index from relcache entry */
+       amrec = source_idx->rd_am;
+
+       /* Extract indcollation from the pg_index tuple */
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indcollation, &isnull);
+       Assert(!isnull);
+       indcollation = (oidvector *) DatumGetPointer(datum);
+
+       /* Extract indclass from the pg_index tuple */
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indclass, &isnull);
+       Assert(!isnull);
+       indclass = (oidvector *) DatumGetPointer(datum);
+
+       /* Begin building the IndexStmt */
        index = makeNode(IndexStmt);
+       index->relation = cxt->relation;
+       index->accessMethod = pstrdup(NameStr(amrec->amname));
+       if (OidIsValid(idxrelrec->reltablespace))
+               index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
+       else
+               index->tableSpace = NULL;
+       index->indexOid = InvalidOid;
        index->unique = idxrec->indisunique;
-       index->concurrent = false;
        index->primary = idxrec->indisprimary;
-       index->relation = cxt->relation;
-       index->isconstraint = false;
+       index->concurrent = false;
 
        /*
         * We don't try to preserve the name of the source index; instead, just
@@ -769,65 +989,108 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
         */
        index->idxname = NULL;
 
-       /* Must get indclass and indoption the hard way */
-       indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                       Anum_pg_index_indclass, &isnull);
-       Assert(!isnull);
-       indclass = (oidvector *) DatumGetPointer(indclassDatum);
-       indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indoption, &isnull);
-       Assert(!isnull);
-       indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-
-       /* Fetch pg_class tuple of source index */
-       ht_idxrel = SearchSysCache(RELOID,
-                                                          ObjectIdGetDatum(source_relid),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(ht_idxrel))
-               elog(ERROR, "cache lookup failed for relation %u", source_relid);
-
        /*
-        * Store the reloptions for later use by this new index
+        * If the index is marked PRIMARY or has an exclusion condition, it's
+        * certainly from a constraint; else, if it's not marked UNIQUE, it
+        * certainly isn't.  If it is or might be from a constraint, we have to
+        * fetch the pg_constraint record.
         */
-       reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
-                                                                Anum_pg_class_reloptions, &isnull);
-       if (!isnull)
-               index->src_options = flatten_reloptions(source_relid);
+       if (index->primary || index->unique || idxrec->indisexclusion)
+       {
+               Oid                     constraintId = get_index_constraint(source_relid);
 
-       idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+               if (OidIsValid(constraintId))
+               {
+                       HeapTuple       ht_constr;
+                       Form_pg_constraint conrec;
+
+                       ht_constr = SearchSysCache1(CONSTROID,
+                                                                               ObjectIdGetDatum(constraintId));
+                       if (!HeapTupleIsValid(ht_constr))
+                               elog(ERROR, "cache lookup failed for constraint %u",
+                                        constraintId);
+                       conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
+
+                       index->isconstraint = true;
+                       index->deferrable = conrec->condeferrable;
+                       index->initdeferred = conrec->condeferred;
+
+                       /* If it's an exclusion constraint, we need the operator names */
+                       if (idxrec->indisexclusion)
+                       {
+                               Datum      *elems;
+                               int                     nElems;
+                               int                     i;
+
+                               Assert(conrec->contype == CONSTRAINT_EXCLUSION);
+                               /* Extract operator OIDs from the pg_constraint tuple */
+                               datum = SysCacheGetAttr(CONSTROID, ht_constr,
+                                                                               Anum_pg_constraint_conexclop,
+                                                                               &isnull);
+                               if (isnull)
+                                       elog(ERROR, "null conexclop for constraint %u",
+                                                constraintId);
+
+                               deconstruct_array(DatumGetArrayTypeP(datum),
+                                                                 OIDOID, sizeof(Oid), true, 'i',
+                                                                 &elems, NULL, &nElems);
+
+                               for (i = 0; i < nElems; i++)
+                               {
+                                       Oid                     operid = DatumGetObjectId(elems[i]);
+                                       HeapTuple       opertup;
+                                       Form_pg_operator operform;
+                                       char       *oprname;
+                                       char       *nspname;
+                                       List       *namelist;
+
+                                       opertup = SearchSysCache1(OPEROID,
+                                                                                         ObjectIdGetDatum(operid));
+                                       if (!HeapTupleIsValid(opertup))
+                                               elog(ERROR, "cache lookup failed for operator %u",
+                                                        operid);
+                                       operform = (Form_pg_operator) GETSTRUCT(opertup);
+                                       oprname = pstrdup(NameStr(operform->oprname));
+                                       /* For simplicity we always schema-qualify the op name */
+                                       nspname = get_namespace_name(operform->oprnamespace);
+                                       namelist = list_make2(makeString(nspname),
+                                                                                 makeString(oprname));
+                                       index->excludeOpNames = lappend(index->excludeOpNames,
+                                                                                                       namelist);
+                                       ReleaseSysCache(opertup);
+                               }
+                       }
 
-       /* Fetch pg_am tuple for the index's access method */
-       ht_am = SearchSysCache(AMOID,
-                                                  ObjectIdGetDatum(idxrelrec->relam),
-                                                  0, 0, 0);
-       if (!HeapTupleIsValid(ht_am))
-               elog(ERROR, "cache lookup failed for access method %u",
-                        idxrelrec->relam);
-       amrec = (Form_pg_am) GETSTRUCT(ht_am);
-       index->accessMethod = pstrdup(NameStr(amrec->amname));
+                       ReleaseSysCache(ht_constr);
+               }
+               else
+                       index->isconstraint = false;
+       }
+       else
+               index->isconstraint = false;
 
        /* Get the index expressions, if any */
-       if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indexprs, &isnull);
+       if (!isnull)
        {
-               Datum           exprsDatum;
-               bool            isnull;
                char       *exprsString;
 
-               exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indexprs, &isnull);
-               exprsString = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                 exprsDatum));
-               Assert(!isnull);
+               exprsString = TextDatumGetCString(datum);
                indexprs = (List *) stringToNode(exprsString);
        }
+       else
+               indexprs = NIL;
 
-       indexpr_item = list_head(indexprs);
+       /* Build the list of IndexElem */
+       index->indexParams = NIL;
 
+       indexpr_item = list_head(indexprs);
        for (keyno = 0; keyno < idxrec->indnatts; keyno++)
        {
-               IndexElem       *iparam;
+               IndexElem  *iparam;
                AttrNumber      attnum = idxrec->indkey.values[keyno];
-               int16           opt = indoption->values[keyno];
+               int16           opt = source_idx->rd_indoption[keyno];
 
                iparam = makeNode(IndexElem);
 
@@ -850,14 +1113,23 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                        if (indexpr_item == NULL)
                                elog(ERROR, "too few entries in indexprs list");
                        indexkey = (Node *) lfirst(indexpr_item);
+                       indexpr_item = lnext(indexpr_item);
+
+                       /* OK to modify indexkey since we are working on a private copy */
                        change_varattnos_of_a_node(indexkey, attmap);
+
                        iparam->name = NULL;
                        iparam->expr = indexkey;
 
-                       indexpr_item = lnext(indexpr_item);
                        keycoltype = exprType(indexkey);
                }
 
+               /* Copy the original index column name */
+               iparam->indexcolname = pstrdup(NameStr(attrs[keyno]->attname));
+
+               /* Add the collation name, if non-default */
+               iparam->collation = get_collation(indcollation->values[keyno], keycoltype);
+
                /* Add the operator class name, if non-default */
                iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
 
@@ -867,46 +1139,90 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                /* Adjust options if necessary */
                if (amrec->amcanorder)
                {
-                       /* If it supports sort ordering, report DESC and NULLS opts */
+                       /*
+                        * If it supports sort ordering, copy DESC and NULLS opts. Don't
+                        * set non-default settings unnecessarily, though, so as to
+                        * improve the chance of recognizing equivalence to constraint
+                        * indexes.
+                        */
                        if (opt & INDOPTION_DESC)
+                       {
                                iparam->ordering = SORTBY_DESC;
-                       if (opt & INDOPTION_NULLS_FIRST)
-                               iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+                               if ((opt & INDOPTION_NULLS_FIRST) == 0)
+                                       iparam->nulls_ordering = SORTBY_NULLS_LAST;
+                       }
+                       else
+                       {
+                               if (opt & INDOPTION_NULLS_FIRST)
+                                       iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+                       }
                }
 
                index->indexParams = lappend(index->indexParams, iparam);
        }
 
-       /* Use the same tablespace as the source index */
-       index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
+       /* Copy reloptions if any */
+       datum = SysCacheGetAttr(RELOID, ht_idxrel,
+                                                       Anum_pg_class_reloptions, &isnull);
+       if (!isnull)
+               index->options = untransformRelOptions(datum);
 
        /* If it's a partial index, decompile and append the predicate */
-       if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indpred, &isnull);
+       if (!isnull)
        {
-               Datum           pred_datum;
-               bool            isnull;
                char       *pred_str;
 
                /* Convert text string to node tree */
-               pred_datum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indpred, &isnull);
-               Assert(!isnull);
-               pred_str = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          pred_datum));
+               pred_str = TextDatumGetCString(datum);
                index->whereClause = (Node *) stringToNode(pred_str);
+               /* Adjust attribute numbers */
                change_varattnos_of_a_node(index->whereClause, attmap);
        }
 
        /* Clean up */
-       ReleaseSysCache(ht_idx);
        ReleaseSysCache(ht_idxrel);
-       ReleaseSysCache(ht_am);
 
        return index;
 }
 
 /*
- * get_opclass                 - fetch name of an index operator class
+ * get_collation               - fetch qualified name of a collation
+ *
+ * If collation is InvalidOid or is the default for the given actual_datatype,
+ * then the return value is NIL.
+ */
+static List *
+get_collation(Oid collation, Oid actual_datatype)
+{
+       List       *result;
+       HeapTuple       ht_coll;
+       Form_pg_collation coll_rec;
+       char       *nsp_name;
+       char       *coll_name;
+
+       if (!OidIsValid(collation))
+               return NIL;                             /* easy case */
+       if (collation == get_typcollation(actual_datatype))
+               return NIL;                             /* just let it default */
+
+       ht_coll = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
+       if (!HeapTupleIsValid(ht_coll))
+               elog(ERROR, "cache lookup failed for collation %u", collation);
+       coll_rec = (Form_pg_collation) GETSTRUCT(ht_coll);
+
+       /* For simplicity, we always schema-qualify the name */
+       nsp_name = get_namespace_name(coll_rec->collnamespace);
+       coll_name = pstrdup(NameStr(coll_rec->collname));
+       result = list_make2(makeString(nsp_name), makeString(coll_name));
+
+       ReleaseSysCache(ht_coll);
+       return result;
+}
+
+/*
+ * get_opclass                 - fetch qualified name of an index operator class
  *
  * If the opclass is the default for the given actual_datatype, then
  * the return value is NIL.
@@ -914,22 +1230,20 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
 static List *
 get_opclass(Oid opclass, Oid actual_datatype)
 {
-       HeapTuple                        ht_opc;
-       Form_pg_opclass          opc_rec;
-       List                            *result = NIL;
+       List       *result = NIL;
+       HeapTuple       ht_opc;
+       Form_pg_opclass opc_rec;
 
-       ht_opc = SearchSysCache(CLAOID,
-                                                       ObjectIdGetDatum(opclass),
-                                                       0, 0, 0);
+       ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
        if (!HeapTupleIsValid(ht_opc))
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
 
-       if (!OidIsValid(actual_datatype) ||
-               GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
+       if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
        {
-               char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
-               char *opc_name = NameStr(opc_rec->opcname);
+               /* For simplicity, we always schema-qualify the name */
+               char       *nsp_name = get_namespace_name(opc_rec->opcnamespace);
+               char       *opc_name = pstrdup(NameStr(opc_rec->opcname));
 
                result = list_make2(makeString(nsp_name), makeString(opc_name));
        }
@@ -941,12 +1255,12 @@ get_opclass(Oid opclass, Oid actual_datatype)
 
 /*
  * transformIndexConstraints
- *             Handle UNIQUE and PRIMARY KEY constraints, which create
- *             indexes. We also merge index definitions arising from
+ *             Handle UNIQUE, PRIMARY KEY, EXCLUDE constraints, which create indexes.
+ *             We also merge in any index definitions arising from
  *             LIKE ... INCLUDING INDEXES.
  */
 static void
-transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
+transformIndexConstraints(CreateStmtContext *cxt)
 {
        IndexStmt  *index;
        List       *indexlist = NIL;
@@ -954,14 +1268,39 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
        /*
         * 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.
+        * KEY, mark each column as NOT NULL and create an index. For UNIQUE or
+        * EXCLUDE, create an index as for PRIMARY KEY, but do not insist on NOT
+        * NULL.
         */
        foreach(lc, cxt->ixconstraints)
        {
                Constraint *constraint = (Constraint *) lfirst(lc);
 
+               Assert(IsA(constraint, Constraint));
+               Assert(constraint->contype == CONSTR_PRIMARY ||
+                          constraint->contype == CONSTR_UNIQUE ||
+                          constraint->contype == CONSTR_EXCLUSION);
+
                index = transformIndexConstraint(constraint, cxt);
+
+               indexlist = lappend(indexlist, index);
+       }
+
+       /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
+       foreach(lc, cxt->inh_indexes)
+       {
+               index = (IndexStmt *) lfirst(lc);
+
+               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;
+               }
+
                indexlist = lappend(indexlist, index);
        }
 
@@ -996,8 +1335,15 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
                {
                        IndexStmt  *priorindex = lfirst(k);
 
-                       if (equal(index->indexParams, priorindex->indexParams))
+                       if (equal(index->indexParams, priorindex->indexParams) &&
+                               equal(index->whereClause, priorindex->whereClause) &&
+                               equal(index->excludeOpNames, priorindex->excludeOpNames) &&
+                               strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
+                               index->deferrable == priorindex->deferrable &&
+                               index->initdeferred == priorindex->initdeferred)
                        {
+                               priorindex->unique |= index->unique;
+
                                /*
                                 * If the prior index is as yet unnamed, and this one is
                                 * named, then transfer the name to the prior index. This
@@ -1014,48 +1360,31 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
                if (keep)
                        cxt->alist = lappend(cxt->alist, index);
        }
-
-       /* Copy indexes defined by LIKE ... INCLUDING INDEXES */
-       foreach(lc, cxt->inh_indexes)
-       {
-               index = (IndexStmt *) lfirst(lc);
-
-               if (index->primary)
-               {
-                       if (cxt->pkey)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                                errmsg("multiple primary keys for table \"%s\" are not allowed",
-                                                               cxt->relation->relname)));
-
-                       cxt->pkey = index;
-               }
-
-               cxt->alist = lappend(cxt->alist, index);
-       }
 }
 
+/*
+ * transformIndexConstraint
+ *             Transform one UNIQUE, PRIMARY KEY, or EXCLUDE constraint for
+ *             transformIndexConstraints.
+ */
 static IndexStmt *
 transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 {
-       IndexStmt       *index;
-       ListCell        *keys;
-       IndexElem       *iparam;
-
-       Assert(constraint->contype == CONSTR_PRIMARY ||
-                  constraint->contype == CONSTR_UNIQUE);
+       IndexStmt  *index;
+       ListCell   *lc;
 
        index = makeNode(IndexStmt);
-       index->unique = true;
-       index->primary = (constraint->contype == CONSTR_PRIMARY);
 
+       index->unique = (constraint->contype != CONSTR_EXCLUSION);
+       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)));
+                        errmsg("multiple primary keys for table \"%s\" are not allowed",
+                                       cxt->relation->relname),
+                                        parser_errposition(cxt->pstate, constraint->location)));
                cxt->pkey = index;
 
                /*
@@ -1064,32 +1393,238 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                 */
        }
        index->isconstraint = true;
+       index->deferrable = constraint->deferrable;
+       index->initdeferred = constraint->initdeferred;
 
-       if (constraint->name != NULL)
-               index->idxname = pstrdup(constraint->name);
+       if (constraint->conname != NULL)
+               index->idxname = pstrdup(constraint->conname);
        else
-               index->idxname = NULL;          /* DefineIndex will choose name */
+               index->idxname = NULL;  /* DefineIndex will choose name */
 
        index->relation = cxt->relation;
-       index->accessMethod = DEFAULT_INDEX_TYPE;
+       index->accessMethod = constraint->access_method ? constraint->access_method : DEFAULT_INDEX_TYPE;
        index->options = constraint->options;
        index->tableSpace = constraint->indexspace;
+       index->whereClause = constraint->where_clause;
        index->indexParams = NIL;
-       index->whereClause = NULL;
+       index->excludeOpNames = NIL;
+       index->indexOid = InvalidOid;
        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.)
+        * If it's ALTER TABLE ADD CONSTRAINT USING INDEX, look up the index and
+        * verify it's usable, then extract the implied column name list.  (We
+        * will not actually need the column name list at runtime, but we need it
+        * now to check for duplicate column entries below.)
+        */
+       if (constraint->indexname != NULL)
+       {
+               char       *index_name = constraint->indexname;
+               Relation        heap_rel = cxt->rel;
+               Oid                     index_oid;
+               Relation        index_rel;
+               Form_pg_index index_form;
+               oidvector  *indclass;
+               Datum           indclassDatum;
+               bool            isnull;
+               int                     i;
+
+               /* Grammar should not allow this with explicit column list */
+               Assert(constraint->keys == NIL);
+
+               /* Grammar should only allow PRIMARY and UNIQUE constraints */
+               Assert(constraint->contype == CONSTR_PRIMARY ||
+                          constraint->contype == CONSTR_UNIQUE);
+
+               /* Must be ALTER, not CREATE, but grammar doesn't enforce that */
+               if (!cxt->isalter)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("cannot use an existing index in CREATE TABLE"),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /* Look for the index in the same schema as the table */
+               index_oid = get_relname_relid(index_name, RelationGetNamespace(heap_rel));
+
+               if (!OidIsValid(index_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("index \"%s\" does not exist", index_name),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /* Open the index (this will throw an error if it is not an index) */
+               index_rel = index_open(index_oid, AccessShareLock);
+               index_form = index_rel->rd_index;
+
+               /* Check that it does not have an associated constraint already */
+               if (OidIsValid(get_index_constraint(index_oid)))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                          errmsg("index \"%s\" is already associated with a constraint",
+                                         index_name),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /* Perform validity checks on the index */
+               if (index_form->indrelid != RelationGetRelid(heap_rel))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                        errmsg("index \"%s\" does not belong to table \"%s\"",
+                                                       index_name, RelationGetRelationName(heap_rel)),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               if (!index_form->indisvalid)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                        errmsg("index \"%s\" is not valid", index_name),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               if (!index_form->indisready)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                        errmsg("index \"%s\" is not ready", index_name),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               if (!index_form->indisunique)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("\"%s\" is not a unique index", index_name),
+                                        errdetail("Cannot create a primary key or unique constraint using such an index."),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               if (RelationGetIndexExpressions(index_rel) != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("index \"%s\" contains expressions", index_name),
+                                        errdetail("Cannot create a primary key or unique constraint using such an index."),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               if (RelationGetIndexPredicate(index_rel) != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("\"%s\" is a partial index", index_name),
+                                        errdetail("Cannot create a primary key or unique constraint using such an index."),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /*
+                * It's probably unsafe to change a deferred index to non-deferred. (A
+                * non-constraint index couldn't be deferred anyway, so this case
+                * should never occur; no need to sweat, but let's check it.)
+                */
+               if (!index_form->indimmediate && !constraint->deferrable)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("\"%s\" is a deferrable index", index_name),
+                                        errdetail("Cannot create a non-deferrable constraint using a deferrable index."),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /*
+                * Insist on it being a btree.  That's the only kind that supports
+                * uniqueness at the moment anyway; but we must have an index that
+                * exactly matches what you'd get from plain ADD CONSTRAINT syntax,
+                * else dump and reload will produce a different index (breaking
+                * pg_upgrade in particular).
+                */
+               if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("index \"%s\" is not a b-tree", index_name),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+               /* Must get indclass the hard way */
+               indclassDatum = SysCacheGetAttr(INDEXRELID, index_rel->rd_indextuple,
+                                                                               Anum_pg_index_indclass, &isnull);
+               Assert(!isnull);
+               indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+               for (i = 0; i < index_form->indnatts; i++)
+               {
+                       int2            attnum = index_form->indkey.values[i];
+                       Form_pg_attribute attform;
+                       char       *attname;
+                       Oid                     defopclass;
+
+                       /*
+                        * We shouldn't see attnum == 0 here, since we already rejected
+                        * expression indexes.  If we do, SystemAttributeDefinition will
+                        * throw an error.
+                        */
+                       if (attnum > 0)
+                       {
+                               Assert(attnum <= heap_rel->rd_att->natts);
+                               attform = heap_rel->rd_att->attrs[attnum - 1];
+                       }
+                       else
+                               attform = SystemAttributeDefinition(attnum,
+                                                                                          heap_rel->rd_rel->relhasoids);
+                       attname = pstrdup(NameStr(attform->attname));
+
+                       /*
+                        * Insist on default opclass and sort options.  While the index
+                        * would still work as a constraint with non-default settings, it
+                        * might not provide exactly the same uniqueness semantics as
+                        * you'd get from a normally-created constraint; and there's also
+                        * the dump/reload problem mentioned above.
+                        */
+                       defopclass = GetDefaultOpClass(attform->atttypid,
+                                                                                  index_rel->rd_rel->relam);
+                       if (indclass->values[i] != defopclass ||
+                               index_rel->rd_indoption[i] != 0)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("index \"%s\" does not have default sorting behavior", index_name),
+                                                errdetail("Cannot create a primary key or unique constraint using such an index."),
+                                        parser_errposition(cxt->pstate, constraint->location)));
+
+                       constraint->keys = lappend(constraint->keys, makeString(attname));
+               }
+
+               /* Close the index relation but keep the lock */
+               relation_close(index_rel, NoLock);
+
+               index->indexOid = index_oid;
+       }
+
+       /*
+        * If it's an EXCLUDE constraint, the grammar returns a list of pairs of
+        * IndexElems and operator names.  We have to break that apart into
+        * separate lists.
+        */
+       if (constraint->contype == CONSTR_EXCLUSION)
+       {
+               foreach(lc, constraint->exclusions)
+               {
+                       List       *pair = (List *) lfirst(lc);
+                       IndexElem  *elem;
+                       List       *opname;
+
+                       Assert(list_length(pair) == 2);
+                       elem = (IndexElem *) linitial(pair);
+                       Assert(IsA(elem, IndexElem));
+                       opname = (List *) lsecond(pair);
+                       Assert(IsA(opname, List));
+
+                       index->indexParams = lappend(index->indexParams, elem);
+                       index->excludeOpNames = lappend(index->excludeOpNames, opname);
+               }
+
+               return index;
+       }
+
+       /*
+        * For UNIQUE and PRIMARY KEY, we just have a list of column names.
+        *
+        * 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)
+       foreach(lc, constraint->keys)
        {
-               char       *key = strVal(lfirst(keys));
+               char       *key = strVal(lfirst(lc));
                bool            found = false;
                ColumnDef  *column = NULL;
                ListCell   *columns;
+               IndexElem  *iparam;
 
                foreach(columns, cxt->columns)
                {
@@ -1110,9 +1645,9 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                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.
+                        * 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;
                }
@@ -1132,8 +1667,8 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                                if (rel->rd_rel->relkind != RELKIND_RELATION)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                                        errmsg("inherited relation \"%s\" is not a table",
-                                                                       inh->relname)));
+                                                  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];
@@ -1146,10 +1681,10 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                                                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.
+                                                * 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;
                                        }
@@ -1162,15 +1697,15 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 
                /*
                 * 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.
+                * 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)));
+                                        errmsg("column \"%s\" named in key does not exist", key),
+                                        parser_errposition(cxt->pstate, constraint->location)));
 
                /* Check for PRIMARY KEY(foo, foo) */
                foreach(columns, index->indexParams)
@@ -1182,12 +1717,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DUPLICATE_COLUMN),
                                                         errmsg("column \"%s\" appears twice in primary key constraint",
-                                                                       key)));
+                                                                       key),
+                                        parser_errposition(cxt->pstate, constraint->location)));
                                else
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                                        errmsg("column \"%s\" appears twice in unique constraint",
-                                                                       key)));
+                                       errmsg("column \"%s\" appears twice in unique constraint",
+                                                  key),
+                                        parser_errposition(cxt->pstate, constraint->location)));
                        }
                }
 
@@ -1195,6 +1732,8 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                iparam = makeNode(IndexElem);
                iparam->name = pstrdup(key);
                iparam->expr = NULL;
+               iparam->indexcolname = NULL;
+               iparam->collation = NIL;
                iparam->opclass = NIL;
                iparam->ordering = SORTBY_DEFAULT;
                iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
@@ -1209,7 +1748,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
  *             handle FOREIGN KEY constraints
  */
 static void
-transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
+transformFKConstraints(CreateStmtContext *cxt,
                                           bool skipValidation, bool isAddConstraint)
 {
        ListCell   *fkclist;
@@ -1219,15 +1758,17 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
 
        /*
         * If CREATE TABLE or adding a column with NULL default, we can safely
-        * skip validation of the constraint.
+        * skip validation of FK constraints, and nonetheless mark them valid.
+        * (This will override any user-supplied NOT VALID flag.)
         */
        if (skipValidation)
        {
                foreach(fkclist, cxt->fkconstraints)
                {
-                       FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+                       Constraint *constraint = (Constraint *) lfirst(fkclist);
 
-                       fkconstraint->skip_validation = true;
+                       constraint->skip_validation = true;
+                       constraint->initially_valid = true;
                }
        }
 
@@ -1252,12 +1793,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
 
                foreach(fkclist, cxt->fkconstraints)
                {
-                       FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+                       Constraint *constraint = (Constraint *) lfirst(fkclist);
                        AlterTableCmd *altercmd = makeNode(AlterTableCmd);
 
                        altercmd->subtype = AT_ProcessedConstraint;
                        altercmd->name = NULL;
-                       altercmd->def = (Node *) fkconstraint;
+                       altercmd->def = (Node *) constraint;
                        alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
                }
 
@@ -1266,10 +1807,10 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
 }
 
 /*
- * transformIndexStmt - parse analysis for CREATE INDEX
+ * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE
  *
  * 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
+ * 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.
  */
@@ -1282,28 +1823,29 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
        ListCell   *l;
 
        /*
-        * We must not scribble on the passed-in IndexStmt, so copy it.  (This
-        * is overkill, but easy.)
+        * 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
+        * 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.
+        * 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. If we are being called by ALTER TABLE, we will
+        * already hold a higher lock.
         */
        rel = heap_openrv(stmt->relation,
-                               (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
+                                 (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.
+        * 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);
 
@@ -1312,9 +1854,13 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
 
        /* take care of the where clause */
        if (stmt->whereClause)
+       {
                stmt->whereClause = transformWhereClause(pstate,
                                                                                                 stmt->whereClause,
                                                                                                 "WHERE");
+               /* we have to fix its collations too */
+               assign_expr_collations(pstate, stmt->whereClause);
+       }
 
        /* take care of any index expressions */
        foreach(l, stmt->indexParams)
@@ -1323,8 +1869,16 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
 
                if (ielem->expr)
                {
+                       /* Extract preliminary index col name before transforming expr */
+                       if (ielem->indexcolname == NULL)
+                               ielem->indexcolname = FigureIndexColname(ielem->expr);
+
+                       /* Now do parse transformation of the expression */
                        ielem->expr = transformExpr(pstate, ielem->expr);
 
+                       /* We have to fix its collations too */
+                       assign_expr_collations(pstate, ielem->expr);
+
                        /*
                         * We check only that the result type is legitimate; this is for
                         * consistency with what transformWhereClause() checks for the
@@ -1393,10 +1947,10 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
         * qualification.
         */
        oldrte = addRangeTableEntryForRelation(pstate, rel,
-                                                                                  makeAlias("*OLD*", NIL),
+                                                                                  makeAlias("old", NIL),
                                                                                   false, false);
        newrte = addRangeTableEntryForRelation(pstate, rel,
-                                                                                  makeAlias("*NEW*", NIL),
+                                                                                  makeAlias("new", NIL),
                                                                                   false, false);
        /* Must override addRangeTableEntry's default access-check flags */
        oldrte->requiredPerms = 0;
@@ -1432,8 +1986,10 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 
        /* take care of the where clause */
        *whereClause = transformWhereClause(pstate,
-                                                                               (Node *) copyObject(stmt->whereClause),
+                                                                         (Node *) copyObject(stmt->whereClause),
                                                                                "WHERE");
+       /* we have to fix its collations too */
+       assign_expr_collations(pstate, *whereClause);
 
        if (list_length(pstate->p_rtable) != 2)         /* naughty, naughty... */
                ereport(ERROR,
@@ -1445,6 +2001,10 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
                ereport(ERROR,
                                (errcode(ERRCODE_GROUPING_ERROR),
                   errmsg("cannot use aggregate function in rule WHERE condition")));
+       if (pstate->p_hasWindowFuncs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WINDOWING_ERROR),
+                         errmsg("cannot use window function in rule WHERE condition")));
 
        /*
         * 'instead nothing' rules with a qualification need a query rangetable so
@@ -1458,7 +2018,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 
                nothing_qry->commandType = CMD_NOTHING;
                nothing_qry->rtable = pstate->p_rtable;
-               nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
+               nothing_qry->jointree = makeFromExpr(NIL, NULL);                /* no join wanted */
 
                *actions = list_make1(nothing_qry);
        }
@@ -1480,8 +2040,8 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
                                                has_new;
 
                        /*
-                        * Since outer ParseState isn't parent of inner, have to pass
-                        * down the query text by hand.
+                        * Since outer ParseState isn't parent of inner, have to pass down
+                        * the query text by hand.
                         */
                        sub_pstate->p_sourcetext = queryString;
 
@@ -1493,10 +2053,10 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
                         * them in the joinlist.
                         */
                        oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                                                                  makeAlias("*OLD*", NIL),
+                                                                                                  makeAlias("old", NIL),
                                                                                                   false, false);
                        newrte = addRangeTableEntryForRelation(sub_pstate, rel,
-                                                                                                  makeAlias("*NEW*", NIL),
+                                                                                                  makeAlias("new", NIL),
                                                                                                   false, false);
                        oldrte->requiredPerms = 0;
                        newrte->requiredPerms = 0;
@@ -1580,6 +2140,35 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
                        }
 
                        /*
+                        * OLD/NEW are not allowed in WITH queries, because they would
+                        * amount to outer references for the WITH, which we disallow.
+                        * However, they were already in the outer rangetable when we
+                        * analyzed the query, so we have to check.
+                        *
+                        * Note that in the INSERT...SELECT case, we need to examine the
+                        * CTE lists of both top_subqry and sub_qry.
+                        *
+                        * Note that we aren't digging into the body of the query looking
+                        * for WITHs in nested sub-SELECTs.  A WITH down there can
+                        * legitimately refer to OLD/NEW, because it'd be an
+                        * indirect-correlated outer reference.
+                        */
+                       if (rangeTableEntry_used((Node *) top_subqry->cteList,
+                                                                        PRS2_OLD_VARNO, 0) ||
+                               rangeTableEntry_used((Node *) sub_qry->cteList,
+                                                                        PRS2_OLD_VARNO, 0))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot refer to OLD within WITH query")));
+                       if (rangeTableEntry_used((Node *) top_subqry->cteList,
+                                                                        PRS2_NEW_VARNO, 0) ||
+                               rangeTableEntry_used((Node *) sub_qry->cteList,
+                                                                        PRS2_NEW_VARNO, 0))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot refer to NEW within WITH query")));
+
+                       /*
                         * For efficiency's sake, add OLD to the rule action's jointree
                         * only if it was actually referenced in the statement or qual.
                         *
@@ -1648,26 +2237,35 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
        List       *newcmds = NIL;
        bool            skipValidation = true;
        AlterTableCmd *newcmd;
+       LOCKMODE        lockmode;
 
        /*
-        * We must not scribble on the passed-in AlterTableStmt, so copy it.
-        * (This is overkill, but easy.)
+        * 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
+        * Determine the appropriate lock level for this list of subcommands.
+        */
+       lockmode = AlterTableGetLockLevel(stmt->cmds);
+
+       /*
+        * Acquire appropriate 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.
+        * based on the state of the relation will still be good at execution. We
+        * must get lock now because execution will later require it; taking a
+        * lower grade lock now and trying to upgrade later risks deadlock.  Any
+        * new commands we add after this must not upgrade the lock level
+        * requested here.
         */
-       rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+       rel = relation_openrv(stmt->relation, lockmode);
 
-       /* Set up pstate */
+       /* Set up pstate and CreateStmtContext */
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = queryString;
 
+       cxt.pstate = pstate;
        cxt.stmtType = "ALTER TABLE";
        cxt.relation = stmt->relation;
        cxt.rel = rel;
@@ -1695,44 +2293,27 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
                switch (cmd->subtype)
                {
                        case AT_AddColumn:
+                       case AT_AddColumnToView:
                                {
                                        ColumnDef  *def = (ColumnDef *) cmd->def;
 
-                                       Assert(IsA(cmd->def, ColumnDef));
-                                       transformColumnDefinition(pstate, &cxt,
-                                                                                         (ColumnDef *) cmd->def);
+                                       Assert(IsA(def, ColumnDef));
+                                       transformColumnDefinition(&cxt, def);
 
                                        /*
                                         * If the column has a non-null default, we can't skip
                                         * validation of foreign keys.
                                         */
-                                       if (((ColumnDef *) cmd->def)->raw_default != NULL)
+                                       if (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;
 
+                                       newcmds = lappend(newcmds, cmd);
                                        break;
                                }
                        case AT_AddConstraint:
@@ -1740,14 +2321,11 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
                                /*
                                 * 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;
+                                       transformTableConstraint(&cxt, (Constraint *) cmd->def);
+                                       if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
+                                               skipValidation = false;
                                }
                                else
                                        elog(ERROR, "unrecognized node type: %d",
@@ -1772,32 +2350,32 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
 
        /*
         * transformIndexConstraints wants cxt.alist to contain only index
-        * statements, so transfer anything we already have into save_alist.
+        * 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);
+       transformIndexConstraints(&cxt);
 
-       transformFKConstraints(pstate, &cxt, skipValidation, true);
+       transformFKConstraints(&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.
+        * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
+        * subcommand has already been through transformIndexStmt.
         */
        foreach(l, cxt.alist)
        {
-               Node       *idxstmt = (Node *) lfirst(l);
+               IndexStmt  *idxstmt = (IndexStmt *) lfirst(l);
 
                Assert(IsA(idxstmt, IndexStmt));
+               idxstmt = transformIndexStmt(idxstmt, queryString);
                newcmd = makeNode(AlterTableCmd);
-               newcmd->subtype = AT_AddIndex;
-               newcmd->def = (Node *) transformIndexStmt((IndexStmt *) idxstmt,
-                                                                                                 queryString);
+               newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex;
+               newcmd->def = (Node *) idxstmt;
                newcmds = lappend(newcmds, newcmd);
        }
        cxt.alist = NIL;
@@ -1839,109 +2417,118 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
  * 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.
+ * NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE,
+ * EXCLUSION, and PRIMARY KEY constraints, but someday they ought to be
+ * supported for other constraint types.
  */
 static void
-transformConstraintAttrs(List *constraintList)
+transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
 {
-       Node       *lastprimarynode = NULL;
+       Constraint *lastprimarycon = NULL;
        bool            saw_deferrability = false;
        bool            saw_initially = false;
        ListCell   *clist;
 
+#define SUPPORTS_ATTRS(node)                           \
+       ((node) != NULL &&                                              \
+        ((node)->contype == CONSTR_PRIMARY ||  \
+         (node)->contype == CONSTR_UNIQUE ||   \
+         (node)->contype == CONSTR_EXCLUSION || \
+         (node)->contype == CONSTR_FOREIGN))
+
        foreach(clist, constraintList)
        {
-               Node       *node = lfirst(clist);
+               Constraint *con = (Constraint *) lfirst(clist);
 
-               if (!IsA(node, Constraint))
-               {
-                       lastprimarynode = node;
-                       /* reset flags for new primary node */
-                       saw_deferrability = false;
-                       saw_initially = false;
-               }
-               else
+               if (!IsA(con, Constraint))
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(con));
+               switch (con->contype)
                {
-                       Constraint *con = (Constraint *) node;
+                       case CONSTR_ATTR_DEFERRABLE:
+                               if (!SUPPORTS_ATTRS(lastprimarycon))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("misplaced DEFERRABLE clause"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               if (saw_deferrability)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               saw_deferrability = true;
+                               lastprimarycon->deferrable = true;
+                               break;
 
-                       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;
+                       case CONSTR_ATTR_NOT_DEFERRABLE:
+                               if (!SUPPORTS_ATTRS(lastprimarycon))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("misplaced NOT DEFERRABLE clause"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               if (saw_deferrability)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               saw_deferrability = true;
+                               lastprimarycon->deferrable = false;
+                               if (saw_initially &&
+                                       lastprimarycon->initdeferred)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               break;
 
-                                       /*
-                                        * 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;
-                       }
+                       case CONSTR_ATTR_DEFERRED:
+                               if (!SUPPORTS_ATTRS(lastprimarycon))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("misplaced INITIALLY DEFERRED clause"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               if (saw_initially)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               saw_initially = true;
+                               lastprimarycon->initdeferred = true;
+
+                               /*
+                                * If only INITIALLY DEFERRED appears, assume DEFERRABLE
+                                */
+                               if (!saw_deferrability)
+                                       lastprimarycon->deferrable = true;
+                               else if (!lastprimarycon->deferrable)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               break;
+
+                       case CONSTR_ATTR_IMMEDIATE:
+                               if (!SUPPORTS_ATTRS(lastprimarycon))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("misplaced INITIALLY IMMEDIATE clause"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               if (saw_initially)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
+                                                        parser_errposition(cxt->pstate, con->location)));
+                               saw_initially = true;
+                               lastprimarycon->initdeferred = false;
+                               break;
+
+                       default:
+                               /* Otherwise it's not an attribute */
+                               lastprimarycon = con;
+                               /* reset flags for new primary node */
+                               saw_deferrability = false;
+                               saw_initially = false;
+                               break;
                }
        }
 }
@@ -1950,12 +2537,30 @@ transformConstraintAttrs(List *constraintList)
  * Special handling of type definition for a column
  */
 static void
-transformColumnType(ParseState *pstate, ColumnDef *column)
+transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
 {
        /*
-        * All we really need to do here is verify that the type is valid.
+        * All we really need to do here is verify that the type is valid,
+        * including any collation spec that might be present.
         */
-       Type            ctype = typenameType(pstate, column->typename);
+       Type            ctype = typenameType(cxt->pstate, column->typeName, NULL);
+
+       if (column->collClause)
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype);
+
+               LookupCollation(cxt->pstate,
+                                               column->collClause->collname,
+                                               column->collClause->location);
+               /* Complain if COLLATE is applied to an uncollatable type */
+               if (!OidIsValid(typtup->typcollation))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("collations are not supported by type %s",
+                                                       format_type_be(HeapTupleGetOid(ctype))),
+                                        parser_errposition(cxt->pstate,
+                                                                               column->collClause->location)));
+       }
 
        ReleaseSysCache(ctype);
 }