]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/tablecmds.c
Update copyright for 2016
[postgresql] / src / backend / commands / tablecmds.c
index 66d5083e4aa2832b682a1af602c73eaae650d464..0b4a3346316417f1f0174c749af8aa337212e8c0 100644 (file)
@@ -3,7 +3,7 @@
  * tablecmds.c
  *       Commands for creating and altering table structures and settings
  *
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/pg_type_fn.h"
 #include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
 #include "catalog/toasting.h"
 #include "commands/cluster.h"
 #include "commands/comment.h"
@@ -71,6 +72,7 @@
 #include "parser/parse_type.h"
 #include "parser/parse_utilcmd.h"
 #include "parser/parser.h"
+#include "pgstat.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
@@ -281,9 +283,9 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
 static void AlterSeqNamespaces(Relation classRel, Relation rel,
                                   Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
                                   LOCKMODE lockmode);
-static void ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
+static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
                                          bool recurse, bool recursing, LOCKMODE lockmode);
-static void ATExecValidateConstraint(Relation rel, char *constrName,
+static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
                                                 bool recurse, bool recursing, LOCKMODE lockmode);
 static int transformColumnNameList(Oid relId, List *colList,
                                                int16 *attnums, Oid *atttypids);
@@ -305,14 +307,14 @@ static void createForeignKeyTriggers(Relation rel, Oid refRelOid,
                                                 Constraint *fkconstraint,
                                                 Oid constraintOid, Oid indexOid);
 static void ATController(AlterTableStmt *parsetree,
-                                                Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
+                        Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                  bool recurse, bool recursing, LOCKMODE lockmode);
 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                  AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATRewriteTables(AlterTableStmt *parsetree,
-                                                       List **wqueue, LOCKMODE lockmode);
+                               List **wqueue, LOCKMODE lockmode);
 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
 static void ATSimplePermissions(Relation rel, int allowed_targets);
@@ -324,48 +326,50 @@ static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cm
 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
                                                          DropBehavior behavior);
 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
-                               AlterTableCmd *cmd, LOCKMODE lockmode);
-static void ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
-                               ColumnDef *colDef, bool isOid,
-                               bool recurse, bool recursing, LOCKMODE lockmode);
-static void check_for_column_name_collision(Relation rel, const char *colname);
+                               bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
+static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
+                               Relation rel, ColumnDef *colDef, bool isOid,
+                               bool recurse, bool recursing,
+                               bool if_not_exists, LOCKMODE lockmode);
+static bool check_for_column_name_collision(Relation rel, const char *colname,
+                                                               bool if_not_exists);
 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
 static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
                          AlterTableCmd *cmd, LOCKMODE lockmode);
-static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
-static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
+static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
+static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
                                 const char *colName, LOCKMODE lockmode);
-static void ATExecColumnDefault(Relation rel, const char *colName,
+static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
                                        Node *newDefault, LOCKMODE lockmode);
 static void ATPrepSetStatistics(Relation rel, const char *colName,
                                        Node *newValue, LOCKMODE lockmode);
-static void ATExecSetStatistics(Relation rel, const char *colName,
+static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
                                        Node *newValue, LOCKMODE lockmode);
-static void ATExecSetOptions(Relation rel, const char *colName,
+static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
                                 Node *options, bool isReset, LOCKMODE lockmode);
-static void ATExecSetStorage(Relation rel, const char *colName,
+static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
                                 Node *newValue, LOCKMODE lockmode);
 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
                                 AlterTableCmd *cmd, LOCKMODE lockmode);
-static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
+static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
                                 bool recurse, bool recursing,
                                 bool missing_ok, LOCKMODE lockmode);
-static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
+static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                           IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
-static void ATExecAddConstraint(List **wqueue,
+static ObjectAddress ATExecAddConstraint(List **wqueue,
                                        AlteredTableInfo *tab, Relation rel,
                                        Constraint *newConstraint, bool recurse, bool is_readd,
                                        LOCKMODE lockmode);
-static void ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
+static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
                                                 IndexStmt *stmt, LOCKMODE lockmode);
-static void ATAddCheckConstraint(List **wqueue,
+static ObjectAddress ATAddCheckConstraint(List **wqueue,
                                         AlteredTableInfo *tab, Relation rel,
                                         Constraint *constr,
                                         bool recurse, bool recursing, bool is_readd,
                                         LOCKMODE lockmode);
-static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
+static ObjectAddress ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                  Constraint *fkconstraint, LOCKMODE lockmode);
 static void ATExecDropConstraint(Relation rel, const char *constrName,
                                         DropBehavior behavior,
@@ -376,22 +380,24 @@ static void ATPrepAlterColumnType(List **wqueue,
                                          bool recurse, bool recursing,
                                          AlterTableCmd *cmd, LOCKMODE lockmode);
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
-static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
+static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                          AlterTableCmd *cmd, LOCKMODE lockmode);
-static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
+static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
                                                                List *options, LOCKMODE lockmode);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
                                           LOCKMODE lockmode);
 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
                                         char *cmd, List **wqueue, LOCKMODE lockmode,
                                         bool rewrite);
+static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
+                                                Oid objid, Relation rel, char *conname);
 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
 static void TryReuseForeignKey(Oid oldId, Constraint *con);
 static void change_owner_fix_column_acls(Oid relationOid,
                                                         Oid oldOwnerId, Oid newOwnerId);
 static void change_owner_recurse_to_sequences(Oid relationOid,
                                                                  Oid newOwnerId, LOCKMODE lockmode);
-static void ATExecClusterOn(Relation rel, const char *indexName,
+static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
                                LOCKMODE lockmode);
 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
@@ -406,15 +412,16 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
 static void ATExecEnableDisableRule(Relation rel, char *rulename,
                                                char fires_when, LOCKMODE lockmode);
 static void ATPrepAddInherit(Relation child_rel);
-static void ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
-static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
+static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
+static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid);
-static void ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
+static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
 static void ATExecGenericOptions(Relation rel, List *options);
 static void ATExecEnableRowSecurity(Relation rel);
 static void ATExecDisableRowSecurity(Relation rel);
+static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
 
 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
                                   ForkNumber forkNum, char relpersistence);
@@ -434,17 +441,19 @@ static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
  * The other arguments are used to extend the behavior for other cases:
  * relkind: relkind to assign to the new relation
  * ownerId: if not InvalidOid, use this as the new relation's owner.
+ * typaddress: if not null, it's set to the pg_type entry's address.
  *
  * Note that permissions checks are done against current user regardless of
  * ownerId.  A nonzero ownerId is used when someone is creating a relation
  * "on behalf of" someone else, so we still want to see that the current user
  * has permissions to do it.
  *
- * If successful, returns the OID of the new relation.
+ * If successful, returns the address of the new relation.
  * ----------------------------------------------------------------
  */
-Oid
-DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
+ObjectAddress
+DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
+                          ObjectAddress *typaddress)
 {
        char            relname[NAMEDATALEN];
        Oid                     namespaceId;
@@ -464,6 +473,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
        AttrNumber      attnum;
        static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
        Oid                     ofTypeId;
+       ObjectAddress address;
 
        /*
         * Truncate relname to appropriate length (probably a waste of time, as
@@ -575,6 +585,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
         */
        descriptor = BuildDescForRelation(schema);
 
+       /*
+        * Notice that we allow OIDs here only for plain tables, even though some
+        * other relkinds can support them.  This is necessary because the
+        * default_with_oids GUC must apply only to plain tables and not any other
+        * relkind; doing otherwise would break existing pg_dump files.  We could
+        * allow explicit "WITH OIDS" while not allowing default_with_oids to
+        * affect other relkinds, but it would complicate interpretOidsOption().
+        */
        localHasOids = interpretOidsOption(stmt->options,
                                                                           (relkind == RELKIND_RELATION));
        descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
@@ -619,6 +637,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 
                        cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
                        cooked->contype = CONSTR_DEFAULT;
+                       cooked->conoid = InvalidOid;            /* until created */
                        cooked->name = NULL;
                        cooked->attnum = attnum;
                        cooked->expr = colDef->cooked_default;
@@ -656,7 +675,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                                                                                  reloptions,
                                                                                  true,
                                                                                  allowSystemTableMods,
-                                                                                 false);
+                                                                                 false,
+                                                                                 typaddress);
 
        /* Store inheritance information for new rel. */
        StoreCatalogInheritance(relationId, inheritOids);
@@ -688,13 +708,15 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
                                                                  true, true, false);
 
+       ObjectAddressSet(address, RelationRelationId, relationId);
+
        /*
         * Clean up.  We keep lock on new relation (although it shouldn't be
         * visible to anyone else anyway, until commit).
         */
        relation_close(rel, NoLock);
 
-       return relationId;
+       return address;
 }
 
 /*
@@ -1218,8 +1240,10 @@ ExecuteTruncate(TruncateStmt *stmt)
                        /*
                         * Reconstruct the indexes to match, and we're done.
                         */
-                       reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST);
+                       reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
                }
+
+               pgstat_count_truncate(rel);
        }
 
        /*
@@ -1495,10 +1519,11 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                 */
                relation = heap_openrv(parent, ShareUpdateExclusiveLock);
 
-               if (relation->rd_rel->relkind != RELKIND_RELATION)
+               if (relation->rd_rel->relkind != RELKIND_RELATION &&
+                       relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
                        ereport(ERROR,
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                        errmsg("inherited relation \"%s\" is not a table",
+                                        errmsg("inherited relation \"%s\" is not a table or foreign table",
                                                        parent->relname)));
                /* Permanent rels cannot inherit from temporary ones */
                if (relpersistence != RELPERSISTENCE_TEMP &&
@@ -1588,8 +1613,10 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                                errmsg("inherited column \"%s\" has a type conflict",
                                                           attributeName),
                                                         errdetail("%s versus %s",
-                                                                          TypeNameToString(def->typeName),
-                                                                          format_type_be(attribute->atttypid))));
+                                                                          format_type_with_typemod(defTypeId,
+                                                                                                                               deftypmod),
+                                                               format_type_with_typemod(attribute->atttypid,
+                                                                                                       attribute->atttypmod))));
                                defCollId = GetColumnDefCollation(NULL, def, defTypeId);
                                if (defCollId != attribute->attcollation)
                                        ereport(ERROR,
@@ -1732,6 +1759,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
 
                                        cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
                                        cooked->contype = CONSTR_CHECK;
+                                       cooked->conoid = InvalidOid;            /* until created */
                                        cooked->name = pstrdup(name);
                                        cooked->attnum = 0; /* not used for constraints */
                                        cooked->expr = expr;
@@ -1747,9 +1775,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                pfree(newattno);
 
                /*
-                * Close the parent rel, but keep our AccessShareLock on it until xact
-                * commit.  That will prevent someone else from deleting or ALTERing
-                * the parent before the child is committed.
+                * Close the parent rel, but keep our ShareUpdateExclusiveLock on it
+                * until xact commit.  That will prevent someone else from deleting or
+                * ALTERing the parent before the child is committed.
                 */
                heap_close(relation, NoLock);
        }
@@ -1761,7 +1789,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
         */
        if (inhSchema != NIL)
        {
-               int             schema_attno = 0;
+               int                     schema_attno = 0;
 
                foreach(entry, schema)
                {
@@ -1789,14 +1817,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                 * Yes, try to merge the two column definitions. They must
                                 * have the same type, typmod, and collation.
                                 */
-                                if (exist_attno == schema_attno)
+                               if (exist_attno == schema_attno)
                                        ereport(NOTICE,
-                                          (errmsg("merging column \"%s\" with inherited definition",
-                                                          attributeName)));
+                                       (errmsg("merging column \"%s\" with inherited definition",
+                                                       attributeName)));
                                else
                                        ereport(NOTICE,
-                                          (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
-                                               errdetail("User-specified column moved to the position of the inherited column.")));
+                                                       (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
+                                                        errdetail("User-specified column moved to the position of the inherited column.")));
                                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
                                typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
                                typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
@@ -1806,8 +1834,10 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                                         errmsg("column \"%s\" has a type conflict",
                                                                        attributeName),
                                                         errdetail("%s versus %s",
-                                                                          TypeNameToString(def->typeName),
-                                                                          TypeNameToString(newdef->typeName))));
+                                                                          format_type_with_typemod(defTypeId,
+                                                                                                                               deftypmod),
+                                                                          format_type_with_typemod(newTypeId,
+                                                                                                                               newtypmod))));
                                defcollid = GetColumnDefCollation(NULL, def, defTypeId);
                                newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
                                if (defcollid != newcollid)
@@ -2155,8 +2185,10 @@ renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
 
 /*
  *             renameatt_internal              - workhorse for renameatt
+ *
+ * Return value is the attribute number in the 'myrelid' relation.
  */
-static void
+static AttrNumber
 renameatt_internal(Oid myrelid,
                                   const char *oldattname,
                                   const char *newattname,
@@ -2169,7 +2201,7 @@ renameatt_internal(Oid myrelid,
        Relation        attrelation;
        HeapTuple       atttup;
        Form_pg_attribute attform;
-       int                     attnum;
+       AttrNumber      attnum;
 
        /*
         * Grab an exclusive lock on the target table, which we will NOT release
@@ -2280,7 +2312,7 @@ renameatt_internal(Oid myrelid,
                                                oldattname)));
 
        /* new name should not already exist */
-       check_for_column_name_collision(targetrelation, newattname);
+       (void) check_for_column_name_collision(targetrelation, newattname, false);
 
        /* apply the update */
        namestrcpy(&(attform->attname), newattname);
@@ -2297,6 +2329,8 @@ renameatt_internal(Oid myrelid,
        heap_close(attrelation, RowExclusiveLock);
 
        relation_close(targetrelation, NoLock);         /* close rel but keep lock */
+
+       return attnum;
 }
 
 /*
@@ -2318,12 +2352,16 @@ RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
 }
 
 /*
- *             renameatt               - changes the name of a attribute in a relation
+ *             renameatt               - changes the name of an attribute in a relation
+ *
+ * The returned ObjectAddress is that of the renamed column.
  */
-Oid
+ObjectAddress
 renameatt(RenameStmt *stmt)
 {
        Oid                     relid;
+       AttrNumber      attnum;
+       ObjectAddress address;
 
        /* lock level taken here should match renameatt_internal */
        relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
@@ -2336,26 +2374,27 @@ renameatt(RenameStmt *stmt)
                ereport(NOTICE,
                                (errmsg("relation \"%s\" does not exist, skipping",
                                                stmt->relation->relname)));
-               return InvalidOid;
+               return InvalidObjectAddress;
        }
 
-       renameatt_internal(relid,
-                                          stmt->subname,       /* old att name */
-                                          stmt->newname,       /* new att name */
-                                          interpretInhOption(stmt->relation->inhOpt),          /* recursive? */
-                                          false,       /* recursing? */
-                                          0,           /* expected inhcount */
-                                          stmt->behavior);
+       attnum =
+               renameatt_internal(relid,
+                                                  stmt->subname,               /* old att name */
+                                                  stmt->newname,               /* new att name */
+                                                  interpretInhOption(stmt->relation->inhOpt),  /* recursive? */
+                                                  false,               /* recursing? */
+                                                  0,   /* expected inhcount */
+                                                  stmt->behavior);
 
-       /* This is an ALTER TABLE command so it's about the relid */
-       return relid;
-}
+       ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
 
+       return address;
+}
 
 /*
  * same logic as renameatt_internal
  */
-static Oid
+static ObjectAddress
 rename_constraint_internal(Oid myrelid,
                                                   Oid mytypid,
                                                   const char *oldconname,
@@ -2368,6 +2407,7 @@ rename_constraint_internal(Oid myrelid,
        Oid                     constraintOid;
        HeapTuple       tuple;
        Form_pg_constraint con;
+       ObjectAddress address;
 
        AssertArg(!myrelid || !mytypid);
 
@@ -2443,15 +2483,17 @@ rename_constraint_internal(Oid myrelid,
        else
                RenameConstraintById(constraintOid, newconname);
 
+       ObjectAddressSet(address, ConstraintRelationId, constraintOid);
+
        ReleaseSysCache(tuple);
 
        if (targetrelation)
                relation_close(targetrelation, NoLock); /* close rel but keep lock */
 
-       return constraintOid;
+       return address;
 }
 
-Oid
+ObjectAddress
 RenameConstraint(RenameStmt *stmt)
 {
        Oid                     relid = InvalidOid;
@@ -2475,9 +2517,16 @@ RenameConstraint(RenameStmt *stmt)
        {
                /* lock level taken here should match rename_constraint_internal */
                relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
-                                                                                false, false,
+                                                                                stmt->missing_ok, false,
                                                                                 RangeVarCallbackForRenameAttribute,
                                                                                 NULL);
+               if (!OidIsValid(relid))
+               {
+                       ereport(NOTICE,
+                                       (errmsg("relation \"%s\" does not exist, skipping",
+                                                       stmt->relation->relname)));
+                       return InvalidObjectAddress;
+               }
        }
 
        return
@@ -2494,10 +2543,11 @@ RenameConstraint(RenameStmt *stmt)
  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
  * RENAME
  */
-Oid
+ObjectAddress
 RenameRelation(RenameStmt *stmt)
 {
        Oid                     relid;
+       ObjectAddress address;
 
        /*
         * Grab an exclusive lock on the target table, index, sequence, view,
@@ -2517,13 +2567,15 @@ RenameRelation(RenameStmt *stmt)
                ereport(NOTICE,
                                (errmsg("relation \"%s\" does not exist, skipping",
                                                stmt->relation->relname)));
-               return InvalidOid;
+               return InvalidObjectAddress;
        }
 
        /* Do the work */
        RenameRelationInternal(relid, stmt->newname, false);
 
-       return relid;
+       ObjectAddressSet(address, RelationRelationId, relid);
+
+       return address;
 }
 
 /*
@@ -2747,6 +2799,8 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
 
        rel = relation_open(relid, lockmode);
 
+       EventTriggerAlterTableRelid(relid);
+
        ATController(NULL, rel, cmds, recurse, lockmode);
 }
 
@@ -2858,13 +2912,8 @@ AlterTableGetLockLevel(List *cmds)
                                break;
 
                                /*
-                                * These subcommands affect write operations only. XXX
-                                * Theoretically, these could be ShareRowExclusiveLock.
+                                * These subcommands affect write operations only.
                                 */
-                       case AT_ColumnDefault:
-                       case AT_ProcessedConstraint:            /* becomes AT_AddConstraint */
-                       case AT_AddConstraintRecurse:           /* becomes AT_AddConstraint */
-                       case AT_ReAddConstraint:        /* becomes AT_AddConstraint */
                        case AT_EnableTrig:
                        case AT_EnableAlwaysTrig:
                        case AT_EnableReplicaTrig:
@@ -2873,6 +2922,14 @@ AlterTableGetLockLevel(List *cmds)
                        case AT_DisableTrig:
                        case AT_DisableTrigAll:
                        case AT_DisableTrigUser:
+                               cmd_lockmode = ShareRowExclusiveLock;
+                               break;
+
+                               /*
+                                * These subcommands affect write operations only. XXX
+                                * Theoretically, these could be ShareRowExclusiveLock.
+                                */
+                       case AT_ColumnDefault:
                        case AT_AlterConstraint:
                        case AT_AddIndex:       /* from ADD CONSTRAINT */
                        case AT_AddIndexConstraint:
@@ -2880,10 +2937,15 @@ AlterTableGetLockLevel(List *cmds)
                        case AT_SetNotNull:
                        case AT_EnableRowSecurity:
                        case AT_DisableRowSecurity:
+                       case AT_ForceRowSecurity:
+                       case AT_NoForceRowSecurity:
                                cmd_lockmode = AccessExclusiveLock;
                                break;
 
                        case AT_AddConstraint:
+                       case AT_ProcessedConstraint:            /* becomes AT_AddConstraint */
+                       case AT_AddConstraintRecurse:           /* becomes AT_AddConstraint */
+                       case AT_ReAddConstraint:        /* becomes AT_AddConstraint */
                                if (IsA(cmd->def, Constraint))
                                {
                                        Constraint *con = (Constraint *) cmd->def;
@@ -2909,11 +2971,9 @@ AlterTableGetLockLevel(List *cmds)
                                                        /*
                                                         * We add triggers to both tables when we add a
                                                         * Foreign Key, so the lock level must be at least
-                                                        * as strong as CREATE TRIGGER. XXX Might be set
-                                                        * down to ShareRowExclusiveLock though trigger
-                                                        * info is accessed by pg_get_triggerdef
+                                                        * as strong as CREATE TRIGGER.
                                                         */
-                                                       cmd_lockmode = AccessExclusiveLock;
+                                                       cmd_lockmode = ShareRowExclusiveLock;
                                                        break;
 
                                                default:
@@ -2987,16 +3047,12 @@ AlterTableGetLockLevel(List *cmds)
                                 * are set here for tables, views and indexes; for historical
                                 * reasons these can all be used with ALTER TABLE, so we can't
                                 * decide between them using the basic grammar.
-                                *
-                                * XXX Look in detail at each option to determine lock level,
-                                * e.g. cmd_lockmode = GetRelOptionsLockLevel((List *)
-                                * cmd->def);
                                 */
                        case AT_SetRelOptions:          /* Uses MVCC in getIndexes() and
                                                                                 * getTables() */
                        case AT_ResetRelOptions:        /* Uses MVCC in getIndexes() and
                                                                                 * getTables() */
-                               cmd_lockmode = AccessExclusiveLock;
+                               cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
                                break;
 
                        default:                        /* oops */
@@ -3082,14 +3138,16 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_AddColumn:              /* ADD COLUMN */
                        ATSimplePermissions(rel,
                                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
-                       ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+                       ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
+                                                       lockmode);
                        /* Recursion occurs during execution phase */
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_AddColumnToView:                /* add column via CREATE OR REPLACE
                                                                                 * VIEW */
                        ATSimplePermissions(rel, ATT_VIEW);
-                       ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
+                       ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
+                                                       lockmode);
                        /* Recursion occurs during execution phase */
                        pass = AT_PASS_ADD_COL;
                        break;
@@ -3131,7 +3189,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_MISC;
                        break;
                case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
-                       ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
                        ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = AT_PASS_MISC;
@@ -3219,14 +3277,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_MISC;
                        break;
                case AT_AddOids:                /* SET WITH OIDS */
-                       ATSimplePermissions(rel, ATT_TABLE);
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
                        if (!rel->rd_rel->relhasoids || recursing)
                                ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
                        /* Recursion occurs during execution phase */
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_DropOids:               /* SET WITHOUT OIDS */
-                       ATSimplePermissions(rel, ATT_TABLE);
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
                        /* Performs own recursion */
                        if (rel->rd_rel->relhasoids)
                        {
@@ -3254,17 +3312,23 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_MISC;
                        break;
                case AT_AddInherit:             /* INHERIT */
-                       ATSimplePermissions(rel, ATT_TABLE);
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
                        /* This command never recurses */
                        ATPrepAddInherit(rel);
                        pass = AT_PASS_MISC;
                        break;
+               case AT_DropInherit:    /* NO INHERIT */
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+                       /* This command never recurses */
+                       /* No command-specific prep needed */
+                       pass = AT_PASS_MISC;
+                       break;
                case AT_AlterConstraint:                /* ALTER CONSTRAINT */
                        ATSimplePermissions(rel, ATT_TABLE);
                        pass = AT_PASS_MISC;
                        break;
                case AT_ValidateConstraint:             /* VALIDATE CONSTRAINT */
-                       ATSimplePermissions(rel, ATT_TABLE);
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
                        /* Recursion occurs during execution phase */
                        /* No command-specific prep needed except saving recurse flag */
                        if (recurse)
@@ -3292,11 +3356,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_EnableAlwaysRule:
                case AT_EnableReplicaRule:
                case AT_DisableRule:
-               case AT_DropInherit:    /* NO INHERIT */
                case AT_AddOf:                  /* OF */
                case AT_DropOf: /* NOT OF */
                case AT_EnableRowSecurity:
                case AT_DisableRowSecurity:
+               case AT_ForceRowSecurity:
+               case AT_NoForceRowSecurity:
                        ATSimplePermissions(rel, ATT_TABLE);
                        /* These commands never recurse */
                        /* No command-specific prep needed */
@@ -3390,78 +3455,95 @@ static void
 ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                  AlterTableCmd *cmd, LOCKMODE lockmode)
 {
+       ObjectAddress address = InvalidObjectAddress;
+
        switch (cmd->subtype)
        {
                case AT_AddColumn:              /* ADD COLUMN */
                case AT_AddColumnToView:                /* add column via CREATE OR REPLACE
                                                                                 * VIEW */
-                       ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
-                                                       false, false, false, lockmode);
+                       address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                                                                         false, false, false,
+                                                                         false, lockmode);
                        break;
                case AT_AddColumnRecurse:
-                       ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
-                                                       false, true, false, lockmode);
+                       address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                                                                         false, true, false,
+                                                                         cmd->missing_ok, lockmode);
                        break;
                case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
-                       ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
+                       address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
                        break;
                case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
-                       ATExecDropNotNull(rel, cmd->name, lockmode);
+                       address = ATExecDropNotNull(rel, cmd->name, lockmode);
                        break;
                case AT_SetNotNull:             /* ALTER COLUMN SET NOT NULL */
-                       ATExecSetNotNull(tab, rel, cmd->name, lockmode);
+                       address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
                        break;
                case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
-                       ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
+                       address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
                        break;
                case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
-                       ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
+                       address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
                        break;
                case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
-                       ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
+                       address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
                        break;
                case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
-                       ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
+                       address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
                        break;
                case AT_DropColumn:             /* DROP COLUMN */
-                       ATExecDropColumn(wqueue, rel, cmd->name,
-                                        cmd->behavior, false, false, cmd->missing_ok, lockmode);
+                       address = ATExecDropColumn(wqueue, rel, cmd->name,
+                                                                          cmd->behavior, false, false,
+                                                                          cmd->missing_ok, lockmode);
                        break;
                case AT_DropColumnRecurse:              /* DROP COLUMN with recursion */
-                       ATExecDropColumn(wqueue, rel, cmd->name,
-                                         cmd->behavior, true, false, cmd->missing_ok, lockmode);
+                       address = ATExecDropColumn(wqueue, rel, cmd->name,
+                                                                          cmd->behavior, true, false,
+                                                                          cmd->missing_ok, lockmode);
                        break;
                case AT_AddIndex:               /* ADD INDEX */
-                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false, lockmode);
+                       address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
+                                                                        lockmode);
                        break;
                case AT_ReAddIndex:             /* ADD INDEX */
-                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true, lockmode);
+                       address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
+                                                                        lockmode);
                        break;
                case AT_AddConstraint:  /* ADD CONSTRAINT */
-                       ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
-                                                               false, false, lockmode);
+                       address =
+                               ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+                                                                       false, false, lockmode);
                        break;
                case AT_AddConstraintRecurse:   /* ADD CONSTRAINT with recursion */
-                       ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
-                                                               true, false, lockmode);
+                       address =
+                               ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+                                                                       true, false, lockmode);
                        break;
                case AT_ReAddConstraint:                /* Re-add pre-existing check
                                                                                 * constraint */
-                       ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
-                                                               false, true, lockmode);
+                       address =
+                               ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+                                                                       true, true, lockmode);
+                       break;
+               case AT_ReAddComment:   /* Re-add existing comment */
+                       address = CommentObject((CommentStmt *) cmd->def);
                        break;
                case AT_AddIndexConstraint:             /* ADD CONSTRAINT USING INDEX */
-                       ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def, lockmode);
+                       address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
+                                                                                          lockmode);
                        break;
                case AT_AlterConstraint:                /* ALTER CONSTRAINT */
-                       ATExecAlterConstraint(rel, cmd, false, false, lockmode);
+                       address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
                        break;
                case AT_ValidateConstraint:             /* VALIDATE CONSTRAINT */
-                       ATExecValidateConstraint(rel, cmd->name, false, false, lockmode);
+                       address = ATExecValidateConstraint(rel, cmd->name, false, false,
+                                                                                          lockmode);
                        break;
                case AT_ValidateConstraintRecurse:              /* VALIDATE CONSTRAINT with
                                                                                                 * recursion */
-                       ATExecValidateConstraint(rel, cmd->name, true, false, lockmode);
+                       address = ATExecValidateConstraint(rel, cmd->name, true, false,
+                                                                                          lockmode);
                        break;
                case AT_DropConstraint: /* DROP CONSTRAINT */
                        ATExecDropConstraint(rel, cmd->name, cmd->behavior,
@@ -3474,18 +3556,20 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                                                 cmd->missing_ok, lockmode);
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
-                       ATExecAlterColumnType(tab, rel, cmd, lockmode);
+                       address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
                        break;
                case AT_AlterColumnGenericOptions:              /* ALTER COLUMN OPTIONS */
-                       ATExecAlterColumnGenericOptions(rel, cmd->name, (List *) cmd->def, lockmode);
+                       address =
+                               ATExecAlterColumnGenericOptions(rel, cmd->name,
+                                                                                               (List *) cmd->def, lockmode);
                        break;
                case AT_ChangeOwner:    /* ALTER OWNER */
                        ATExecChangeOwner(RelationGetRelid(rel),
-                                                         get_role_oid(cmd->name, false),
+                                                         get_rolespec_oid(cmd->newowner, false),
                                                          false, lockmode);
                        break;
                case AT_ClusterOn:              /* CLUSTER ON */
-                       ATExecClusterOn(rel, cmd->name, lockmode);
+                       address = ATExecClusterOn(rel, cmd->name, lockmode);
                        break;
                case AT_DropCluster:    /* SET WITHOUT CLUSTER */
                        ATExecDropCluster(rel, lockmode);
@@ -3496,14 +3580,18 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                case AT_AddOids:                /* SET WITH OIDS */
                        /* Use the ADD COLUMN code, unless prep decided to do nothing */
                        if (cmd->def != NULL)
-                               ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
-                                                               true, false, false, lockmode);
+                               address =
+                                       ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                                                                       true, false, false,
+                                                                       cmd->missing_ok, lockmode);
                        break;
                case AT_AddOidsRecurse: /* SET WITH OIDS */
                        /* Use the ADD COLUMN code, unless prep decided to do nothing */
                        if (cmd->def != NULL)
-                               ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
-                                                               true, true, false, lockmode);
+                               address =
+                                       ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                                                                       true, true, false,
+                                                                       cmd->missing_ok, lockmode);
                        break;
                case AT_DropOids:               /* SET WITHOUT OIDS */
 
@@ -3574,13 +3662,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        break;
 
                case AT_AddInherit:
-                       ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
+                       address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
                        break;
                case AT_DropInherit:
-                       ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
+                       address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
                        break;
                case AT_AddOf:
-                       ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
+                       address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
                        break;
                case AT_DropOf:
                        ATExecDropOf(rel, lockmode);
@@ -3594,6 +3682,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                case AT_DisableRowSecurity:
                        ATExecDisableRowSecurity(rel);
                        break;
+               case AT_ForceRowSecurity:
+                       ATExecForceNoForceRowSecurity(rel, true);
+                       break;
+               case AT_NoForceRowSecurity:
+                       ATExecForceNoForceRowSecurity(rel, false);
+                       break;
                case AT_GenericOptions:
                        ATExecGenericOptions(rel, (List *) cmd->def);
                        break;
@@ -3603,6 +3697,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        break;
        }
 
+       /*
+        * Report the subcommand to interested event triggers.
+        */
+       EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
+
        /*
         * Bump the command counter to ensure the next subcommand in the sequence
         * can see the changes so far
@@ -3727,7 +3826,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
                         * And fire it only once.
                         */
                        if (parsetree)
-                               EventTriggerTableRewrite((Node *)parsetree,
+                               EventTriggerTableRewrite((Node *) parsetree,
                                                                                 tab->relid,
                                                                                 tab->rewrite);
 
@@ -4247,6 +4346,9 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
                case ATT_TABLE | ATT_VIEW:
                        msg = _("\"%s\" is not a table or view");
                        break;
+               case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
+                       msg = _("\"%s\" is not a table, view, or foreign table");
+                       break;
                case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
                        msg = _("\"%s\" is not a table, view, materialized view, or index");
                        break;
@@ -4256,6 +4358,9 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
                case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
                        msg = _("\"%s\" is not a table, materialized view, or index");
                        break;
+               case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
+                       msg = _("\"%s\" is not a table, materialized view, or foreign table");
+                       break;
                case ATT_TABLE | ATT_FOREIGN_TABLE:
                        msg = _("\"%s\" is not a table or foreign table");
                        break;
@@ -4263,7 +4368,7 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
                        msg = _("\"%s\" is not a table, composite type, or foreign table");
                        break;
                case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
-                       msg = _("\"%s\" is not a table, materialized view, composite type, or foreign table");
+                       msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
                        break;
                case ATT_VIEW:
                        msg = _("\"%s\" is not a view");
@@ -4295,10 +4400,12 @@ ATSimpleRecursion(List **wqueue, Relation rel,
                                  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
 {
        /*
-        * Propagate to children if desired.  Non-table relations never have
-        * children, so no need to search in that case.
+        * Propagate to children if desired.  Only plain tables and foreign tables
+        * have children, so no need to search for other relkinds.
         */
-       if (recurse && rel->rd_rel->relkind == RELKIND_RELATION)
+       if (recurse &&
+               (rel->rd_rel->relkind == RELKIND_RELATION ||
+                rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE))
        {
                Oid                     relid = RelationGetRelid(rel);
                ListCell   *child;
@@ -4573,7 +4680,7 @@ check_of_type(HeapTuple typetuple)
  */
 static void
 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
-                               AlterTableCmd *cmd, LOCKMODE lockmode)
+                               bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        if (rel->rd_rel->reloftype && !recursing)
                ereport(ERROR,
@@ -4583,14 +4690,19 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
        if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
                ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
 
-       if (recurse)
+       if (recurse && !is_view)
                cmd->subtype = AT_AddColumnRecurse;
 }
 
-static void
+/*
+ * Add a column to a table; this handles the AT_AddOids cases as well.  The
+ * return value is the address of the new column in the parent relation.
+ */
+static ObjectAddress
 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                ColumnDef *colDef, bool isOid,
-                               bool recurse, bool recursing, LOCKMODE lockmode)
+                               bool recurse, bool recursing,
+                               bool if_not_exists, LOCKMODE lockmode)
 {
        Oid                     myrelid = RelationGetRelid(rel);
        Relation        pgclass,
@@ -4608,10 +4720,11 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
        List       *children;
        ListCell   *child;
        AclResult       aclresult;
+       ObjectAddress address;
 
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
-               ATSimplePermissions(rel, ATT_TABLE);
+               ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
 
        attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
 
@@ -4672,7 +4785,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                          colDef->colname, RelationGetRelationName(rel))));
 
                        heap_close(attrdesc, RowExclusiveLock);
-                       return;
+                       return InvalidObjectAddress;
                }
        }
 
@@ -4683,8 +4796,14 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                elog(ERROR, "cache lookup failed for relation %u", myrelid);
        relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
 
-       /* new name should not already exist */
-       check_for_column_name_collision(rel, colDef->colname);
+       /* skip if the name already exists and if_not_exists is true */
+       if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
+       {
+               heap_close(attrdesc, RowExclusiveLock);
+               heap_freetuple(reltup);
+               heap_close(pgclass, RowExclusiveLock);
+               return InvalidObjectAddress;
+       }
 
        /* Determine the new attribute's number */
        if (isOid)
@@ -4819,7 +4938,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
        {
                defval = (Expr *) build_column_default(rel, attribute.attnum);
 
-               if (!defval && GetDomainConstraints(typeOid) != NIL)
+               if (!defval && DomainHasConstraints(typeOid))
                {
                        Oid                     baseTypeId;
                        int32           baseTypeMod;
@@ -4912,20 +5031,25 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                /* Find or create work queue entry for this table */
                childtab = ATGetQueueEntry(wqueue, childrel);
 
-               /* Recurse to child */
+               /* Recurse to child; return value is ignored */
                ATExecAddColumn(wqueue, childtab, childrel,
-                                               colDef, isOid, recurse, true, lockmode);
+                                               colDef, isOid, recurse, true,
+                                               if_not_exists, lockmode);
 
                heap_close(childrel, NoLock);
        }
+
+       ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
+       return address;
 }
 
 /*
  * If a new or renamed column will collide with the name of an existing
- * column, error out.
+ * column and if_not_exists is false then error out, else do nothing.
  */
-static void
-check_for_column_name_collision(Relation rel, const char *colname)
+static bool
+check_for_column_name_collision(Relation rel, const char *colname,
+                                                               bool if_not_exists)
 {
        HeapTuple       attTuple;
        int                     attnum;
@@ -4938,7 +5062,7 @@ check_for_column_name_collision(Relation rel, const char *colname)
                                                           ObjectIdGetDatum(RelationGetRelid(rel)),
                                                           PointerGetDatum(colname));
        if (!HeapTupleIsValid(attTuple))
-               return;
+               return true;
 
        attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
        ReleaseSysCache(attTuple);
@@ -4954,10 +5078,23 @@ check_for_column_name_collision(Relation rel, const char *colname)
                         errmsg("column name \"%s\" conflicts with a system column name",
                                        colname)));
        else
+       {
+               if (if_not_exists)
+               {
+                       ereport(NOTICE,
+                                       (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                        errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
+                                                       colname, RelationGetRelationName(rel))));
+                       return false;
+               }
+
                ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_COLUMN),
                                 errmsg("column \"%s\" of relation \"%s\" already exists",
                                                colname, RelationGetRelationName(rel))));
+       }
+
+       return true;
 }
 
 /*
@@ -5023,7 +5160,7 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
                cdef->location = -1;
                cmd->def = (Node *) cdef;
        }
-       ATPrepAddColumn(wqueue, rel, recurse, false, cmd, lockmode);
+       ATPrepAddColumn(wqueue, rel, recurse, false, false, cmd, lockmode);
 
        if (recurse)
                cmd->subtype = AT_AddOidsRecurse;
@@ -5031,8 +5168,11 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
 
 /*
  * ALTER TABLE ALTER COLUMN DROP NOT NULL
+ *
+ * Return the address of the modified column.  If the column was already
+ * nullable, InvalidObjectAddress is returned.
  */
-static void
+static ObjectAddress
 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
 {
        HeapTuple       tuple;
@@ -5040,6 +5180,7 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
        Relation        attr_rel;
        List       *indexoidlist;
        ListCell   *indexoidscan;
+       ObjectAddress address;
 
        /*
         * lookup the attribute
@@ -5117,24 +5258,35 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
 
                /* keep the system catalog indexes current */
                CatalogUpdateIndexes(attr_rel, tuple);
+
+               ObjectAddressSubSet(address, RelationRelationId,
+                                                       RelationGetRelid(rel), attnum);
        }
+       else
+               address = InvalidObjectAddress;
 
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel), attnum);
 
        heap_close(attr_rel, RowExclusiveLock);
+
+       return address;
 }
 
 /*
  * ALTER TABLE ALTER COLUMN SET NOT NULL
+ *
+ * Return the address of the modified column.  If the column was already NOT
+ * NULL, InvalidObjectAddress is returned.
  */
-static void
+static ObjectAddress
 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
                                 const char *colName, LOCKMODE lockmode)
 {
        HeapTuple       tuple;
        AttrNumber      attnum;
        Relation        attr_rel;
+       ObjectAddress address;
 
        /*
         * lookup the attribute
@@ -5172,22 +5324,32 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
 
                /* Tell Phase 3 it needs to test the constraint */
                tab->new_notnull = true;
+
+               ObjectAddressSubSet(address, RelationRelationId,
+                                                       RelationGetRelid(rel), attnum);
        }
+       else
+               address = InvalidObjectAddress;
 
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel), attnum);
 
        heap_close(attr_rel, RowExclusiveLock);
+
+       return address;
 }
 
 /*
  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
+ *
+ * Return the address of the affected column.
  */
-static void
+static ObjectAddress
 ATExecColumnDefault(Relation rel, const char *colName,
                                        Node *newDefault, LOCKMODE lockmode)
 {
        AttrNumber      attnum;
+       ObjectAddress address;
 
        /*
         * get the number of the attribute
@@ -5234,6 +5396,10 @@ ATExecColumnDefault(Relation rel, const char *colName,
                AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
                                                                  false, true, false);
        }
+
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
+       return address;
 }
 
 /*
@@ -5263,13 +5429,18 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
                                           RelationGetRelationName(rel));
 }
 
-static void
+/*
+ * Return value is the address of the modified column
+ */
+static ObjectAddress
 ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 {
        int                     newtarget;
        Relation        attrelation;
        HeapTuple       tuple;
        Form_pg_attribute attrtuple;
+       AttrNumber      attnum;
+       ObjectAddress address;
 
        Assert(IsA(newValue, Integer));
        newtarget = intVal(newValue);
@@ -5304,7 +5475,8 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
                                                colName, RelationGetRelationName(rel))));
        attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
 
-       if (attrtuple->attnum <= 0)
+       attnum = attrtuple->attnum;
+       if (attnum <= 0)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot alter system column \"%s\"",
@@ -5320,12 +5492,19 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel),
                                                          attrtuple->attnum);
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
        heap_freetuple(tuple);
 
        heap_close(attrelation, RowExclusiveLock);
+
+       return address;
 }
 
-static void
+/*
+ * Return value is the address of the modified column
+ */
+static ObjectAddress
 ATExecSetOptions(Relation rel, const char *colName, Node *options,
                                 bool isReset, LOCKMODE lockmode)
 {
@@ -5333,9 +5512,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
        HeapTuple       tuple,
                                newtuple;
        Form_pg_attribute attrtuple;
+       AttrNumber      attnum;
        Datum           datum,
                                newOptions;
        bool            isnull;
+       ObjectAddress address;
        Datum           repl_val[Natts_pg_attribute];
        bool            repl_null[Natts_pg_attribute];
        bool            repl_repl[Natts_pg_attribute];
@@ -5351,7 +5532,8 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
                                                colName, RelationGetRelationName(rel))));
        attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
 
-       if (attrtuple->attnum <= 0)
+       attnum = attrtuple->attnum;
+       if (attnum <= 0)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot alter system column \"%s\"",
@@ -5385,17 +5567,24 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel),
                                                          attrtuple->attnum);
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
+
        heap_freetuple(newtuple);
 
        ReleaseSysCache(tuple);
 
        heap_close(attrelation, RowExclusiveLock);
+
+       return address;
 }
 
 /*
  * ALTER TABLE ALTER COLUMN SET STORAGE
+ *
+ * Return value is the address of the modified column
  */
-static void
+static ObjectAddress
 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 {
        char       *storagemode;
@@ -5403,6 +5592,8 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
        Relation        attrelation;
        HeapTuple       tuple;
        Form_pg_attribute attrtuple;
+       AttrNumber      attnum;
+       ObjectAddress address;
 
        Assert(IsA(newValue, String));
        storagemode = strVal(newValue);
@@ -5435,7 +5626,8 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
                                                colName, RelationGetRelationName(rel))));
        attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
 
-       if (attrtuple->attnum <= 0)
+       attnum = attrtuple->attnum;
+       if (attnum <= 0)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot alter system column \"%s\"",
@@ -5465,6 +5657,10 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
        heap_freetuple(tuple);
 
        heap_close(attrelation, RowExclusiveLock);
+
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
+       return address;
 }
 
 
@@ -5493,7 +5689,10 @@ ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
                cmd->subtype = AT_DropColumnRecurse;
 }
 
-static void
+/*
+ * Return value is the address of the dropped column.
+ */
+static ObjectAddress
 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
                                 bool recurse, bool recursing,
@@ -5507,7 +5706,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
-               ATSimplePermissions(rel, ATT_TABLE);
+               ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
 
        /*
         * get the number of the attribute
@@ -5527,7 +5726,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                        ereport(NOTICE,
                                        (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
                                                        colName, RelationGetRelationName(rel))));
-                       return;
+                       return InvalidObjectAddress;
                }
        }
        targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
@@ -5681,6 +5880,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                /* Tell Phase 3 to physically remove the OID column */
                tab->rewrite |= AT_REWRITE_ALTER_OID;
        }
+
+       return object;
 }
 
 /*
@@ -5689,19 +5890,24 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
  * There is no such command in the grammar, but parse_utilcmd.c converts
  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
  * us schedule creation of the index at the appropriate time during ALTER.
+ *
+ * Return value is the address of the new index.
  */
-static void
+static ObjectAddress
 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                           IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 {
        bool            check_rights;
        bool            skip_build;
        bool            quiet;
-       Oid                     new_index;
+       ObjectAddress address;
 
        Assert(IsA(stmt, IndexStmt));
        Assert(!stmt->concurrent);
 
+       /* The IndexStmt has already been through transformIndexStmt */
+       Assert(stmt->transformed);
+
        /* suppress schema rights check when rebuilding existing index */
        check_rights = !is_rebuild;
        /* skip index build if phase 3 will do it or we're reusing an old one */
@@ -5709,15 +5915,13 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
        /* suppress notices when rebuilding existing index */
        quiet = is_rebuild;
 
-       /* The IndexStmt has already been through transformIndexStmt */
-
-       new_index = DefineIndex(RelationGetRelid(rel),
-                                                       stmt,
-                                                       InvalidOid, /* no predefined OID */
-                                                       true,           /* is_alter_table */
-                                                       check_rights,
-                                                       skip_build,
-                                                       quiet);
+       address = DefineIndex(RelationGetRelid(rel),
+                                                 stmt,
+                                                 InvalidOid,   /* no predefined OID */
+                                                 true, /* is_alter_table */
+                                                 check_rights,
+                                                 skip_build,
+                                                 quiet);
 
        /*
         * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
@@ -5727,17 +5931,21 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
         */
        if (OidIsValid(stmt->oldNode))
        {
-               Relation        irel = index_open(new_index, NoLock);
+               Relation        irel = index_open(address.objectId, NoLock);
 
                RelationPreserveStorage(irel->rd_node, true);
                index_close(irel, NoLock);
        }
+
+       return address;
 }
 
 /*
  * ALTER TABLE ADD CONSTRAINT USING INDEX
+ *
+ * Returns the address of the new constraint.
  */
-static void
+static ObjectAddress
 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
                                                 IndexStmt *stmt, LOCKMODE lockmode)
 {
@@ -5747,6 +5955,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
        IndexInfo  *indexInfo;
        char       *constraintName;
        char            constraintType;
+       ObjectAddress address;
 
        Assert(IsA(stmt, IndexStmt));
        Assert(OidIsValid(index_oid));
@@ -5791,30 +6000,37 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
                constraintType = CONSTRAINT_UNIQUE;
 
        /* Create the catalog entries for the constraint */
-       index_constraint_create(rel,
-                                                       index_oid,
-                                                       indexInfo,
-                                                       constraintName,
-                                                       constraintType,
-                                                       stmt->deferrable,
-                                                       stmt->initdeferred,
-                                                       stmt->primary,
-                                                       true,           /* update pg_index */
-                                                       true,           /* remove old dependencies */
-                                                       allowSystemTableMods,
-                                                       false);         /* is_internal */
+       address = index_constraint_create(rel,
+                                                                         index_oid,
+                                                                         indexInfo,
+                                                                         constraintName,
+                                                                         constraintType,
+                                                                         stmt->deferrable,
+                                                                         stmt->initdeferred,
+                                                                         stmt->primary,
+                                                                         true,         /* update pg_index */
+                                                                         true,         /* remove old dependencies */
+                                                                         allowSystemTableMods,
+                                                                         false);       /* is_internal */
 
        index_close(indexRel, NoLock);
+
+       return address;
 }
 
 /*
  * ALTER TABLE ADD CONSTRAINT
+ *
+ * Return value is the address of the new constraint; if no constraint was
+ * added, InvalidObjectAddress is returned.
  */
-static void
+static ObjectAddress
 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                        Constraint *newConstraint, bool recurse, bool is_readd,
                                        LOCKMODE lockmode)
 {
+       ObjectAddress address = InvalidObjectAddress;
+
        Assert(IsA(newConstraint, Constraint));
 
        /*
@@ -5825,9 +6041,10 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
        switch (newConstraint->contype)
        {
                case CONSTR_CHECK:
-                       ATAddCheckConstraint(wqueue, tab, rel,
-                                                                newConstraint, recurse, false, is_readd,
-                                                                lockmode);
+                       address =
+                               ATAddCheckConstraint(wqueue, tab, rel,
+                                                                        newConstraint, recurse, false, is_readd,
+                                                                        lockmode);
                        break;
 
                case CONSTR_FOREIGN:
@@ -5858,17 +6075,22 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                                                                 RelationGetNamespace(rel),
                                                                                 NIL);
 
-                       ATAddForeignKeyConstraint(tab, rel, newConstraint, lockmode);
+                       address = ATAddForeignKeyConstraint(tab, rel, newConstraint,
+                                                                                               lockmode);
                        break;
 
                default:
                        elog(ERROR, "unrecognized constraint type: %d",
                                 (int) newConstraint->contype);
        }
+
+       return address;
 }
 
 /*
- * Add a check constraint to a single table and its children
+ * Add a check constraint to a single table and its children.  Returns the
+ * address of the constraint added to the parent relation, if one gets added,
+ * or InvalidObjectAddress otherwise.
  *
  * Subroutine for ATExecAddConstraint.
  *
@@ -5879,15 +6101,8 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
  * AddRelationNewConstraints would normally assign different names to the
  * child constraints.  To fix that, we must capture the name assigned at
  * the parent table and pass that down.
- *
- * When re-adding a previously existing constraint (during ALTER COLUMN TYPE),
- * we don't need to recurse here, because recursion will be carried out at a
- * higher level; the constraint name issue doesn't apply because the names
- * have already been assigned and are just being re-used.  We need a separate
- * "is_readd" flag for that; just setting recurse=false would result in an
- * error if there are child tables.
  */
-static void
+static ObjectAddress
 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                         Constraint *constr, bool recurse, bool recursing,
                                         bool is_readd, LOCKMODE lockmode)
@@ -5896,10 +6111,11 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
        ListCell   *lcon;
        List       *children;
        ListCell   *child;
+       ObjectAddress address = InvalidObjectAddress;
 
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
-               ATSimplePermissions(rel, ATT_TABLE);
+               ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
 
        /*
         * Call AddRelationNewConstraints to do the work, making sure it works on
@@ -5913,10 +6129,13 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
         */
        newcons = AddRelationNewConstraints(rel, NIL,
                                                                                list_make1(copyObject(constr)),
-                                                                               recursing,              /* allow_merge */
+                                                                               recursing | is_readd,   /* allow_merge */
                                                                                !recursing,             /* is_local */
                                                                                is_readd);              /* is_internal */
 
+       /* we don't expect more than one constraint here */
+       Assert(list_length(newcons) <= 1);
+
        /* Add each to-be-validated constraint to Phase 3's queue */
        foreach(lcon, newcons)
        {
@@ -5938,6 +6157,8 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                /* Save the actually assigned name if it was defaulted */
                if (constr->conname == NULL)
                        constr->conname = ccon->name;
+
+               ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
        }
 
        /* At this point we must have a locked-down name to use */
@@ -5953,15 +6174,13 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
         * incorrect value for coninhcount.
         */
        if (newcons == NIL)
-               return;
+               return address;
 
        /*
         * If adding a NO INHERIT constraint, no need to find our children.
-        * Likewise, in a re-add operation, we don't need to recurse (that will be
-        * handled at higher levels).
         */
-       if (constr->is_no_inherit || is_readd)
-               return;
+       if (constr->is_no_inherit)
+               return address;
 
        /*
         * Propagate to children as appropriate.  Unlike most other ALTER
@@ -5999,16 +6218,19 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
                heap_close(childrel, NoLock);
        }
+
+       return address;
 }
 
 /*
- * Add a foreign-key constraint to a single table
+ * Add a foreign-key constraint to a single table; return the new constraint's
+ * address.
  *
  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
  * lock on the rel, and have done appropriate validity checks for it.
  * We do permissions checks here, however.
  */
-static void
+static ObjectAddress
 ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                  Constraint *fkconstraint, LOCKMODE lockmode)
 {
@@ -6027,19 +6249,17 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
        Oid                     indexOid;
        Oid                     constrOid;
        bool            old_check_ok;
+       ObjectAddress address;
        ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
 
        /*
-        * Grab an exclusive lock on the pk table, so that someone doesn't delete
-        * rows out from under us. (Although a lesser lock would do for that
-        * purpose, we'll need exclusive lock anyway to add triggers to the pk
-        * table; trying to start with a lesser lock will just create a risk of
-        * deadlock.)
+        * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
+        * delete rows out from under us.
         */
        if (OidIsValid(fkconstraint->old_pktable_oid))
-               pkrel = heap_open(fkconstraint->old_pktable_oid, AccessExclusiveLock);
+               pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
        else
-               pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
+               pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
 
        /*
         * Validity checks (permission checks wait till we have the column
@@ -6374,6 +6594,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                                          0,            /* inhcount */
                                                                          true,         /* isnoinherit */
                                                                          false);       /* is_internal */
+       ObjectAddressSet(address, ConstraintRelationId, constrOid);
 
        /*
         * Create the triggers that will enforce the constraint.
@@ -6407,6 +6628,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
         * Close pk table, but keep lock until we've committed.
         */
        heap_close(pkrel, NoLock);
+
+       return address;
 }
 
 /*
@@ -6418,18 +6641,22 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  * Foreign keys do not inherit, so we purposely ignore the
  * recursion bit here, but we keep the API the same for when
  * other constraint types are supported.
+ *
+ * If the constraint is modified, returns its address; otherwise, return
+ * InvalidObjectAddress.
  */
-static void
+static ObjectAddress
 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
                                          bool recurse, bool recursing, LOCKMODE lockmode)
 {
+       Constraint *cmdcon;
        Relation        conrel;
        SysScanDesc scan;
        ScanKeyData key;
        HeapTuple       contuple;
        Form_pg_constraint currcon = NULL;
-       Constraint *cmdcon = NULL;
        bool            found = false;
+       ObjectAddress address;
 
        Assert(IsA(cmd->def, Constraint));
        cmdcon = (Constraint *) cmd->def;
@@ -6474,10 +6701,11 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
                HeapTuple       copyTuple;
                HeapTuple       tgtuple;
                Form_pg_constraint copy_con;
-               Form_pg_trigger copy_tg;
+               List       *otherrelids = NIL;
                ScanKeyData tgkey;
                SysScanDesc tgscan;
                Relation        tgrel;
+               ListCell   *lc;
 
                /*
                 * Now update the catalog, while we have the door open.
@@ -6510,8 +6738,16 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 
                while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
                {
+                       Form_pg_trigger copy_tg;
+
                        copyTuple = heap_copytuple(tgtuple);
                        copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
+
+                       /* Remember OIDs of other relation(s) involved in FK constraint */
+                       if (copy_tg->tgrelid != RelationGetRelid(rel))
+                               otherrelids = list_append_unique_oid(otherrelids,
+                                                                                                        copy_tg->tgrelid);
+
                        copy_tg->tgdeferrable = cmdcon->deferrable;
                        copy_tg->tginitdeferred = cmdcon->initdeferred;
                        simple_heap_update(tgrel, &copyTuple->t_self, copyTuple);
@@ -6528,14 +6764,28 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
                heap_close(tgrel, RowExclusiveLock);
 
                /*
-                * Invalidate relcache so that others see the new attributes.
+                * Invalidate relcache so that others see the new attributes.  We must
+                * inval both the named rel and any others having relevant triggers.
+                * (At present there should always be exactly one other rel, but
+                * there's no need to hard-wire such an assumption here.)
                 */
                CacheInvalidateRelcache(rel);
+               foreach(lc, otherrelids)
+               {
+                       CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
+               }
+
+               ObjectAddressSet(address, ConstraintRelationId,
+                                                HeapTupleGetOid(contuple));
        }
+       else
+               address = InvalidObjectAddress;
 
        systable_endscan(scan);
 
        heap_close(conrel, RowExclusiveLock);
+
+       return address;
 }
 
 /*
@@ -6545,8 +6795,11 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
  * there's no good way to skip recursing when handling foreign keys: there is
  * no need to lock children in that case, yet we wouldn't be able to avoid
  * doing so at that level.
+ *
+ * Return value is the address of the validated constraint.  If the constraint
+ * was already validated, InvalidObjectAddress is returned.
  */
-static void
+static ObjectAddress
 ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
                                                 bool recursing, LOCKMODE lockmode)
 {
@@ -6556,6 +6809,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
        HeapTuple       tuple;
        Form_pg_constraint con = NULL;
        bool            found = false;
+       ObjectAddress address;
 
        conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -6599,7 +6853,6 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 
                if (con->contype == CONSTRAINT_FOREIGN)
                {
-                       Oid                     conid = HeapTupleGetOid(tuple);
                        Relation        refrel;
 
                        /*
@@ -6614,7 +6867,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 
                        validateForeignKeyConstraint(constrName, rel, refrel,
                                                                                 con->conindid,
-                                                                                conid);
+                                                                                HeapTupleGetOid(tuple));
                        heap_close(refrel, NoLock);
 
                        /*
@@ -6690,11 +6943,18 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
                                                                  HeapTupleGetOid(tuple), 0);
 
                heap_freetuple(copyTuple);
+
+               ObjectAddressSet(address, ConstraintRelationId,
+                                                HeapTupleGetOid(tuple));
        }
+       else
+               address = InvalidObjectAddress; /* already validated */
 
        systable_endscan(scan);
 
        heap_close(conrel, RowExclusiveLock);
+
+       return address;
 }
 
 
@@ -7057,6 +7317,10 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
        bool            isnull;
        Snapshot        snapshot;
 
+       /* VALIDATE CONSTRAINT is a no-op for foreign tables */
+       if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+               return;
+
        constrForm = (Form_pg_constraint) GETSTRUCT(constrtup);
 
        estate = CreateExecutorState();
@@ -7399,7 +7663,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
 
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
-               ATSimplePermissions(rel, ATT_TABLE);
+               ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
 
        conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -7585,7 +7849,7 @@ ATPrepAlterColumnType(List **wqueue,
        char       *colName = cmd->name;
        ColumnDef  *def = (ColumnDef *) cmd->def;
        TypeName   *typeName = def->typeName;
-       Node       *transform = def->raw_default;
+       Node       *transform = def->cooked_default;
        HeapTuple       tuple;
        Form_pg_attribute attTup;
        AttrNumber      attnum;
@@ -7644,34 +7908,14 @@ ATPrepAlterColumnType(List **wqueue,
        {
                /*
                 * Set up an expression to transform the old data value to the new
-                * type. If a USING option was given, transform and use that
-                * expression, else just take the old value and try to coerce it.  We
-                * do this first so that type incompatibility can be detected before
-                * we waste effort, and because we need the expression to be parsed
-                * against the original table row type.
+                * type. If a USING option was given, use the expression as
+                * transformed by transformAlterTableStmt, else just take the old
+                * value and try to coerce it.  We do this first so that type
+                * incompatibility can be detected before we waste effort, and because
+                * we need the expression to be parsed against the original table row
+                * type.
                 */
-               if (transform)
-               {
-                       RangeTblEntry *rte;
-
-                       /* Expression must be able to access vars of old table */
-                       rte = addRangeTableEntryForRelation(pstate,
-                                                                                               rel,
-                                                                                               NULL,
-                                                                                               false,
-                                                                                               true);
-                       addRTEtoQuery(pstate, rte, false, true, true);
-
-                       transform = transformExpr(pstate, transform,
-                                                                         EXPR_KIND_ALTER_COL_TRANSFORM);
-
-                       /* It can't return a set */
-                       if (expression_returns_set(transform))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                         errmsg("transform expression must not return a set")));
-               }
-               else
+               if (!transform)
                {
                        transform = (Node *) makeVar(1, attnum,
                                                                                 attTup->atttypid, attTup->atttypmod,
@@ -7686,11 +7930,26 @@ ATPrepAlterColumnType(List **wqueue,
                                                                                  COERCE_IMPLICIT_CAST,
                                                                                  -1);
                if (transform == NULL)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                         errmsg("column \"%s\" cannot be cast automatically to type %s",
-                                        colName, format_type_be(targettype)),
-                                        errhint("Specify a USING expression to perform the conversion.")));
+               {
+                       /* error text depends on whether USING was specified or not */
+                       if (def->cooked_default != NULL)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("result of USING clause for column \"%s\""
+                                                               " cannot be cast automatically to type %s",
+                                                               colName, format_type_be(targettype)),
+                                                errhint("You might need to add an explicit cast.")));
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("column \"%s\" cannot be cast automatically to type %s",
+                                                               colName, format_type_be(targettype)),
+                               /* translator: USING is SQL, don't translate it */
+                                          errhint("You might need to specify \"USING %s::%s\".",
+                                                          quote_identifier(colName),
+                                                          format_type_with_typemod(targettype,
+                                                                                                               targettypmod))));
+               }
 
                /* Fix collations after all else */
                assign_expr_collations(pstate, transform);
@@ -7772,7 +8031,7 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
                {
                        CoerceToDomain *d = (CoerceToDomain *) expr;
 
-                       if (GetDomainConstraints(d->resulttype) != NIL)
+                       if (DomainHasConstraints(d->resulttype))
                                return true;
                        expr = (Node *) d->arg;
                }
@@ -7781,7 +8040,12 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
        }
 }
 
-static void
+/*
+ * ALTER COLUMN .. SET DATA TYPE
+ *
+ * Return the address of the modified column.
+ */
+static ObjectAddress
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                          AlterTableCmd *cmd, LOCKMODE lockmode)
 {
@@ -7802,6 +8066,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
        ScanKeyData key[3];
        SysScanDesc scan;
        HeapTuple       depTup;
+       ObjectAddress address;
 
        attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
 
@@ -7946,7 +8211,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                if (!list_member_oid(tab->changedConstraintOids,
                                                                         foundObject.objectId))
                                {
-                                       char       *defstring = pg_get_constraintdef_string(foundObject.objectId);
+                                       char       *defstring = pg_get_constraintdef_command(foundObject.objectId);
 
                                        /*
                                         * Put NORMAL dependencies at the front of the list and
@@ -8014,8 +8279,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                 * specified in the policy's USING or WITH CHECK qual
                                 * expressions.  It might be possible to rewrite and recheck
                                 * the policy expression, but punt for now.  It's certainly
-                                * easy enough to remove and recreate the policy; still,
-                                * FIXME someday.
+                                * easy enough to remove and recreate the policy; still, FIXME
+                                * someday.
                                 */
                                ereport(ERROR,
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -8173,11 +8438,19 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                StoreAttrDefault(rel, attnum, defaultexpr, true);
        }
 
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
+
        /* Cleanup */
        heap_freetuple(heapTup);
+
+       return address;
 }
 
-static void
+/*
+ * Returns the address of the modified column
+ */
+static ObjectAddress
 ATExecAlterColumnGenericOptions(Relation rel,
                                                                const char *colName,
                                                                List *options,
@@ -8196,9 +8469,11 @@ ATExecAlterColumnGenericOptions(Relation rel,
        Datum           datum;
        Form_pg_foreign_table fttableform;
        Form_pg_attribute atttableform;
+       AttrNumber      attnum;
+       ObjectAddress address;
 
        if (options == NIL)
-               return;
+               return InvalidObjectAddress;
 
        /* First, determine FDW validator associated to the foreign table. */
        ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
@@ -8225,7 +8500,8 @@ ATExecAlterColumnGenericOptions(Relation rel,
 
        /* Prevent them from altering a system attribute */
        atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
-       if (atttableform->attnum <= 0)
+       attnum = atttableform->attnum;
+       if (attnum <= 0)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot alter system column \"%s\"", colName)));
@@ -8268,12 +8544,16 @@ ATExecAlterColumnGenericOptions(Relation rel,
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel),
                                                          atttableform->attnum);
+       ObjectAddressSubSet(address, RelationRelationId,
+                                               RelationGetRelid(rel), attnum);
 
        ReleaseSysCache(tuple);
 
        heap_close(attrel, RowExclusiveLock);
 
        heap_freetuple(newtuple);
+
+       return address;
 }
 
 /*
@@ -8306,10 +8586,30 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
                        def_item, tab->changedConstraintDefs)
        {
                Oid                     oldId = lfirst_oid(oid_item);
+               HeapTuple       tup;
+               Form_pg_constraint con;
                Oid                     relid;
                Oid                     confrelid;
+               bool            conislocal;
+
+               tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
+               if (!HeapTupleIsValid(tup))             /* should not happen */
+                       elog(ERROR, "cache lookup failed for constraint %u", oldId);
+               con = (Form_pg_constraint) GETSTRUCT(tup);
+               relid = con->conrelid;
+               confrelid = con->confrelid;
+               conislocal = con->conislocal;
+               ReleaseSysCache(tup);
+
+               /*
+                * If the constraint is inherited (only), we don't want to inject a
+                * new definition here; it'll get recreated when ATAddCheckConstraint
+                * recurses from adding the parent table's constraint.  But we had to
+                * carry the info this far so that we can drop the constraint below.
+                */
+               if (!conislocal)
+                       continue;
 
-               get_constraint_relation_oids(oldId, &relid, &confrelid);
                ATPostAlterTypeParse(oldId, relid, confrelid,
                                                         (char *) lfirst(def_item),
                                                         wqueue, lockmode, tab->rewrite);
@@ -8407,74 +8707,128 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
                Node       *stm = (Node *) lfirst(list_item);
                AlteredTableInfo *tab;
 
-               switch (nodeTag(stm))
+               tab = ATGetQueueEntry(wqueue, rel);
+
+               if (IsA(stm, IndexStmt))
                {
-                       case T_IndexStmt:
+                       IndexStmt  *stmt = (IndexStmt *) stm;
+                       AlterTableCmd *newcmd;
+
+                       if (!rewrite)
+                               TryReuseIndex(oldId, stmt);
+                       /* keep the index's comment */
+                       stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
+
+                       newcmd = makeNode(AlterTableCmd);
+                       newcmd->subtype = AT_ReAddIndex;
+                       newcmd->def = (Node *) stmt;
+                       tab->subcmds[AT_PASS_OLD_INDEX] =
+                               lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+               }
+               else if (IsA(stm, AlterTableStmt))
+               {
+                       AlterTableStmt *stmt = (AlterTableStmt *) stm;
+                       ListCell   *lcmd;
+
+                       foreach(lcmd, stmt->cmds)
+                       {
+                               AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+
+                               if (cmd->subtype == AT_AddIndex)
                                {
-                                       IndexStmt  *stmt = (IndexStmt *) stm;
-                                       AlterTableCmd *newcmd;
+                                       IndexStmt  *indstmt;
+                                       Oid                     indoid;
+
+                                       Assert(IsA(cmd->def, IndexStmt));
+
+                                       indstmt = (IndexStmt *) cmd->def;
+                                       indoid = get_constraint_index(oldId);
 
                                        if (!rewrite)
-                                               TryReuseIndex(oldId, stmt);
+                                               TryReuseIndex(indoid, indstmt);
+                                       /* keep any comment on the index */
+                                       indstmt->idxcomment = GetComment(indoid,
+                                                                                                        RelationRelationId, 0);
 
-                                       tab = ATGetQueueEntry(wqueue, rel);
-                                       newcmd = makeNode(AlterTableCmd);
-                                       newcmd->subtype = AT_ReAddIndex;
-                                       newcmd->def = (Node *) stmt;
+                                       cmd->subtype = AT_ReAddIndex;
                                        tab->subcmds[AT_PASS_OLD_INDEX] =
-                                               lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
-                                       break;
+                                               lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+
+                                       /* recreate any comment on the constraint */
+                                       RebuildConstraintComment(tab,
+                                                                                        AT_PASS_OLD_INDEX,
+                                                                                        oldId,
+                                                                                        rel, indstmt->idxname);
                                }
-                       case T_AlterTableStmt:
+                               else if (cmd->subtype == AT_AddConstraint)
                                {
-                                       AlterTableStmt *stmt = (AlterTableStmt *) stm;
-                                       ListCell   *lcmd;
-
-                                       tab = ATGetQueueEntry(wqueue, rel);
-                                       foreach(lcmd, stmt->cmds)
-                                       {
-                                               AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
-                                               Constraint *con;
-
-                                               switch (cmd->subtype)
-                                               {
-                                                       case AT_AddIndex:
-                                                               Assert(IsA(cmd->def, IndexStmt));
-                                                               if (!rewrite)
-                                                                       TryReuseIndex(get_constraint_index(oldId),
-                                                                                                 (IndexStmt *) cmd->def);
-                                                               cmd->subtype = AT_ReAddIndex;
-                                                               tab->subcmds[AT_PASS_OLD_INDEX] =
-                                                                       lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
-                                                               break;
-                                                       case AT_AddConstraint:
-                                                               Assert(IsA(cmd->def, Constraint));
-                                                               con = (Constraint *) cmd->def;
-                                                               con->old_pktable_oid = refRelId;
-                                                               /* rewriting neither side of a FK */
-                                                               if (con->contype == CONSTR_FOREIGN &&
-                                                                       !rewrite && tab->rewrite == 0)
-                                                                       TryReuseForeignKey(oldId, con);
-                                                               cmd->subtype = AT_ReAddConstraint;
-                                                               tab->subcmds[AT_PASS_OLD_CONSTR] =
-                                                                       lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
-                                                               break;
-                                                       default:
-                                                               elog(ERROR, "unexpected statement type: %d",
-                                                                        (int) cmd->subtype);
-                                               }
-                                       }
-                                       break;
+                                       Constraint *con;
+
+                                       Assert(IsA(cmd->def, Constraint));
+
+                                       con = (Constraint *) cmd->def;
+                                       con->old_pktable_oid = refRelId;
+                                       /* rewriting neither side of a FK */
+                                       if (con->contype == CONSTR_FOREIGN &&
+                                               !rewrite && tab->rewrite == 0)
+                                               TryReuseForeignKey(oldId, con);
+                                       cmd->subtype = AT_ReAddConstraint;
+                                       tab->subcmds[AT_PASS_OLD_CONSTR] =
+                                               lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+
+                                       /* recreate any comment on the constraint */
+                                       RebuildConstraintComment(tab,
+                                                                                        AT_PASS_OLD_CONSTR,
+                                                                                        oldId,
+                                                                                        rel, con->conname);
                                }
-                       default:
-                               elog(ERROR, "unexpected statement type: %d",
-                                        (int) nodeTag(stm));
+                               else
+                                       elog(ERROR, "unexpected statement subtype: %d",
+                                                (int) cmd->subtype);
+                       }
                }
+               else
+                       elog(ERROR, "unexpected statement type: %d",
+                                (int) nodeTag(stm));
        }
 
        relation_close(rel, NoLock);
 }
 
+/*
+ * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
+ * a constraint that is being re-added.
+ */
+static void
+RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
+                                                Relation rel, char *conname)
+{
+       CommentStmt *cmd;
+       char       *comment_str;
+       AlterTableCmd *newcmd;
+
+       /* Look for comment for object wanted, and leave if none */
+       comment_str = GetComment(objid, ConstraintRelationId, 0);
+       if (comment_str == NULL)
+               return;
+
+       /* Build node CommentStmt */
+       cmd = makeNode(CommentStmt);
+       cmd->objtype = OBJECT_TABCONSTRAINT;
+       cmd->objname = list_make3(
+                                  makeString(get_namespace_name(RelationGetNamespace(rel))),
+                                                         makeString(RelationGetRelationName(rel)),
+                                                         makeString(conname));
+       cmd->objargs = NIL;
+       cmd->comment = comment_str;
+
+       /* Append it to list of commands */
+       newcmd = makeNode(AlterTableCmd);
+       newcmd->subtype = AT_ReAddComment;
+       newcmd->def = (Node *) cmd;
+       tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
+}
+
 /*
  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
  * for the real analysis, then mutates the IndexStmt based on that verdict.
@@ -8731,8 +9085,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
                 * Also change the ownership of the table's row type, if it has one
                 */
                if (tuple_class->relkind != RELKIND_INDEX)
-                       AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId,
-                                                        tuple_class->relkind == RELKIND_COMPOSITE_TYPE);
+                       AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
 
                /*
                 * If we are operating on a table or materialized view, also change
@@ -8914,11 +9267,14 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lock
  * ALTER TABLE CLUSTER ON
  *
  * The only thing we have to do is to change the indisclustered bits.
+ *
+ * Return the address of the new clustering index.
  */
-static void
+static ObjectAddress
 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
 {
        Oid                     indexOid;
+       ObjectAddress address;
 
        indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
 
@@ -8933,6 +9289,11 @@ ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
 
        /* And do the work */
        mark_index_clustered(rel, indexOid, false);
+
+       ObjectAddressSet(address,
+                                        RelationRelationId, indexOid);
+
+       return address;
 }
 
 /*
@@ -9302,6 +9663,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
                if (smgrexists(rel->rd_smgr, forkNum))
                {
                        smgrcreate(dstrel, forkNum, false);
+
+                       /*
+                        * WAL log creation if the relation is persistent, or this is the
+                        * init fork of an unlogged relation.
+                        */
+                       if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
+                               (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
+                                forkNum == INIT_FORKNUM))
+                               log_smgrcreate(&newrnode, forkNum);
                        copy_relation_data(rel->rd_smgr, dstrel, forkNum,
                                                           rel->rd_rel->relpersistence);
                }
@@ -9361,7 +9731,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
        HeapTuple       tuple;
        Oid                     orig_tablespaceoid;
        Oid                     new_tablespaceoid;
-       List       *role_oids = roleNamesToIds(stmt->roles);
+       List       *role_oids = roleSpecsToIds(stmt->roles);
 
        /* Ensure we were not asked to move something we can't */
        if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
@@ -9471,9 +9841,9 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
                        !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
                        ereport(ERROR,
                                        (errcode(ERRCODE_OBJECT_IN_USE),
-                          errmsg("aborting because lock on relation \"%s\".\"%s\" is not available",
-                                         get_namespace_name(relForm->relnamespace),
-                                         NameStr(relForm->relname))));
+                                        errmsg("aborting because lock on relation \"%s.%s\" is not available",
+                                                       get_namespace_name(relForm->relnamespace),
+                                                       NameStr(relForm->relname))));
                else
                        LockRelationOid(relOid, AccessExclusiveLock);
 
@@ -9502,7 +9872,10 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
 
                cmds = lappend(cmds, cmd);
 
+               EventTriggerAlterTableStart((Node *) stmt);
+               /* OID is set by AlterTableInternal */
                AlterTableInternal(lfirst_oid(l), cmds, false);
+               EventTriggerAlterTableEnd();
        }
 
        return new_tablespaceoid;
@@ -9518,6 +9891,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
        char       *buf;
        Page            page;
        bool            use_wal;
+       bool            copying_initfork;
        BlockNumber nblocks;
        BlockNumber blkno;
 
@@ -9530,11 +9904,20 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
        buf = (char *) palloc(BLCKSZ);
        page = (Page) buf;
 
+       /*
+        * The init fork for an unlogged relation in many respects has to be
+        * treated the same as normal relation, changes need to be WAL logged and
+        * it needs to be synced to disk.
+        */
+       copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
+               forkNum == INIT_FORKNUM;
+
        /*
         * We need to log the copied data in WAL iff WAL archiving/streaming is
         * enabled AND it's a permanent relation.
         */
-       use_wal = XLogIsNeeded() && relpersistence == RELPERSISTENCE_PERMANENT;
+       use_wal = XLogIsNeeded() &&
+               (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
 
        nblocks = smgrnblocks(src, forkNum);
 
@@ -9589,7 +9972,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
         * wouldn't replay our earlier WAL entries. If we do not fsync those pages
         * here, they might still not be on disk when the crash occurs.
         */
-       if (relpersistence == RELPERSISTENCE_PERMANENT)
+       if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
                smgrimmedsync(dst, forkNum);
 }
 
@@ -9633,7 +10016,10 @@ ATPrepAddInherit(Relation child_rel)
                                 errmsg("cannot change inheritance of typed table")));
 }
 
-static void
+/*
+ * Return the address of the new parent relation.
+ */
+static ObjectAddress
 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 {
        Relation        parent_rel,
@@ -9643,6 +10029,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
        HeapTuple       inheritsTuple;
        int32           inhseqno;
        List       *children;
+       ObjectAddress address;
 
        /*
         * A self-exclusive lock is needed here.  See the similar case in
@@ -9654,7 +10041,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
         * Must be owner of both parent and child -- child was checked by
         * ATSimplePermissions call in ATPrepCmd
         */
-       ATSimplePermissions(parent_rel, ATT_TABLE);
+       ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
 
        /* Permanent rels cannot inherit from temporary ones */
        if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
@@ -9757,11 +10144,16 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
                                                         inhseqno + 1,
                                                         catalogRelation);
 
+       ObjectAddressSet(address, RelationRelationId,
+                                        RelationGetRelid(parent_rel));
+
        /* Now we're done with pg_inherits */
        heap_close(catalogRelation, RowExclusiveLock);
 
        /* keep our lock on the parent relation until commit */
        heap_close(parent_rel, NoLock);
+
+       return address;
 }
 
 /*
@@ -10029,11 +10421,14 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  *
  * coninhcount and conislocal for inherited constraints are adjusted in
  * exactly the same way.
+ *
+ * Return value is the address of the relation that is no longer parent.
  */
-static void
+static ObjectAddress
 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
 {
        Relation        parent_rel;
+       Oid                     parent_oid;
        Relation        catalogRelation;
        SysScanDesc scan;
        ScanKeyData key[3];
@@ -10042,6 +10437,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
                                constraintTuple;
        List       *connames;
        bool            found = false;
+       ObjectAddress address;
 
        /*
         * AccessShareLock on the parent is probably enough, seeing that DROP
@@ -10202,6 +10598,8 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
                }
        }
 
+       parent_oid = RelationGetRelid(parent_rel);
+
        systable_endscan(scan);
        heap_close(catalogRelation, RowExclusiveLock);
 
@@ -10220,6 +10618,10 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
 
        /* keep our lock on the parent relation until commit */
        heap_close(parent_rel, NoLock);
+
+       ObjectAddressSet(address, RelationRelationId, parent_oid);
+
+       return address;
 }
 
 /*
@@ -10277,8 +10679,10 @@ drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid)
  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
  * subject table must not have inheritance parents.  These restrictions ensure
  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
+ *
+ * The address of the type is returned.
  */
-static void
+static ObjectAddress
 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 {
        Oid                     relid = RelationGetRelid(rel);
@@ -10407,6 +10811,8 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
        heap_close(relationRelation, RowExclusiveLock);
 
        ReleaseSysCache(typetuple);
+
+       return typeobj;
 }
 
 /*
@@ -10667,9 +11073,9 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 static void
 ATExecEnableRowSecurity(Relation rel)
 {
-       Relation                pg_class;
-       Oid                             relid;
-       HeapTuple               tuple;
+       Relation        pg_class;
+       Oid                     relid;
+       HeapTuple       tuple;
 
        relid = RelationGetRelid(rel);
 
@@ -10693,9 +11099,9 @@ ATExecEnableRowSecurity(Relation rel)
 static void
 ATExecDisableRowSecurity(Relation rel)
 {
-       Relation                pg_class;
-       Oid                             relid;
-       HeapTuple               tuple;
+       Relation        pg_class;
+       Oid                     relid;
+       HeapTuple       tuple;
 
        relid = RelationGetRelid(rel);
 
@@ -10717,6 +11123,35 @@ ATExecDisableRowSecurity(Relation rel)
        heap_freetuple(tuple);
 }
 
+/*
+ * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
+ */
+static void
+ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
+{
+       Relation        pg_class;
+       Oid                     relid;
+       HeapTuple       tuple;
+
+       relid = RelationGetRelid(rel);
+
+       pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
+
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup failed for relation %u", relid);
+
+       ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
+       simple_heap_update(pg_class, &tuple->t_self, tuple);
+
+       /* keep catalog indexes current */
+       CatalogUpdateIndexes(pg_class, tuple);
+
+       heap_close(pg_class, RowExclusiveLock);
+       heap_freetuple(tuple);
+}
+
 /*
  * ALTER FOREIGN TABLE <name> OPTIONS (...)
  */
@@ -10818,10 +11253,8 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
                case RELPERSISTENCE_TEMP:
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                        errmsg("cannot change logged status of table %s",
+                                        errmsg("cannot change logged status of table \"%s\" because it is temporary",
                                                        RelationGetRelationName(rel)),
-                                        errdetail("Table %s is temporary.",
-                                                          RelationGetRelationName(rel)),
                                         errtable(rel)));
                        break;
                case RELPERSISTENCE_PERMANENT:
@@ -10838,7 +11271,7 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
 
        /*
         * Check existing foreign key constraints to preserve the invariant that
-        * no permanent tables cannot reference unlogged ones.  Self-referencing
+        * permanent tables cannot reference unlogged ones.  Self-referencing
         * foreign keys can safely be ignored.
         */
        pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
@@ -10879,9 +11312,7 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
                                if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                                errmsg("cannot change status of table %s to logged",
-                                                               RelationGetRelationName(rel)),
-                                                 errdetail("Table %s references unlogged table %s.",
+                                                        errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
                                                                        RelationGetRelationName(rel),
                                                                        RelationGetRelationName(foreignrel)),
                                                         errtableconstraint(rel, NameStr(con->conname))));
@@ -10891,11 +11322,9 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
                                if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                          errmsg("cannot change status of table %s to unlogged",
-                                                         RelationGetRelationName(rel)),
-                                         errdetail("Logged table %s is referenced by table %s.",
-                                                               RelationGetRelationName(foreignrel),
-                                                               RelationGetRelationName(rel)),
+                                                        errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
+                                                                       RelationGetRelationName(rel),
+                                                                       RelationGetRelationName(foreignrel)),
                                                         errtableconstraint(rel, NameStr(con->conname))));
                        }
 
@@ -10913,8 +11342,8 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
 /*
  * Execute ALTER TABLE SET SCHEMA
  */
-Oid
-AlterTableNamespace(AlterObjectSchemaStmt *stmt)
+ObjectAddress
+AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
 {
        Relation        rel;
        Oid                     relid;
@@ -10922,6 +11351,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt)
        Oid                     nspOid;
        RangeVar   *newrv;
        ObjectAddresses *objsMoved;
+       ObjectAddress myself;
 
        relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
                                                                         stmt->missing_ok, false,
@@ -10933,7 +11363,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt)
                ereport(NOTICE,
                                (errmsg("relation \"%s\" does not exist, skipping",
                                                stmt->relation->relname)));
-               return InvalidOid;
+               return InvalidObjectAddress;
        }
 
        rel = relation_open(relid, NoLock);
@@ -10960,16 +11390,21 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt)
        nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
 
        /* common checks on switching namespaces */
-       CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid);
+       CheckSetNamespace(oldNspOid, nspOid);
 
        objsMoved = new_object_addresses();
        AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
        free_object_addresses(objsMoved);
 
+       ObjectAddressSet(myself, RelationRelationId, relid);
+
+       if (oldschema)
+               *oldschema = oldNspOid;
+
        /* close rel, but keep lock until commit */
        relation_close(rel, NoLock);
 
-       return relid;
+       return myself;
 }
 
 /*
@@ -11023,6 +11458,7 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
        HeapTuple       classTup;
        Form_pg_class classForm;
        ObjectAddress thisobj;
+       bool            already_done = false;
 
        classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
        if (!HeapTupleIsValid(classTup))
@@ -11036,9 +11472,12 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
        thisobj.objectSubId = 0;
 
        /*
-        * Do nothing when there's nothing to do.
+        * If the object has already been moved, don't move it again.  If it's
+        * already in the right place, don't move it, but still fire the object
+        * access hook.
         */
-       if (!object_address_present(&thisobj, objsMoved))
+       already_done = object_address_present(&thisobj, objsMoved);
+       if (!already_done && oldNspOid != newNspOid)
        {
                /* check for duplicate name (more friendly than unique-index failure) */
                if (get_relname_relid(NameStr(classForm->relname),
@@ -11064,7 +11503,9 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
                                                                newNspOid) != 1)
                        elog(ERROR, "failed to change schema dependency for relation \"%s\"",
                                 NameStr(classForm->relname));
-
+       }
+       if (!already_done)
+       {
                add_exact_object_address(&thisobj, objsMoved);
 
                InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);