]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/tablecmds.c
Update copyright for 2016
[postgresql] / src / backend / commands / tablecmds.c
index 1aa1ad91277371649d7abba27694c3c710626a7d..0b4a3346316417f1f0174c749af8aa337212e8c0 100644 (file)
@@ -3,7 +3,7 @@
  * tablecmds.c
  *       Commands for creating and altering table structures and settings
  *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 
 #include "access/genam.h"
 #include "access/heapam.h"
-#include "access/heapam_xlog.h"
 #include "access/multixact.h"
 #include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
+#include "access/xlog.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #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"
 #include "commands/defrem.h"
+#include "commands/event_trigger.h"
+#include "commands/policy.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
-#include "common/relpath.h"
+#include "commands/user.h"
 #include "executor/executor.h"
 #include "foreign/foreign.h"
 #include "miscadmin.h"
@@ -69,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"
@@ -84,6 +88,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
@@ -151,8 +156,10 @@ typedef struct AlteredTableInfo
        List       *constraints;        /* List of NewConstraint */
        List       *newvals;            /* List of NewColumnValue */
        bool            new_notnull;    /* T if we added new NOT NULL constraints */
-       bool            rewrite;                /* T if a rewrite is forced */
+       int                     rewrite;                /* Reason for forced rewrite, if any */
        Oid                     newTableSpace;  /* new tablespace; 0 means no change */
+       bool            chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
+       char            newrelpersistence;              /* if above is true */
        /* Objects to rebuild after completing ALTER TYPE operations */
        List       *changedConstraintOids;      /* OIDs of constraints to rebuild */
        List       *changedConstraintDefs;      /* string definitions of same */
@@ -276,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,
-                                                bool recurse, bool recursing, LOCKMODE lockmode);
-static void ATExecValidateConstraint(Relation rel, char *constrName,
+static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
+                                         bool recurse, bool recursing, LOCKMODE lockmode);
+static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
                                                 bool recurse, bool recursing, LOCKMODE lockmode);
 static int transformColumnNameList(Oid relId, List *colList,
                                                int16 *attnums, Oid *atttypids);
@@ -296,15 +303,18 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup);
 static void validateForeignKeyConstraint(char *conname,
                                                         Relation rel, Relation pkrel,
                                                         Oid pkindOid, Oid constraintOid);
-static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
+static void createForeignKeyTriggers(Relation rel, Oid refRelOid,
+                                                Constraint *fkconstraint,
                                                 Oid constraintOid, Oid indexOid);
-static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
+static void ATController(AlterTableStmt *parsetree,
+                        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(List **wqueue, LOCKMODE lockmode);
+static void ATRewriteTables(AlterTableStmt *parsetree,
+                               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);
@@ -316,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,
@@ -368,21 +380,27 @@ 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, char *cmd,
-                                        List **wqueue, LOCKMODE lockmode, bool rewrite);
+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, LOCKMODE lockmode);
+static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
+                               LOCKMODE lockmode);
 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
+static bool ATPrepChangePersistence(Relation rel, bool toLogged);
 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
                                        char *tablespacename, LOCKMODE lockmode);
 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
@@ -394,13 +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);
@@ -420,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;
@@ -450,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
@@ -465,10 +489,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("ON COMMIT can only be used on temporary tables")));
-       if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
-               ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("constraints are not supported on foreign tables")));
 
        /*
         * Look up the namespace in which we are supposed to create the relation,
@@ -532,7 +552,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
        reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
                                                                         true, false);
 
-       (void) heap_reloptions(relkind, reloptions, true);
+       if (relkind == RELKIND_VIEW)
+               (void) view_reloptions(reloptions, true);
+       else
+               (void) heap_reloptions(relkind, reloptions, true);
 
        if (stmt->ofTypename)
        {
@@ -556,15 +579,22 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                                                         &inheritOids, &old_constraints, &parentOidCount);
 
        /*
-        * Create a tuple descriptor from the relation schema.  Note that this
+        * Create a tuple descriptor from the relation schema.  Note that this
         * deals with column names, types, and NOT NULL constraints, but not
         * default values or CHECK constraints; we handle those below.
         */
        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 ||
-                                                                               relkind == RELKIND_FOREIGN_TABLE));
+                                                                          (relkind == RELKIND_RELATION));
        descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
 
        /*
@@ -607,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;
@@ -644,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);
@@ -656,7 +688,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
        CommandCounterIncrement();
 
        /*
-        * Open the new relation and acquire exclusive lock on it.      This isn't
+        * Open the new relation and acquire exclusive lock on it.  This isn't
         * really necessary for locking out other backends (since they can't see
         * the new rel anyway until we commit), but it keeps the lock manager from
         * complaining about deadlock risks.
@@ -676,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;
 }
 
 /*
@@ -690,10 +724,28 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
  * non-existent relation
  */
 static void
-DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok)
+DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
 {
        const struct dropmsgstrings *rentry;
 
+       if (rel->schemaname != NULL &&
+               !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
+       {
+               if (!missing_ok)
+               {
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_SCHEMA),
+                                  errmsg("schema \"%s\" does not exist", rel->schemaname)));
+               }
+               else
+               {
+                       ereport(NOTICE,
+                                       (errmsg("schema \"%s\" does not exist, skipping",
+                                                       rel->schemaname)));
+               }
+               return;
+       }
+
        for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
        {
                if (rentry->kind == rightkind)
@@ -702,11 +754,11 @@ DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok)
                        {
                                ereport(ERROR,
                                                (errcode(rentry->nonexistent_code),
-                                                errmsg(rentry->nonexistent_msg, relname)));
+                                                errmsg(rentry->nonexistent_msg, rel->relname)));
                        }
                        else
                        {
-                               ereport(NOTICE, (errmsg(rentry->skipping_msg, relname)));
+                               ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
                                break;
                        }
                }
@@ -845,7 +897,7 @@ RemoveRelations(DropStmt *drop)
                /* Not there? */
                if (!OidIsValid(relOid))
                {
-                       DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok);
+                       DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
                        continue;
                }
 
@@ -1003,10 +1055,10 @@ ExecuteTruncate(TruncateStmt *stmt)
        }
 
        /*
-        * In CASCADE mode, suck in all referencing relations as well.  This
+        * In CASCADE mode, suck in all referencing relations as well.  This
         * requires multiple iterations to find indirectly-dependent relations. At
         * each phase, we need to exclusive-lock new rels before looking for their
-        * dependencies, else we might miss something.  Also, we check each rel as
+        * dependencies, else we might miss something.  Also, we check each rel as
         * soon as we open it, to avoid a faux pas such as holding lock for a long
         * time on a rel we have no permissions for.
         */
@@ -1164,7 +1216,8 @@ ExecuteTruncate(TruncateStmt *stmt)
                         * as the relfilenode value. The old storage file is scheduled for
                         * deletion at commit.
                         */
-                       RelationSetNewRelfilenode(rel, RecentXmin, minmulti);
+                       RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
+                                                                         RecentXmin, minmulti);
                        if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
                                heap_create_init_fork(rel);
 
@@ -1177,7 +1230,8 @@ ExecuteTruncate(TruncateStmt *stmt)
                        if (OidIsValid(toast_relid))
                        {
                                rel = relation_open(toast_relid, AccessExclusiveLock);
-                               RelationSetNewRelfilenode(rel, RecentXmin, minmulti);
+                               RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
+                                                                                 RecentXmin, minmulti);
                                if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
                                        heap_create_init_fork(rel);
                                heap_close(rel, NoLock);
@@ -1186,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);
        }
 
        /*
@@ -1227,7 +1283,7 @@ ExecuteTruncate(TruncateStmt *stmt)
 }
 
 /*
- * Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
+ * Check that a given rel is safe to truncate.  Subroutine for ExecuteTruncate
  */
 static void
 truncate_check_rel(Relation rel)
@@ -1463,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 &&
@@ -1556,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,
@@ -1655,7 +1714,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
 
                /*
                 * Now copy the CHECK constraints of this parent, adjusting attnos
-                * using the completed newattno[] map.  Identically named constraints
+                * using the completed newattno[] map.  Identically named constraints
                 * are merged if possible, else we throw error.
                 */
                if (constr && constr->num_check > 0)
@@ -1700,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;
@@ -1715,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);
        }
@@ -1729,12 +1789,16 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
         */
        if (inhSchema != NIL)
        {
+               int                     schema_attno = 0;
+
                foreach(entry, schema)
                {
                        ColumnDef  *newdef = lfirst(entry);
                        char       *attributeName = newdef->colname;
                        int                     exist_attno;
 
+                       schema_attno++;
+
                        /*
                         * Does it conflict with some previously inherited column?
                         */
@@ -1753,9 +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.
                                 */
-                               ereport(NOTICE,
-                                  (errmsg("merging column \"%s\" with inherited definition",
-                                                  attributeName)));
+                               if (exist_attno == schema_attno)
+                                       ereport(NOTICE,
+                                       (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.")));
                                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
                                typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
                                typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
@@ -1765,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)
@@ -2114,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,
@@ -2128,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
@@ -2224,7 +2297,7 @@ renameatt_internal(Oid myrelid,
                                                oldattname)));
 
        /*
-        * if the attribute is inherited, forbid the renaming.  if this is a
+        * if the attribute is inherited, forbid the renaming.  if this is a
         * top-level call to renameatt(), then expected_parents will be 0, so the
         * effect of this code will be to prohibit the renaming if the attribute
         * is inherited at all.  if this is a recursive call to renameatt(),
@@ -2239,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);
@@ -2256,6 +2329,8 @@ renameatt_internal(Oid myrelid,
        heap_close(attrelation, RowExclusiveLock);
 
        relation_close(targetrelation, NoLock);         /* close rel but keep lock */
+
+       return attnum;
 }
 
 /*
@@ -2277,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,
@@ -2295,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,
@@ -2327,6 +2407,7 @@ rename_constraint_internal(Oid myrelid,
        Oid                     constraintOid;
        HeapTuple       tuple;
        Form_pg_constraint con;
+       ObjectAddress address;
 
        AssertArg(!myrelid || !mytypid);
 
@@ -2402,21 +2483,23 @@ 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;
        Oid                     typid = InvalidOid;
 
-       if (stmt->relationType == OBJECT_DOMAIN)
+       if (stmt->renameType == OBJECT_DOMCONSTRAINT)
        {
                Relation        rel;
                HeapTuple       tup;
@@ -2434,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
@@ -2450,16 +2540,19 @@ RenameConstraint(RenameStmt *stmt)
 }
 
 /*
- * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
+ * 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 or view,
-        * which we will NOT release until end of transaction.
+        * Grab an exclusive lock on the target table, index, sequence, view,
+        * materialized view, or foreign table, which we will NOT release until
+        * end of transaction.
         *
         * Lock level used here should match RenameRelationInternal, to avoid lock
         * escalation.
@@ -2474,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;
 }
 
 /*
@@ -2502,8 +2597,9 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
        Oid                     namespaceId;
 
        /*
-        * Grab an exclusive lock on the target table, index, sequence or view,
-        * which we will NOT release until end of transaction.
+        * Grab an exclusive lock on the target table, index, sequence, view,
+        * materialized view, or foreign table, which we will NOT release until
+        * end of transaction.
         */
        targetrelation = relation_open(myrelid, AccessExclusiveLock);
        namespaceId = RelationGetNamespace(targetrelation);
@@ -2525,7 +2621,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
                                                newrelname)));
 
        /*
-        * Update pg_class tuple with new relname.      (Scribbling on reltup is OK
+        * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
         * because it's a copy...)
         */
        namestrcpy(&(relform->relname), newrelname);
@@ -2581,7 +2677,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
  * We also reject these commands if there are any pending AFTER trigger events
  * for the rel.  This is certainly necessary for the rewriting variants of
  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
- * events would try to fetch the wrong tuples. It might be overly cautious
+ * events would try to fetch the wrong tuples.  It might be overly cautious
  * in other cases, but again it seems better to err on the side of paranoia.
  *
  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
@@ -2637,23 +2733,23 @@ AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
  *             3. Scan table(s) to check new constraints, and optionally recopy
  *                the data into new table(s).
  * Phase 3 is not performed unless one or more of the subcommands requires
- * it. The intention of this design is to allow multiple independent
+ * it.  The intention of this design is to allow multiple independent
  * updates of the table schema to be performed with only one pass over the
  * data.
  *
- * ATPrepCmd performs phase 1. A "work queue" entry is created for
+ * ATPrepCmd performs phase 1.  A "work queue" entry is created for
  * each table to be affected (there may be multiple affected tables if the
  * commands traverse a table inheritance hierarchy).  Also we do preliminary
  * validation of the subcommands, including parse transformation of those
  * expressions that need to be evaluated with respect to the old table
  * schema.
  *
- * ATRewriteCatalogs performs phase 2 for each affected table. (Note that
+ * ATRewriteCatalogs performs phase 2 for each affected table.  (Note that
  * phases 2 and 3 normally do no explicit recursion, since phase 1 already
  * did it --- although some subcommands have to recurse in phase 2 instead.)
  * Certain subcommands need to be performed before others to avoid
  * unnecessary conflicts; for example, DROP COLUMN should come before
- * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
+ * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
  * lists, one for each logical "pass" of phase 2.
  *
  * ATRewriteTables performs phase 3 for those tables that need it.
@@ -2662,10 +2758,8 @@ AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
  * the whole operation; we don't have to do anything special to clean up.
  *
  * The caller must lock the relation, with an appropriate lock level
- * for the subcommands requested. Any subcommand that needs to rewrite
- * tuples in the table forces the whole command to be executed with
- * AccessExclusiveLock (actually, that is currently required always, but
- * we hope to relax it at some point). We pass the lock level down
+ * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
+ * or higher. We pass the lock level down
  * so that we can apply it recursively to inherited tables. Note that the
  * lock level we want as we recurse might well be higher than required for
  * that specific subcommand. So we pass down the overall lock requirement,
@@ -2681,7 +2775,8 @@ AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
 
        CheckTableNotInUse(rel, "ALTER TABLE");
 
-       ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt),
+       ATController(stmt,
+                                rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt),
                                 lockmode);
 }
 
@@ -2704,7 +2799,9 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
 
        rel = relation_open(relid, lockmode);
 
-       ATController(rel, cmds, recurse, lockmode);
+       EventTriggerAlterTableRelid(relid);
+
+       ATController(NULL, rel, cmds, recurse, lockmode);
 }
 
 /*
@@ -2727,35 +2824,24 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
  * and does not travel through this section of code and cannot be combined with
  * any of the subcommands given here.
+ *
+ * Note that Hot Standby only knows about AccessExclusiveLocks on the master
+ * so any changes that might affect SELECTs running on standbys need to use
+ * AccessExclusiveLocks even if you think a lesser lock would do, unless you
+ * have a solution for that also.
+ *
+ * Also note that pg_dump uses only an AccessShareLock, meaning that anything
+ * that takes a lock less than AccessExclusiveLock can change object definitions
+ * while pg_dump is running. Be careful to check that the appropriate data is
+ * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
+ * otherwise we might end up with an inconsistent dump that can't restore.
  */
 LOCKMODE
 AlterTableGetLockLevel(List *cmds)
 {
        /*
-        * Late in 9.1 dev cycle a number of issues were uncovered with access to
-        * catalog relations, leading to the decision to re-enforce all DDL at
-        * AccessExclusiveLock level by default.
-        *
-        * The issues are that there is a pervasive assumption in the code that
-        * the catalogs will not be read unless an AccessExclusiveLock is held. If
-        * that rule is relaxed, we must protect against a number of potential
-        * effects - infrequent, but proven possible with test cases where
-        * multiple DDL operations occur in a stream against frequently accessed
-        * tables.
-        *
-        * 1. Catalog tables were read using SnapshotNow, which has a race bug that
-        * allows a scan to return no valid rows even when one is present in the
-        * case of a commit of a concurrent update of the catalog table.
-        * SnapshotNow also ignores transactions in progress, so takes the latest
-        * committed version without waiting for the latest changes.
-        *
-        * 2. Relcache needs to be internally consistent, so unless we lock the
-        * definition during reads we have no way to guarantee that.
-        *
-        * 3. Catcache access isn't coordinated at all so refreshes can occur at
-        * any time.
+        * This only works if we read catalog tables using MVCC snapshots.
         */
-#ifdef REDUCED_ALTER_TABLE_LOCK_LEVELS
        ListCell   *lcmd;
        LOCKMODE        lockmode = ShareUpdateExclusiveLock;
 
@@ -2767,28 +2853,59 @@ AlterTableGetLockLevel(List *cmds)
                switch (cmd->subtype)
                {
                                /*
-                                * Need AccessExclusiveLock for these subcommands because they
-                                * affect or potentially affect both read and write
-                                * operations.
-                                *
-                                * New subcommand types should be added here by default.
+                                * These subcommands rewrite the heap, so require full locks.
                                 */
                        case AT_AddColumn:      /* may rewrite heap, in some cases and visible
                                                                 * to SELECT */
-                       case AT_DropColumn:     /* change visible to SELECT */
-                       case AT_AddColumnToView:        /* CREATE VIEW */
+                       case AT_SetTableSpace:          /* must rewrite heap */
                        case AT_AlterColumnType:        /* must rewrite heap */
-                       case AT_DropConstraint:         /* as DROP INDEX */
                        case AT_AddOids:        /* must rewrite heap */
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * These subcommands may require addition of toast tables. If
+                                * we add a toast table to a table currently being scanned, we
+                                * might miss data added to the new toast table by concurrent
+                                * insert transactions.
+                                */
+                       case AT_SetStorage:/* may add toast tables, see
+                                                                * ATRewriteCatalogs() */
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * Removing constraints can affect SELECTs that have been
+                                * optimised assuming the constraint holds true.
+                                */
+                       case AT_DropConstraint:         /* as DROP INDEX */
+                       case AT_DropNotNull:            /* may change some SQL plans */
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * Subcommands that may be visible to concurrent SELECTs
+                                */
+                       case AT_DropColumn:     /* change visible to SELECT */
+                       case AT_AddColumnToView:        /* CREATE VIEW */
                        case AT_DropOids:       /* calls AT_DropColumn */
                        case AT_EnableAlwaysRule:       /* may change SELECT rules */
                        case AT_EnableReplicaRule:      /* may change SELECT rules */
                        case AT_EnableRule:     /* may change SELECT rules */
                        case AT_DisableRule:            /* may change SELECT rules */
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * Changing owner may remove implicit SELECT privileges
+                                */
                        case AT_ChangeOwner:            /* change visible to SELECT */
-                       case AT_SetTableSpace:          /* must rewrite heap */
-                       case AT_DropNotNull:            /* may change some SQL plans */
-                       case AT_SetNotNull:
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * Changing foreign table options may affect optimisation.
+                                */
                        case AT_GenericOptions:
                        case AT_AlterColumnGenericOptions:
                                cmd_lockmode = AccessExclusiveLock;
@@ -2797,10 +2914,6 @@ AlterTableGetLockLevel(List *cmds)
                                /*
                                 * 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:
@@ -2809,13 +2922,30 @@ 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:
                        case AT_ReplicaIdentity:
-                               cmd_lockmode = ShareRowExclusiveLock;
+                       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;
@@ -2830,9 +2960,11 @@ AlterTableGetLockLevel(List *cmds)
                                                         * Cases essentially the same as CREATE INDEX. We
                                                         * could reduce the lock strength to ShareLock if
                                                         * we can work out how to allow concurrent catalog
-                                                        * updates.
+                                                        * updates. XXX Might be set down to
+                                                        * ShareRowExclusiveLock but requires further
+                                                        * analysis.
                                                         */
-                                                       cmd_lockmode = ShareRowExclusiveLock;
+                                                       cmd_lockmode = AccessExclusiveLock;
                                                        break;
                                                case CONSTR_FOREIGN:
 
@@ -2845,7 +2977,7 @@ AlterTableGetLockLevel(List *cmds)
                                                        break;
 
                                                default:
-                                                       cmd_lockmode = ShareRowExclusiveLock;
+                                                       cmd_lockmode = AccessExclusiveLock;
                                        }
                                }
                                break;
@@ -2855,23 +2987,32 @@ AlterTableGetLockLevel(List *cmds)
                                 * started before us will continue to see the old inheritance
                                 * behaviour, while queries started after we commit will see
                                 * new behaviour. No need to prevent reads or writes to the
-                                * subtable while we hook it up though.
+                                * subtable while we hook it up though. Changing the TupDesc
+                                * may be a problem, so keep highest lock.
                                 */
                        case AT_AddInherit:
                        case AT_DropInherit:
-                               cmd_lockmode = ShareUpdateExclusiveLock;
+                               cmd_lockmode = AccessExclusiveLock;
                                break;
 
                                /*
                                 * These subcommands affect implicit row type conversion. They
-                                * have affects similar to CREATE/DROP CAST on queries.  We
-                                * don't provide for invalidating parse trees as a result of
-                                * such changes.  Do avoid concurrent pg_class updates,
-                                * though.
+                                * have affects similar to CREATE/DROP CAST on queries. don't
+                                * provide for invalidating parse trees as a result of such
+                                * changes, so we keep these at AccessExclusiveLock.
                                 */
                        case AT_AddOf:
                        case AT_DropOf:
-                               cmd_lockmode = ShareUpdateExclusiveLock;
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                               /*
+                                * Only used by CREATE OR REPLACE VIEW which must conflict
+                                * with an SELECTs currently using the view.
+                                */
+                       case AT_ReplaceRelOptions:
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
 
                                /*
                                 * These subcommands affect general strategies for performance
@@ -2883,20 +3024,37 @@ AlterTableGetLockLevel(List *cmds)
                                 * applies: we don't currently allow concurrent catalog
                                 * updates.
                                 */
-                       case AT_SetStatistics:
-                       case AT_ClusterOn:
-                       case AT_DropCluster:
-                       case AT_SetRelOptions:
-                       case AT_ResetRelOptions:
-                       case AT_ReplaceRelOptions:
-                       case AT_SetOptions:
-                       case AT_ResetOptions:
-                       case AT_SetStorage:
-                       case AT_AlterConstraint:
-                       case AT_ValidateConstraint:
+                       case AT_SetStatistics:          /* Uses MVCC in getTableAttrs() */
+                       case AT_ClusterOn:      /* Uses MVCC in getIndexes() */
+                       case AT_DropCluster:            /* Uses MVCC in getIndexes() */
+                       case AT_SetOptions:     /* Uses MVCC in getTableAttrs() */
+                       case AT_ResetOptions:           /* Uses MVCC in getTableAttrs() */
+                               cmd_lockmode = ShareUpdateExclusiveLock;
+                               break;
+
+                       case AT_SetLogged:
+                       case AT_SetUnLogged:
+                               cmd_lockmode = AccessExclusiveLock;
+                               break;
+
+                       case AT_ValidateConstraint: /* Uses MVCC in
+                                                                                                * getConstraints() */
                                cmd_lockmode = ShareUpdateExclusiveLock;
                                break;
 
+                               /*
+                                * Rel options are more complex than first appears. Options
+                                * 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.
+                                */
+                       case AT_SetRelOptions:          /* Uses MVCC in getIndexes() and
+                                                                                * getTables() */
+                       case AT_ResetRelOptions:        /* Uses MVCC in getIndexes() and
+                                                                                * getTables() */
+                               cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
+                               break;
+
                        default:                        /* oops */
                                elog(ERROR, "unrecognized alter table type: %d",
                                         (int) cmd->subtype);
@@ -2909,15 +3067,19 @@ AlterTableGetLockLevel(List *cmds)
                if (cmd_lockmode > lockmode)
                        lockmode = cmd_lockmode;
        }
-#else
-       LOCKMODE        lockmode = AccessExclusiveLock;
-#endif
 
        return lockmode;
 }
 
+/*
+ * ATController provides top level control over the phases.
+ *
+ * parsetree is passed in to allow it to be passed to event triggers
+ * when requested.
+ */
 static void
-ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
+ATController(AlterTableStmt *parsetree,
+                        Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
 {
        List       *wqueue = NIL;
        ListCell   *lcmd;
@@ -2937,7 +3099,7 @@ ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
        ATRewriteCatalogs(&wqueue, lockmode);
 
        /* Phase 3: scan/rewrite tables as needed */
-       ATRewriteTables(&wqueue, lockmode);
+       ATRewriteTables(parsetree, &wqueue, lockmode);
 }
 
 /*
@@ -2976,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;
@@ -3025,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;
@@ -3044,7 +3208,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_ADD_INDEX;
                        break;
                case AT_AddConstraint:  /* ADD 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)
@@ -3058,7 +3222,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_ADD_CONSTR;
                        break;
                case AT_DropConstraint: /* DROP 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)
@@ -3090,15 +3254,37 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        /* No command-specific prep needed */
                        pass = AT_PASS_MISC;
                        break;
-               case AT_AddOids:                /* SET WITH OIDS */
+               case AT_SetLogged:              /* SET LOGGED */
+                       ATSimplePermissions(rel, ATT_TABLE);
+                       tab->chgPersistence = ATPrepChangePersistence(rel, true);
+                       /* force rewrite if necessary; see comment in ATRewriteTables */
+                       if (tab->chgPersistence)
+                       {
+                               tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
+                               tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
+                       }
+                       pass = AT_PASS_MISC;
+                       break;
+               case AT_SetUnLogged:    /* SET UNLOGGED */
                        ATSimplePermissions(rel, ATT_TABLE);
+                       tab->chgPersistence = ATPrepChangePersistence(rel, false);
+                       /* force rewrite if necessary; see comment in ATRewriteTables */
+                       if (tab->chgPersistence)
+                       {
+                               tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
+                               tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
+                       }
+                       pass = AT_PASS_MISC;
+                       break;
+               case AT_AddOids:                /* SET WITH OIDS */
+                       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)
                        {
@@ -3126,24 +3312,30 @@ 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)
                                cmd->subtype = AT_ValidateConstraintRecurse;
                        pass = AT_PASS_MISC;
                        break;
-               case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
+               case AT_ReplicaIdentity:                /* REPLICA IDENTITY ... */
                        ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
                        pass = AT_PASS_MISC;
                        /* This command never recurses */
@@ -3157,13 +3349,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_DisableTrig:    /* DISABLE TRIGGER variants */
                case AT_DisableTrigAll:
                case AT_DisableTrigUser:
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+                       pass = AT_PASS_MISC;
+                       break;
                case AT_EnableRule:             /* ENABLE/DISABLE RULE variants */
                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 */
@@ -3189,7 +3387,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 /*
  * ATRewriteCatalogs
  *
- * Traffic cop for ALTER TABLE Phase 2 operations.     Subcommands are
+ * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
  * dispatched in a "safe" execution order (designed to avoid unnecessary
  * conflicts).
  */
@@ -3246,7 +3444,7 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 
                if (tab->relkind == RELKIND_RELATION ||
                        tab->relkind == RELKIND_MATVIEW)
-                       AlterTableCreateToastTable(tab->relid, (Datum) 0);
+                       AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
        }
 }
 
@@ -3257,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,
@@ -3341,33 +3556,42 @@ 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);
                        break;
+               case AT_SetLogged:              /* SET LOGGED */
+               case AT_SetUnLogged:    /* SET UNLOGGED */
+                       break;
                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 */
 
@@ -3438,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);
@@ -3452,6 +3676,18 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                case AT_ReplicaIdentity:
                        ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
                        break;
+               case AT_EnableRowSecurity:
+                       ATExecEnableRowSecurity(rel);
+                       break;
+               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;
@@ -3461,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
@@ -3472,7 +3713,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
  * ATRewriteTables: ALTER TABLE phase 3
  */
 static void
-ATRewriteTables(List **wqueue, LOCKMODE lockmode)
+ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 {
        ListCell   *ltab;
 
@@ -3499,7 +3740,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
                 * constraints, so it's not necessary/appropriate to enforce them just
                 * during ALTER.)
                 */
-               if (tab->newvals != NIL || tab->rewrite)
+               if (tab->newvals != NIL || tab->rewrite > 0)
                {
                        Relation        rel;
 
@@ -3510,14 +3751,23 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
 
                /*
                 * We only need to rewrite the table if at least one column needs to
-                * be recomputed, or we are adding/removing the OID column.
+                * be recomputed, we are adding/removing the OID column, or we are
+                * changing its persistence.
+                *
+                * There are two reasons for requiring a rewrite when changing
+                * persistence: on one hand, we need to ensure that the buffers
+                * belonging to each of the two relations are marked with or without
+                * BM_PERMANENT properly.  On the other hand, since rewriting creates
+                * and assigns a new relfilenode, we automatically create or drop an
+                * init fork for the relation as appropriate.
                 */
-               if (tab->rewrite)
+               if (tab->rewrite > 0)
                {
                        /* Build a temporary relation and copy data */
                        Relation        OldHeap;
                        Oid                     OIDNewHeap;
                        Oid                     NewTableSpace;
+                       char            persistence;
 
                        OldHeap = heap_open(tab->relid, NoLock);
 
@@ -3532,6 +3782,12 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
                                                 errmsg("cannot rewrite system relation \"%s\"",
                                                                RelationGetRelationName(OldHeap))));
 
+                       if (RelationIsUsedAsCatalogTable(OldHeap))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                               errmsg("cannot rewrite table \"%s\" used as a catalog table",
+                                          RelationGetRelationName(OldHeap))));
+
                        /*
                         * Don't allow rewrite on temp tables of other backends ... their
                         * local buffer manager is not going to cope.
@@ -3550,11 +3806,47 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
                        else
                                NewTableSpace = OldHeap->rd_rel->reltablespace;
 
+                       /*
+                        * Select persistence of transient table (same as original unless
+                        * user requested a change)
+                        */
+                       persistence = tab->chgPersistence ?
+                               tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
+
                        heap_close(OldHeap, NoLock);
 
-                       /* Create transient table that will receive the modified data */
-                       OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, false,
-                                                                          AccessExclusiveLock);
+                       /*
+                        * Fire off an Event Trigger now, before actually rewriting the
+                        * table.
+                        *
+                        * We don't support Event Trigger for nested commands anywhere,
+                        * here included, and parsetree is given NULL when coming from
+                        * AlterTableInternal.
+                        *
+                        * And fire it only once.
+                        */
+                       if (parsetree)
+                               EventTriggerTableRewrite((Node *) parsetree,
+                                                                                tab->relid,
+                                                                                tab->rewrite);
+
+                       /*
+                        * Create transient table that will receive the modified data.
+                        *
+                        * Ensure it is marked correctly as logged or unlogged.  We have
+                        * to do this here so that buffers for the new relfilenode will
+                        * have the right persistence set, and at the same time ensure
+                        * that the original filenode's buffers will get read in with the
+                        * correct setting (i.e. the original one).  Otherwise a rollback
+                        * after the rewrite would possibly result with buffers for the
+                        * original filenode having the wrong persistence setting.
+                        *
+                        * NB: This relies on swap_relation_files() also swapping the
+                        * persistence. That wouldn't work for pg_class, but that can't be
+                        * unlogged anyway.
+                        */
+                       OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
+                                                                          lockmode);
 
                        /*
                         * Copy the heap data into the new table with the desired
@@ -3575,7 +3867,8 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
                                                         false, false, true,
                                                         !OidIsValid(tab->newTableSpace),
                                                         RecentXmin,
-                                                        ReadNextMultiXactId());
+                                                        ReadNextMultiXactId(),
+                                                        persistence);
                }
                else
                {
@@ -3781,7 +4074,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                {
                        /*
                         * All predicate locks on the tuples or pages are about to be made
-                        * invalid, because we move tuples around.      Promote them to
+                        * invalid, because we move tuples around.  Promote them to
                         * relation locks.
                         */
                        TransferPredicateLocksToHeapRelation(oldrel);
@@ -3830,7 +4123,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 
                while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
                {
-                       if (tab->rewrite)
+                       if (tab->rewrite > 0)
                        {
                                Oid                     tupOid = InvalidOid;
 
@@ -3871,8 +4164,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                                        HeapTupleSetOid(tuple, tupOid);
 
                                /*
-                                * Constraints might reference the tableoid column, so initialize
-                                * t_tableOid before evaluating them.
+                                * Constraints might reference the tableoid column, so
+                                * initialize t_tableOid before evaluating them.
                                 */
                                tuple->t_tableOid = RelationGetRelid(oldrel);
                        }
@@ -3973,6 +4266,8 @@ ATGetQueueEntry(List **wqueue, Relation rel)
        tab->relid = relid;
        tab->relkind = rel->rd_rel->relkind;
        tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
+       tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
+       tab->chgPersistence = false;
 
        *wqueue = lappend(*wqueue, tab);
 
@@ -4051,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;
@@ -4060,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;
@@ -4067,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");
@@ -4099,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;
@@ -4329,7 +4632,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
  *
  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
  * isn't suitable, throw an error.  Currently, we require that the type
- * originated with CREATE TYPE AS.     We could support any row type, but doing so
+ * originated with CREATE TYPE AS.  We could support any row type, but doing so
  * would require handling a number of extra corner cases in the DDL commands.
  */
 void
@@ -4348,7 +4651,7 @@ check_of_type(HeapTuple typetuple)
 
                /*
                 * Close the parent rel, but keep our AccessShareLock on it until xact
-                * commit.      That will prevent someone else from deleting or ALTERing
+                * commit.  That will prevent someone else from deleting or ALTERing
                 * the type before the typed table creation/conversion commits.
                 */
                relation_close(typeRelation, NoLock);
@@ -4377,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,
@@ -4387,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,
@@ -4412,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);
 
@@ -4476,7 +4785,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                          colDef->colname, RelationGetRelationName(rel))));
 
                        heap_close(attrdesc, RowExclusiveLock);
-                       return;
+                       return InvalidObjectAddress;
                }
        }
 
@@ -4487,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)
@@ -4623,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;
@@ -4654,7 +4969,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        newval->expr = expression_planner(defval);
 
                        tab->newvals = lappend(tab->newvals, newval);
-                       tab->rewrite = true;
+                       tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
                }
 
                /*
@@ -4671,7 +4986,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
         * table to fix that.
         */
        if (isOid)
-               tab->rewrite = true;
+               tab->rewrite |= AT_REWRITE_ALTER_OID;
 
        /*
         * Add needed dependency entries for the new column.
@@ -4716,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;
@@ -4742,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);
@@ -4758,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;
 }
 
 /*
@@ -4807,7 +5140,7 @@ add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
 /*
  * ALTER TABLE SET WITH OIDS
  *
- * Basically this is an ADD COLUMN for the special OID column. We have
+ * Basically this is an ADD COLUMN for the special OID column.  We have
  * to cons up a ColumnDef node because the ADD COLUMN code needs one.
  */
 static void
@@ -4827,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;
@@ -4835,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;
@@ -4844,6 +5180,7 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
        Relation        attr_rel;
        List       *indexoidlist;
        ListCell   *indexoidscan;
+       ObjectAddress address;
 
        /*
         * lookup the attribute
@@ -4921,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
@@ -4976,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
@@ -5038,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;
 }
 
 /*
@@ -5067,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);
@@ -5108,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\"",
@@ -5124,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)
 {
@@ -5137,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];
@@ -5155,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\"",
@@ -5189,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;
@@ -5207,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);
@@ -5239,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\"",
@@ -5269,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;
 }
 
 
@@ -5277,7 +5669,7 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
  *
  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
  * because we have to decide at runtime whether to recurse or not depending
- * on whether attinhcount goes to zero or not. (We can't check this in a
+ * on whether attinhcount goes to zero or not.  (We can't check this in a
  * static pre-pass because it won't handle multiple inheritance situations
  * correctly.)
  */
@@ -5297,8 +5689,11 @@ ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
                cmd->subtype = AT_DropColumnRecurse;
 }
 
-static void
-ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
+/*
+ * 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,
                                 bool missing_ok, LOCKMODE lockmode)
@@ -5311,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
@@ -5331,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);
@@ -5483,8 +5878,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                tab = ATGetQueueEntry(wqueue, rel);
 
                /* Tell Phase 3 to physically remove the OID column */
-               tab->rewrite = true;
+               tab->rewrite |= AT_REWRITE_ALTER_OID;
        }
+
+       return object;
 }
 
 /*
@@ -5493,54 +5890,62 @@ 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 */
-       skip_build = tab->rewrite || OidIsValid(stmt->oldNode);
+       skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
        /* suppress notices when rebuilding existing index */
        quiet = is_rebuild;
 
-       /* The IndexStmt has already been through transformIndexStmt */
-
-       new_index = DefineIndex(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
-        * index instead of building from scratch.      The DROP of the old edition of
+        * index instead of building from scratch.  The DROP of the old edition of
         * this index will have scheduled the storage for deletion at commit, so
         * cancel that pending deletion.
         */
        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)
 {
@@ -5550,6 +5955,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
        IndexInfo  *indexInfo;
        char       *constraintName;
        char            constraintType;
+       ObjectAddress address;
 
        Assert(IsA(stmt, IndexStmt));
        Assert(OidIsValid(index_oid));
@@ -5566,7 +5972,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
                elog(ERROR, "index \"%s\" is not unique", indexName);
 
        /*
-        * Determine name to assign to constraint.      We require a constraint to
+        * Determine name to assign to constraint.  We require a constraint to
         * have the same name as the underlying index; therefore, use the index's
         * existing name as the default constraint name, and if the user
         * explicitly gives some other name for the constraint, rename the index
@@ -5594,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));
 
        /*
@@ -5628,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:
@@ -5661,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.
  *
@@ -5682,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)
@@ -5699,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
@@ -5716,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)
        {
@@ -5741,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 */
@@ -5756,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
@@ -5775,7 +6191,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
        /*
         * Check if ONLY was specified with ALTER TABLE.  If so, allow the
-        * contraint creation only if there are no children currently.  Error out
+        * contraint creation only if there are no children currently.  Error out
         * otherwise.
         */
        if (!recurse && children != NIL)
@@ -5802,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
+ * 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)
 {
@@ -5830,16 +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.
         */
-       pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
+       if (OidIsValid(fkconstraint->old_pktable_oid))
+               pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
+       else
+               pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
 
        /*
         * Validity checks (permission checks wait till we have the column
@@ -5943,7 +6363,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
         *
         * Note that we have to be careful about the difference between the actual
         * PK column type and the opclass' declared input type, which might be
-        * only binary-compatible with it.      The declared opcintype is the right
+        * only binary-compatible with it.  The declared opcintype is the right
         * thing to probe pg_amop with.
         */
        if (numfks != numpks)
@@ -6100,7 +6520,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
 
                        /*
                         * Upon a change to the cast from the FK column to its pfeqop
-                        * operand, revalidate the constraint.  For this evaluation, a
+                        * operand, revalidate the constraint.  For this evaluation, a
                         * binary coercion cast is equivalent to no cast at all.  While
                         * type implementors should design implicit casts with an eye
                         * toward consistency of operations like equality, we cannot
@@ -6118,7 +6538,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                         * Necessarily, the primary key column must then be of the domain
                         * type.  Since the constraint was previously valid, all values on
                         * the foreign side necessarily exist on the primary side and in
-                        * turn conform to the domain.  Consequently, we need not treat
+                        * turn conform to the domain.  Consequently, we need not treat
                         * domains specially here.
                         *
                         * Since we require that all collations share the same notion of
@@ -6128,7 +6548,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                         * We need not directly consider the PK type.  It's necessarily
                         * binary coercible to the opcintype of the unique index column,
                         * and ri_triggers.c will only deal with PK datums in terms of
-                        * that opcintype.      Changing the opcintype also changes pfeqop.
+                        * that opcintype.  Changing the opcintype also changes pfeqop.
                         */
                        old_check_ok = (new_pathtype == old_pathtype &&
                                                        new_castfunc == old_castfunc &&
@@ -6174,11 +6594,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                                          0,            /* inhcount */
                                                                          true,         /* isnoinherit */
                                                                          false);       /* is_internal */
+       ObjectAddressSet(address, ConstraintRelationId, constrOid);
 
        /*
         * Create the triggers that will enforce the constraint.
         */
-       createForeignKeyTriggers(rel, fkconstraint, constrOid, indexOid);
+       createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint,
+                                                        constrOid, indexOid);
 
        /*
         * Tell Phase 3 to check that the constraint is satisfied by existing
@@ -6206,6 +6628,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
         * Close pk table, but keep lock until we've committed.
         */
        heap_close(pkrel, NoLock);
+
+       return address;
 }
 
 /*
@@ -6217,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)
+                                         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;
@@ -6273,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.
@@ -6294,8 +6723,8 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
                heap_freetuple(copyTuple);
 
                /*
-                * Now we need to update the multiple entries in pg_trigger
-                * that implement the constraint.
+                * Now we need to update the multiple entries in pg_trigger that
+                * implement the constraint.
                 */
                tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
 
@@ -6309,15 +6738,23 @@ 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);
                        CatalogUpdateIndexes(tgrel, copyTuple);
 
                        InvokeObjectPostAlterHook(TriggerRelationId,
-                                                                               HeapTupleGetOid(tgtuple), 0);
+                                                                         HeapTupleGetOid(tgtuple), 0);
 
                        heap_freetuple(copyTuple);
                }
@@ -6327,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;
 }
 
 /*
@@ -6344,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)
 {
@@ -6355,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);
 
@@ -6398,7 +6853,6 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 
                if (con->contype == CONSTRAINT_FOREIGN)
                {
-                       Oid                     conid = HeapTupleGetOid(tuple);
                        Relation        refrel;
 
                        /*
@@ -6413,7 +6867,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 
                        validateForeignKeyConstraint(constrName, rel, refrel,
                                                                                 con->conindid,
-                                                                                conid);
+                                                                                HeapTupleGetOid(tuple));
                        heap_close(refrel, NoLock);
 
                        /*
@@ -6489,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;
 }
 
 
@@ -6539,10 +7000,10 @@ transformColumnNameList(Oid relId, List *colList,
  * transformFkeyGetPrimaryKey -
  *
  *     Look up the names, attnums, and types of the primary key attributes
- *     for the pkrel.  Also return the index OID and index opclasses of the
+ *     for the pkrel.  Also return the index OID and index opclasses of the
  *     index supporting the primary key.
  *
- *     All parameters except pkrel are output parameters.      Also, the function
+ *     All parameters except pkrel are output parameters.  Also, the function
  *     return value is the number of attributes in the primary key.
  *
  *     Used when the column list in the REFERENCES specification is omitted.
@@ -6582,7 +7043,7 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
                if (indexStruct->indisprimary && IndexIsValid(indexStruct))
                {
                        /*
-                        * Refuse to use a deferrable primary key.      This is per SQL spec,
+                        * Refuse to use a deferrable primary key.  This is per SQL spec,
                         * and there would be a lot of interesting semantic problems if we
                         * tried to allow it.
                         */
@@ -6654,6 +7115,26 @@ transformFkeyCheckAttrs(Relation pkrel,
        bool            found_deferrable = false;
        List       *indexoidlist;
        ListCell   *indexoidscan;
+       int                     i,
+                               j;
+
+       /*
+        * Reject duplicate appearances of columns in the referenced-columns list.
+        * Such a case is forbidden by the SQL standard, and even if we thought it
+        * useful to allow it, there would be ambiguity about how to match the
+        * list to unique indexes (in particular, it'd be unclear which index
+        * opclass goes with which FK column).
+        */
+       for (i = 0; i < numattrs; i++)
+       {
+               for (j = i + 1; j < numattrs; j++)
+               {
+                       if (attnums[i] == attnums[j])
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+                                                errmsg("foreign key referenced-columns list must not contain duplicates")));
+               }
+       }
 
        /*
         * Get the list of index OIDs for the table from the relcache, and look up
@@ -6666,8 +7147,6 @@ transformFkeyCheckAttrs(Relation pkrel,
        {
                HeapTuple       indexTuple;
                Form_pg_index indexStruct;
-               int                     i,
-                                       j;
 
                indexoid = lfirst_oid(indexoidscan);
                indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
@@ -6686,11 +7165,11 @@ transformFkeyCheckAttrs(Relation pkrel,
                        heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
                        heap_attisnull(indexTuple, Anum_pg_index_indexprs))
                {
-                       /* Must get indclass the hard way */
                        Datum           indclassDatum;
                        bool            isnull;
                        oidvector  *indclass;
 
+                       /* Must get indclass the hard way */
                        indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
                                                                                        Anum_pg_index_indclass, &isnull);
                        Assert(!isnull);
@@ -6698,7 +7177,13 @@ transformFkeyCheckAttrs(Relation pkrel,
 
                        /*
                         * The given attnum list may match the index columns in any order.
-                        * Check that each list is a subset of the other.
+                        * Check for a match, and extract the appropriate opclasses while
+                        * we're at it.
+                        *
+                        * We know that attnums[] is duplicate-free per the test at the
+                        * start of this function, and we checked above that the number of
+                        * index columns agrees, so if we find a match for each attnums[]
+                        * entry then we must have a one-to-one match in some order.
                         */
                        for (i = 0; i < numattrs; i++)
                        {
@@ -6707,6 +7192,7 @@ transformFkeyCheckAttrs(Relation pkrel,
                                {
                                        if (attnums[i] == indexStruct->indkey.values[j])
                                        {
+                                               opclasses[i] = indclass->values[j];
                                                found = true;
                                                break;
                                        }
@@ -6714,24 +7200,6 @@ transformFkeyCheckAttrs(Relation pkrel,
                                if (!found)
                                        break;
                        }
-                       if (found)
-                       {
-                               for (i = 0; i < numattrs; i++)
-                               {
-                                       found = false;
-                                       for (j = 0; j < numattrs; j++)
-                                       {
-                                               if (attnums[j] == indexStruct->indkey.values[i])
-                                               {
-                                                       opclasses[j] = indclass->values[i];
-                                                       found = true;
-                                                       break;
-                                               }
-                                       }
-                                       if (!found)
-                                               break;
-                               }
-                       }
 
                        /*
                         * Refuse to use a deferrable unique/primary key.  This is per SQL
@@ -6849,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();
@@ -6988,7 +7460,7 @@ validateForeignKeyConstraint(char *conname,
 }
 
 static void
-CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
+CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
                                         Oid constraintOid, Oid indexOid, bool on_insert)
 {
        CreateTrigStmt *fk_trigger;
@@ -7004,7 +7476,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
         */
        fk_trigger = makeNode(CreateTrigStmt);
        fk_trigger->trigname = "RI_ConstraintTrigger_c";
-       fk_trigger->relation = myRel;
+       fk_trigger->relation = NULL;
        fk_trigger->row = true;
        fk_trigger->timing = TRIGGER_TYPE_AFTER;
 
@@ -7025,10 +7497,11 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
        fk_trigger->isconstraint = true;
        fk_trigger->deferrable = fkconstraint->deferrable;
        fk_trigger->initdeferred = fkconstraint->initdeferred;
-       fk_trigger->constrrel = fkconstraint->pktable;
+       fk_trigger->constrrel = NULL;
        fk_trigger->args = NIL;
 
-       (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
+       (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
+                                                indexOid, true);
 
        /* Make changes-so-far visible */
        CommandCounterIncrement();
@@ -7038,18 +7511,13 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
  * Create the triggers that implement an FK constraint.
  */
 static void
-createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
+createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
                                                 Oid constraintOid, Oid indexOid)
 {
-       RangeVar   *myRel;
+       Oid                     myRelOid;
        CreateTrigStmt *fk_trigger;
 
-       /*
-        * Reconstruct a RangeVar for my relation (not passed in, unfortunately).
-        */
-       myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
-                                                pstrdup(RelationGetRelationName(rel)),
-                                                -1);
+       myRelOid = RelationGetRelid(rel);
 
        /* Make changes-so-far visible */
        CommandCounterIncrement();
@@ -7060,14 +7528,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
         */
        fk_trigger = makeNode(CreateTrigStmt);
        fk_trigger->trigname = "RI_ConstraintTrigger_a";
-       fk_trigger->relation = fkconstraint->pktable;
+       fk_trigger->relation = NULL;
        fk_trigger->row = true;
        fk_trigger->timing = TRIGGER_TYPE_AFTER;
        fk_trigger->events = TRIGGER_TYPE_DELETE;
        fk_trigger->columns = NIL;
        fk_trigger->whenClause = NULL;
        fk_trigger->isconstraint = true;
-       fk_trigger->constrrel = myRel;
+       fk_trigger->constrrel = NULL;
        switch (fkconstraint->fk_del_action)
        {
                case FKCONSTR_ACTION_NOACTION:
@@ -7102,7 +7570,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
        }
        fk_trigger->args = NIL;
 
-       (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
+       (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
+                                                indexOid, true);
 
        /* Make changes-so-far visible */
        CommandCounterIncrement();
@@ -7113,14 +7582,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
         */
        fk_trigger = makeNode(CreateTrigStmt);
        fk_trigger->trigname = "RI_ConstraintTrigger_a";
-       fk_trigger->relation = fkconstraint->pktable;
+       fk_trigger->relation = NULL;
        fk_trigger->row = true;
        fk_trigger->timing = TRIGGER_TYPE_AFTER;
        fk_trigger->events = TRIGGER_TYPE_UPDATE;
        fk_trigger->columns = NIL;
        fk_trigger->whenClause = NULL;
        fk_trigger->isconstraint = true;
-       fk_trigger->constrrel = myRel;
+       fk_trigger->constrrel = NULL;
        switch (fkconstraint->fk_upd_action)
        {
                case FKCONSTR_ACTION_NOACTION:
@@ -7155,7 +7624,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
        }
        fk_trigger->args = NIL;
 
-       (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
+       (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
+                                                indexOid, true);
 
        /* Make changes-so-far visible */
        CommandCounterIncrement();
@@ -7164,8 +7634,10 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
         * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK
         * action for both INSERTs and UPDATEs on the referencing table.
         */
-       CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, true);
-       CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, false);
+       CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
+                                                indexOid, true);
+       CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
+                                                indexOid, false);
 }
 
 /*
@@ -7191,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);
 
@@ -7377,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;
@@ -7436,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,
@@ -7478,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);
@@ -7500,7 +7967,7 @@ ATPrepAlterColumnType(List **wqueue,
 
                tab->newvals = lappend(tab->newvals, newval);
                if (ATColumnChangeRequiresRewrite(transform, attnum))
-                       tab->rewrite = true;
+                       tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
        }
        else if (transform)
                ereport(ERROR,
@@ -7512,7 +7979,7 @@ ATPrepAlterColumnType(List **wqueue,
                tab->relkind == RELKIND_FOREIGN_TABLE)
        {
                /*
-                * For composite types, do this check now.      Tables will check it later
+                * For composite types, do this check now.  Tables will check it later
                 * when the table is being rewritten.
                 */
                find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
@@ -7521,7 +7988,7 @@ ATPrepAlterColumnType(List **wqueue,
        ReleaseSysCache(tuple);
 
        /*
-        * The recursion case is handled by ATSimpleRecursion.  However, if we are
+        * The recursion case is handled by ATSimpleRecursion.  However, if we are
         * told not to recurse, there had better not be any child tables; else the
         * alter would put them out of step.
         */
@@ -7564,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;
                }
@@ -7573,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)
 {
@@ -7594,6 +8066,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
        ScanKeyData key[3];
        SysScanDesc scan;
        HeapTuple       depTup;
+       ObjectAddress address;
 
        attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
 
@@ -7630,7 +8103,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
         *
         * We remove any implicit coercion steps at the top level of the old
         * default expression; this has been agreed to satisfy the principle of
-        * least surprise.      (The conversion to the new column type should act like
+        * least surprise.  (The conversion to the new column type should act like
         * it started from what the user sees as the stored expression, and the
         * implicit coercions aren't going to be shown.)
         */
@@ -7659,7 +8132,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
         * and record enough information to let us recreate the objects.
         *
         * The actual recreation does not happen here, but only after we have
-        * performed all the individual ALTER TYPE operations.  We have to save
+        * performed all the individual ALTER TYPE operations.  We have to save
         * the info before executing ALTER TYPE, though, else the deparser will
         * get confused.
         *
@@ -7738,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
@@ -7788,7 +8261,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                 * used in the trigger's WHEN condition.  The first case would
                                 * not require any extra work, but the second case would
                                 * require updating the WHEN expression, which will take a
-                                * significant amount of new code.      Since we can't easily tell
+                                * significant amount of new code.  Since we can't easily tell
                                 * which case applies, we punt for both.  FIXME someday.
                                 */
                                ereport(ERROR,
@@ -7799,6 +8272,24 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                                                                   colName)));
                                break;
 
+                       case OCLASS_POLICY:
+
+                               /*
+                                * A policy can depend on a column because the column is
+                                * 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.
+                                */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("cannot alter type of a column used in a policy definition"),
+                                                errdetail("%s depends on column \"%s\"",
+                                                                  getObjectDescription(&foundObject),
+                                                                  colName)));
+                               break;
+
                        case OCLASS_DEFAULT:
 
                                /*
@@ -7947,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,
@@ -7970,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);
@@ -7999,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)));
@@ -8042,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;
 }
 
 /*
@@ -8064,20 +8570,61 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 
        /*
         * Re-parse the index and constraint definitions, and attach them to the
-        * appropriate work queue entries.      We do this before dropping because in
+        * appropriate work queue entries.  We do this before dropping because in
         * the case of a FOREIGN KEY constraint, we might not yet have exclusive
         * lock on the table the constraint is attached to, and we need to get
         * that before dropping.  It's safe because the parser won't actually look
         * at the catalogs to detect the existing entry.
+        *
+        * We can't rely on the output of deparsing to tell us which relation to
+        * operate on, because concurrent activity might have made the name
+        * resolve differently.  Instead, we've got to use the OID of the
+        * constraint or index we're processing to figure out which relation to
+        * operate on.
         */
        forboth(oid_item, tab->changedConstraintOids,
                        def_item, tab->changedConstraintDefs)
-               ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item),
+       {
+               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;
+
+               ATPostAlterTypeParse(oldId, relid, confrelid,
+                                                        (char *) lfirst(def_item),
                                                         wqueue, lockmode, tab->rewrite);
+       }
        forboth(oid_item, tab->changedIndexOids,
                        def_item, tab->changedIndexDefs)
-               ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item),
+       {
+               Oid                     oldId = lfirst_oid(oid_item);
+               Oid                     relid;
+
+               relid = IndexGetRelation(oldId, false);
+               ATPostAlterTypeParse(oldId, relid, InvalidOid,
+                                                        (char *) lfirst(def_item),
                                                         wqueue, lockmode, tab->rewrite);
+       }
 
        /*
         * Now we can drop the existing constraints and indexes --- constraints
@@ -8110,12 +8657,13 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 }
 
 static void
-ATPostAlterTypeParse(Oid oldId, char *cmd,
+ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
                                         List **wqueue, LOCKMODE lockmode, bool rewrite)
 {
        List       *raw_parsetree_list;
        List       *querytree_list;
        ListCell   *list_item;
+       Relation        rel;
 
        /*
         * We expect that we will get only ALTER TABLE and CREATE INDEX
@@ -8131,16 +8679,21 @@ ATPostAlterTypeParse(Oid oldId, char *cmd,
 
                if (IsA(stmt, IndexStmt))
                        querytree_list = lappend(querytree_list,
-                                                                        transformIndexStmt((IndexStmt *) stmt,
+                                                                        transformIndexStmt(oldRelId,
+                                                                                                               (IndexStmt *) stmt,
                                                                                                                cmd));
                else if (IsA(stmt, AlterTableStmt))
                        querytree_list = list_concat(querytree_list,
-                                                        transformAlterTableStmt((AlterTableStmt *) stmt,
-                                                                                                        cmd));
+                                                                                transformAlterTableStmt(oldRelId,
+                                                                                                        (AlterTableStmt *) stmt,
+                                                                                                                                cmd));
                else
                        querytree_list = lappend(querytree_list, stmt);
        }
 
+       /* Caller should already have acquired whatever lock we need. */
+       rel = relation_open(oldRelId, NoLock);
+
        /*
         * Attach each generated command to the proper place in the work queue.
         * Note this could result in creation of entirely new work-queue entries.
@@ -8152,76 +8705,128 @@ ATPostAlterTypeParse(Oid oldId, char *cmd,
        foreach(list_item, querytree_list)
        {
                Node       *stm = (Node *) lfirst(list_item);
-               Relation        rel;
                AlteredTableInfo *tab;
 
-               switch (nodeTag(stm))
+               tab = ATGetQueueEntry(wqueue, rel);
+
+               if (IsA(stm, 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))
                {
-                       case T_IndexStmt:
+                       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);
 
-                                       rel = relation_openrv(stmt->relation, lockmode);
-                                       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);
-                                       relation_close(rel, NoLock);
-                                       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;
-
-                                       rel = relation_openrv(stmt->relation, lockmode);
-                                       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;
-                                                               /* rewriting neither side of a FK */
-                                                               if (con->contype == CONSTR_FOREIGN &&
-                                                                       !rewrite && !tab->rewrite)
-                                                                       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);
-                                               }
-                                       }
-                                       relation_close(rel, NoLock);
-                                       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);
 }
 
 /*
@@ -8232,7 +8837,6 @@ static void
 TryReuseIndex(Oid oldId, IndexStmt *stmt)
 {
        if (CheckIndexCompatible(oldId,
-                                                        stmt->relation,
                                                         stmt->accessMethod,
                                                         stmt->indexParams,
                                                         stmt->excludeOpNames))
@@ -8481,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
@@ -8664,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);
 
@@ -8683,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;
 }
 
 /*
@@ -8704,21 +9315,26 @@ static void
 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
 {
        Oid                     tablespaceId;
-       AclResult       aclresult;
 
        /* Check that the tablespace exists */
        tablespaceId = get_tablespace_oid(tablespacename, false);
 
-       /* Check its permissions */
-       aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
-       if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
+       /* Check permissions except when moving to database's default */
+       if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
+       {
+               AclResult       aclresult;
+
+               aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
+       }
 
        /* Save info for Phase 3 to do the real work */
        if (OidIsValid(tab->newTableSpace))
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("cannot have multiple SET TABLESPACE subcommands")));
+
        tab->newTableSpace = tablespaceId;
 }
 
@@ -8778,10 +9394,12 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
        {
                case RELKIND_RELATION:
                case RELKIND_TOASTVALUE:
-               case RELKIND_VIEW:
                case RELKIND_MATVIEW:
                        (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
                        break;
+               case RELKIND_VIEW:
+                       (void) view_reloptions(newOptions, true);
+                       break;
                case RELKIND_INDEX:
                        (void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
                        break;
@@ -8800,7 +9418,6 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
                List       *view_options = untransformRelOptions(newOptions);
                ListCell   *cell;
                bool            check_option = false;
-               bool            security_barrier = false;
 
                foreach(cell, view_options)
                {
@@ -8808,8 +9425,6 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 
                        if (pg_strcasecmp(defel->defname, "check_option") == 0)
                                check_option = true;
-                       if (pg_strcasecmp(defel->defname, "security_barrier") == 0)
-                               security_barrier = defGetBoolean(defel);
                }
 
                /*
@@ -8819,14 +9434,13 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
                if (check_option)
                {
                        const char *view_updatable_error =
-                               view_query_is_auto_updatable(view_query,
-                                                                                        security_barrier, true);
+                       view_query_is_auto_updatable(view_query, true);
 
                        if (view_updatable_error)
                                ereport(ERROR,
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                               errmsg("WITH CHECK OPTION is supported only on auto-updatable views"),
-                                               errhint("%s", view_updatable_error)));
+                                                errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
+                                                errhint("%s", view_updatable_error)));
                }
        }
 
@@ -8993,7 +9607,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
        /* Fetch the list of indexes on toast relation if necessary */
        if (OidIsValid(reltoastrelid))
        {
-               Relation toastRel = relation_open(reltoastrelid, lockmode);
+               Relation        toastRel = relation_open(reltoastrelid, lockmode);
+
                reltoastidxids = RelationGetIndexList(toastRel);
                relation_close(toastRel, lockmode);
        }
@@ -9015,8 +9630,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
        FlushRelationBuffers(rel);
 
        /*
-        * Relfilenodes are not unique across tablespaces, so we need to allocate
-        * a new one in the new tablespace.
+        * Relfilenodes are not unique in databases across tablespaces, so we need
+        * to allocate a new one in the new tablespace.
         */
        newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
                                                                           rel->rd_rel->relpersistence);
@@ -9048,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);
                }
@@ -9084,6 +9708,179 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
        list_free(reltoastidxids);
 }
 
+/*
+ * Alter Table ALL ... SET TABLESPACE
+ *
+ * Allows a user to move all objects of some type in a given tablespace in the
+ * current database to another tablespace.  Objects can be chosen based on the
+ * owner of the object also, to allow users to move only their objects.
+ * The user must have CREATE rights on the new tablespace, as usual.   The main
+ * permissions handling is done by the lower-level table move function.
+ *
+ * All to-be-moved objects are locked first. If NOWAIT is specified and the
+ * lock can't be acquired then we ereport(ERROR).
+ */
+Oid
+AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
+{
+       List       *relations = NIL;
+       ListCell   *l;
+       ScanKeyData key[1];
+       Relation        rel;
+       HeapScanDesc scan;
+       HeapTuple       tuple;
+       Oid                     orig_tablespaceoid;
+       Oid                     new_tablespaceoid;
+       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 &&
+               stmt->objtype != OBJECT_MATVIEW)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("only tables, indexes, and materialized views exist in tablespaces")));
+
+       /* Get the orig and new tablespace OIDs */
+       orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
+       new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
+
+       /* Can't move shared relations in to or out of pg_global */
+       /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
+       if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
+               new_tablespaceoid == GLOBALTABLESPACE_OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("cannot move relations in to or out of pg_global tablespace")));
+
+       /*
+        * Must have CREATE rights on the new tablespace, unless it is the
+        * database default tablespace (which all users implicitly have CREATE
+        * rights on).
+        */
+       if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
+       {
+               AclResult       aclresult;
+
+               aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
+                                                                                  ACL_CREATE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
+                                                  get_tablespace_name(new_tablespaceoid));
+       }
+
+       /*
+        * Now that the checks are done, check if we should set either to
+        * InvalidOid because it is our database's default tablespace.
+        */
+       if (orig_tablespaceoid == MyDatabaseTableSpace)
+               orig_tablespaceoid = InvalidOid;
+
+       if (new_tablespaceoid == MyDatabaseTableSpace)
+               new_tablespaceoid = InvalidOid;
+
+       /* no-op */
+       if (orig_tablespaceoid == new_tablespaceoid)
+               return new_tablespaceoid;
+
+       /*
+        * Walk the list of objects in the tablespace and move them. This will
+        * only find objects in our database, of course.
+        */
+       ScanKeyInit(&key[0],
+                               Anum_pg_class_reltablespace,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(orig_tablespaceoid));
+
+       rel = heap_open(RelationRelationId, AccessShareLock);
+       scan = heap_beginscan_catalog(rel, 1, key);
+       while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+       {
+               Oid                     relOid = HeapTupleGetOid(tuple);
+               Form_pg_class relForm;
+
+               relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+               /*
+                * Do not move objects in pg_catalog as part of this, if an admin
+                * really wishes to do so, they can issue the individual ALTER
+                * commands directly.
+                *
+                * Also, explicitly avoid any shared tables, temp tables, or TOAST
+                * (TOAST will be moved with the main table).
+                */
+               if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared ||
+                       isAnyTempNamespace(relForm->relnamespace) ||
+                       relForm->relnamespace == PG_TOAST_NAMESPACE)
+                       continue;
+
+               /* Only move the object type requested */
+               if ((stmt->objtype == OBJECT_TABLE &&
+                        relForm->relkind != RELKIND_RELATION) ||
+                       (stmt->objtype == OBJECT_INDEX &&
+                        relForm->relkind != RELKIND_INDEX) ||
+                       (stmt->objtype == OBJECT_MATVIEW &&
+                        relForm->relkind != RELKIND_MATVIEW))
+                       continue;
+
+               /* Check if we are only moving objects owned by certain roles */
+               if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
+                       continue;
+
+               /*
+                * Handle permissions-checking here since we are locking the tables
+                * and also to avoid doing a bunch of work only to fail part-way. Note
+                * that permissions will also be checked by AlterTableInternal().
+                *
+                * Caller must be considered an owner on the table to move it.
+                */
+               if (!pg_class_ownercheck(relOid, GetUserId()))
+                       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                                                  NameStr(relForm->relname));
+
+               if (stmt->nowait &&
+                       !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))));
+               else
+                       LockRelationOid(relOid, AccessExclusiveLock);
+
+               /* Add to our list of objects to move */
+               relations = lappend_oid(relations, relOid);
+       }
+
+       heap_endscan(scan);
+       heap_close(rel, AccessShareLock);
+
+       if (relations == NIL)
+               ereport(NOTICE,
+                               (errcode(ERRCODE_NO_DATA_FOUND),
+                                errmsg("no matching relations in tablespace \"%s\" found",
+                                       orig_tablespaceoid == InvalidOid ? "(database default)" :
+                                               get_tablespace_name(orig_tablespaceoid))));
+
+       /* Everything is locked, loop through and move all of the relations. */
+       foreach(l, relations)
+       {
+               List       *cmds = NIL;
+               AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+               cmd->subtype = AT_SetTableSpace;
+               cmd->name = stmt->new_tablespacename;
+
+               cmds = lappend(cmds, cmd);
+
+               EventTriggerAlterTableStart((Node *) stmt);
+               /* OID is set by AlterTableInternal */
+               AlterTableInternal(lfirst_oid(l), cmds, false);
+               EventTriggerAlterTableEnd();
+       }
+
+       return new_tablespaceoid;
+}
+
 /*
  * Copy data, block by block
  */
@@ -9094,6 +9891,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
        char       *buf;
        Page            page;
        bool            use_wal;
+       bool            copying_initfork;
        BlockNumber nblocks;
        BlockNumber blkno;
 
@@ -9106,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);
 
@@ -9130,14 +9937,18 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
                                                                                   src->smgr_rnode.backend,
                                                                                   forkNum))));
 
-               /* XLOG stuff */
+               /*
+                * WAL-log the copied page. Unfortunately we don't know what kind of a
+                * page this is, so we have to log the full page including any unused
+                * space.
+                */
                if (use_wal)
-                       log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page);
+                       log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
 
                PageSetChecksumInplace(page, blkno);
 
                /*
-                * Now write the page.  We say isTemp = true even if it's not a temp
+                * Now write the page.  We say isTemp = true even if it's not a temp
                 * rel, because there's no need for smgr to schedule an fsync for this
                 * write; we'll do it ourselves below.
                 */
@@ -9147,7 +9958,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
        pfree(buf);
 
        /*
-        * If the rel is WAL-logged, must fsync before commit.  We use heap_sync
+        * If the rel is WAL-logged, must fsync before commit.  We use heap_sync
         * to ensure that the toast table gets fsync'd too.  (For a temp or
         * unlogged rel we don't care since the data will be gone after a crash
         * anyway.)
@@ -9161,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);
 }
 
@@ -9205,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,
@@ -9215,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
@@ -9226,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 &&
@@ -9322,18 +10137,23 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
        MergeConstraintsIntoExisting(child_rel, parent_rel);
 
        /*
-        * OK, it looks valid.  Make the catalog entries that show inheritance.
+        * OK, it looks valid.  Make the catalog entries that show inheritance.
         */
        StoreCatalogInheritance1(RelationGetRelid(child_rel),
                                                         RelationGetRelid(parent_rel),
                                                         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;
 }
 
 /*
@@ -9601,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];
@@ -9614,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
@@ -9774,6 +10598,8 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
                }
        }
 
+       parent_oid = RelationGetRelid(parent_rel);
+
        systable_endscan(scan);
        heap_close(catalogRelation, RowExclusiveLock);
 
@@ -9792,13 +10618,17 @@ 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;
 }
 
 /*
  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
- * be TypeRelationId). There's no convenient way to do this, so go trawling
+ * be TypeRelationId).  There's no convenient way to do this, so go trawling
  * through pg_depend.
  */
 static void
@@ -9849,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);
@@ -9979,12 +10811,14 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
        heap_close(relationRelation, RowExclusiveLock);
 
        ReleaseSysCache(typetuple);
+
+       return typeobj;
 }
 
 /*
  * ALTER TABLE NOT OF
  *
- * Detach a typed table from its originating type.     Just clear reloftype and
+ * Detach a typed table from its originating type.  Just clear reloftype and
  * remove the dependency.
  */
 static void
@@ -10046,7 +10880,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
         */
        pg_class = heap_open(RelationRelationId, RowExclusiveLock);
        pg_class_tuple = SearchSysCacheCopy1(RELOID,
-                                                                                ObjectIdGetDatum(RelationGetRelid(rel)));
+                                                                       ObjectIdGetDatum(RelationGetRelid(rel)));
        if (!HeapTupleIsValid(pg_class_tuple))
                elog(ERROR, "cache lookup failed for relation \"%s\"",
                         RelationGetRelationName(rel));
@@ -10082,8 +10916,8 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
        }
 
        /*
-        * Clear the indisreplident flag from any index that had it previously, and
-        * set it for any index that should have it now.
+        * Clear the indisreplident flag from any index that had it previously,
+        * and set it for any index that should have it now.
         */
        pg_index = heap_open(IndexRelationId, RowExclusiveLock);
        foreach(index, RelationGetIndexList(rel))
@@ -10092,7 +10926,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
                bool            dirty = false;
 
                pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
-                                                                                ObjectIdGetDatum(thisIndexOid));
+                                                                                        ObjectIdGetDatum(thisIndexOid));
                if (!HeapTupleIsValid(pg_index_tuple))
                        elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
                pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
@@ -10152,7 +10986,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
        }
        else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
        {
-               /* fallthrough */;
+                /* fallthrough */ ;
        }
        else
                elog(ERROR, "unexpected identity type %u", stmt->identity_type);
@@ -10180,20 +11014,20 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
        if (!indexRel->rd_am->amcanunique || !indexRel->rd_index->indisunique)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("cannot use non-unique index \"%s\" as replica identity",
-                                               RelationGetRelationName(indexRel))));
+                        errmsg("cannot use non-unique index \"%s\" as replica identity",
+                                       RelationGetRelationName(indexRel))));
        /* Deferred indexes are not guaranteed to be always unique. */
        if (!indexRel->rd_index->indimmediate)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot use non-immediate index \"%s\" as replica identity",
-                                               RelationGetRelationName(indexRel))));
+                 errmsg("cannot use non-immediate index \"%s\" as replica identity",
+                                RelationGetRelationName(indexRel))));
        /* Expression indexes aren't supported. */
        if (RelationGetIndexExpressions(indexRel) != NIL)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("cannot use expression index \"%s\" as replica identity",
-                                               RelationGetRelationName(indexRel))));
+                        errmsg("cannot use expression index \"%s\" as replica identity",
+                                       RelationGetRelationName(indexRel))));
        /* Predicate indexes aren't supported. */
        if (RelationGetIndexPredicate(indexRel) != NIL)
                ereport(ERROR,
@@ -10210,7 +11044,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
        /* Check index for nullable columns. */
        for (key = 0; key < indexRel->rd_index->indnatts; key++)
        {
-               int16 attno = indexRel->rd_index->indkey.values[key];
+               int16           attno = indexRel->rd_index->indkey.values[key];
                Form_pg_attribute attr;
 
                /* Of the system columns, only oid is indexable. */
@@ -10233,6 +11067,91 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
        index_close(indexRel, NoLock);
 }
 
+/*
+ * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
+ */
+static void
+ATExecEnableRowSecurity(Relation rel)
+{
+       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))->relrowsecurity = true;
+       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);
+}
+
+static void
+ATExecDisableRowSecurity(Relation rel)
+{
+       Relation        pg_class;
+       Oid                     relid;
+       HeapTuple       tuple;
+
+       relid = RelationGetRelid(rel);
+
+       /* Pull the record for this relation and update it */
+       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))->relrowsecurity = false;
+       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 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 (...)
  */
@@ -10306,11 +11225,125 @@ ATExecGenericOptions(Relation rel, List *options)
        heap_freetuple(tuple);
 }
 
+/*
+ * Preparation phase for SET LOGGED/UNLOGGED
+ *
+ * This verifies that we're not trying to change a temp table.  Also,
+ * existing foreign key constraints are checked to avoid ending up with
+ * permanent tables referencing unlogged tables.
+ *
+ * Return value is false if the operation is a no-op (in which case the
+ * checks are skipped), otherwise true.
+ */
+static bool
+ATPrepChangePersistence(Relation rel, bool toLogged)
+{
+       Relation        pg_constraint;
+       HeapTuple       tuple;
+       SysScanDesc scan;
+       ScanKeyData skey[1];
+
+       /*
+        * Disallow changing status for a temp table.  Also verify whether we can
+        * get away with doing nothing; in such cases we don't need to run the
+        * checks below, either.
+        */
+       switch (rel->rd_rel->relpersistence)
+       {
+               case RELPERSISTENCE_TEMP:
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                        errmsg("cannot change logged status of table \"%s\" because it is temporary",
+                                                       RelationGetRelationName(rel)),
+                                        errtable(rel)));
+                       break;
+               case RELPERSISTENCE_PERMANENT:
+                       if (toLogged)
+                               /* nothing to do */
+                               return false;
+                       break;
+               case RELPERSISTENCE_UNLOGGED:
+                       if (!toLogged)
+                               /* nothing to do */
+                               return false;
+                       break;
+       }
+
+       /*
+        * Check existing foreign key constraints to preserve the invariant that
+        * permanent tables cannot reference unlogged ones.  Self-referencing
+        * foreign keys can safely be ignored.
+        */
+       pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
+
+       /*
+        * Scan conrelid if changing to permanent, else confrelid.  This also
+        * determines whether a useful index exists.
+        */
+       ScanKeyInit(&skey[0],
+                               toLogged ? Anum_pg_constraint_conrelid :
+                               Anum_pg_constraint_confrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetRelid(rel)));
+       scan = systable_beginscan(pg_constraint,
+                                                         toLogged ? ConstraintRelidIndexId : InvalidOid,
+                                                         true, NULL, 1, skey);
+
+       while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+       {
+               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+               if (con->contype == CONSTRAINT_FOREIGN)
+               {
+                       Oid                     foreignrelid;
+                       Relation        foreignrel;
+
+                       /* the opposite end of what we used as scankey */
+                       foreignrelid = toLogged ? con->confrelid : con->conrelid;
+
+                       /* ignore if self-referencing */
+                       if (RelationGetRelid(rel) == foreignrelid)
+                               continue;
+
+                       foreignrel = relation_open(foreignrelid, AccessShareLock);
+
+                       if (toLogged)
+                       {
+                               if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                        errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
+                                                                       RelationGetRelationName(rel),
+                                                                       RelationGetRelationName(foreignrel)),
+                                                        errtableconstraint(rel, NameStr(con->conname))));
+                       }
+                       else
+                       {
+                               if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                        errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
+                                                                       RelationGetRelationName(rel),
+                                                                       RelationGetRelationName(foreignrel)),
+                                                        errtableconstraint(rel, NameStr(con->conname))));
+                       }
+
+                       relation_close(foreignrel, AccessShareLock);
+               }
+       }
+
+       systable_endscan(scan);
+
+       heap_close(pg_constraint, AccessShareLock);
+
+       return true;
+}
+
 /*
  * Execute ALTER TABLE SET SCHEMA
  */
-Oid
-AlterTableNamespace(AlterObjectSchemaStmt *stmt)
+ObjectAddress
+AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
 {
        Relation        rel;
        Oid                     relid;
@@ -10318,6 +11351,7 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt)
        Oid                     nspOid;
        RangeVar   *newrv;
        ObjectAddresses *objsMoved;
+       ObjectAddress myself;
 
        relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
                                                                         stmt->missing_ok, false,
@@ -10329,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);
@@ -10356,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;
 }
 
 /*
@@ -10419,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))
@@ -10432,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),
@@ -10460,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);
@@ -10769,7 +11814,7 @@ AtEOXact_on_commit_actions(bool isCommit)
  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
  *
  * During subabort, we can immediately remove entries created during this
- * subtransaction.     During subcommit, just relabel entries marked during
+ * subtransaction.  During subcommit, just relabel entries marked during
  * this subtransaction as being the parent's responsibility.
  */
 void
@@ -10813,7 +11858,7 @@ AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
  * the relation to be locked only if (1) it's a plain table, materialized
  * view, or TOAST table and (2) the current user is the owner (or the
- * superuser). This meets the permission-checking needs of CLUSTER, REINDEX
+ * superuser).  This meets the permission-checking needs of CLUSTER, REINDEX
  * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
  * used by all.
  */
@@ -10830,7 +11875,7 @@ RangeVarCallbackOwnsTable(const RangeVar *relation,
        /*
         * If the relation does exist, check whether it's an index.  But note that
         * the relation might have been dropped between the time we did the name
-        * lookup and now.      In that case, there's nothing to do.
+        * lookup and now.  In that case, there's nothing to do.
         */
        relkind = get_rel_relkind(relId);
        if (!relkind)
@@ -10846,6 +11891,38 @@ RangeVarCallbackOwnsTable(const RangeVar *relation,
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
 }
 
+/*
+ * Callback to RangeVarGetRelidExtended(), similar to
+ * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
+ */
+void
+RangeVarCallbackOwnsRelation(const RangeVar *relation,
+                                                        Oid relId, Oid oldRelId, void *arg)
+{
+       HeapTuple       tuple;
+
+       /* Nothing to do if the relation was not found. */
+       if (!OidIsValid(relId))
+               return;
+
+       tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
+       if (!HeapTupleIsValid(tuple))           /* should not happen */
+               elog(ERROR, "cache lookup failed for relation %u", relId);
+
+       if (!pg_class_ownercheck(relId, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                                          relation->relname);
+
+       if (!allowSystemTableMods &&
+               IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("permission denied: \"%s\" is a system catalog",
+                                               relation->relname)));
+
+       ReleaseSysCache(tuple);
+}
+
 /*
  * Common RangeVarGetRelid callback for rename, set schema, and alter table
  * processing.
@@ -10964,8 +12041,8 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
                relkind != RELKIND_FOREIGN_TABLE)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                       errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
-                                  rv->relname)));
+                                errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
+                                               rv->relname)));
 
        ReleaseSysCache(tuple);
 }