]> granicus.if.org Git - postgresql/commitdiff
Reduce lock levels of CREATE TRIGGER and some ALTER TABLE, CREATE RULE actions.
authorSimon Riggs <simon@2ndQuadrant.com>
Wed, 28 Jul 2010 05:22:24 +0000 (05:22 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Wed, 28 Jul 2010 05:22:24 +0000 (05:22 +0000)
Avoid hard-coding lockmode used for many altering DDL commands, allowing easier
future changes of lock levels. Implementation of initial analysis on DDL
sub-commands, so that many lock levels are now at ShareUpdateExclusiveLock or
ShareRowExclusiveLock, allowing certain DDL not to block reads/writes.
First of number of planned changes in this area; additional docs required
when full project complete.

doc/src/sgml/mvcc.sgml
src/backend/catalog/pg_shdepend.c
src/backend/commands/alter.c
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/commands/typecmds.c
src/backend/parser/parse_utilcmd.c
src/backend/rewrite/rewriteDefine.c
src/backend/utils/adt/ri_triggers.c
src/include/commands/tablecmds.h

index 341351e46ff4b603f3fd2cafc55b1c1e08a78b54..e72a32c58eec16ac00a411d2a145674094e1f5f5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/mvcc.sgml,v 2.75 2010/05/03 15:35:30 alvherre Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/mvcc.sgml,v 2.76 2010/07/28 05:22:24 sriggs Exp $ -->
 
  <chapter id="mvcc">
   <title>Concurrency Control</title>
@@ -532,7 +532,7 @@ SELECT SUM(value) FROM mytab WHERE class = 2;
     most <productname>PostgreSQL</productname> commands automatically
     acquire locks of appropriate modes to ensure that referenced
     tables are not dropped or modified in incompatible ways while the
-    command executes.  (For example, <command>ALTER TABLE</> cannot safely be
+    command executes.  (For example, <command>TRUNCATE</> cannot safely be
     executed concurrently with other operations on the same table, so it
     obtains an exclusive lock on the table to enforce that.)
    </para>
@@ -695,8 +695,9 @@ SELECT SUM(value) FROM mytab WHERE class = 2;
         </para>
 
         <para>
-         This lock mode is not automatically acquired by any
-         <productname>PostgreSQL</productname> command.
+         Acquired by <command>CREATE TRIGGER</command>,
+         <command>CREATE RULE</command> (except for <literal>ON SELECT</>
+         rules) and in some cases <command>ALTER TABLE</command>.
         </para>
        </listitem>
       </varlistentry>
@@ -742,11 +743,12 @@ SELECT SUM(value) FROM mytab WHERE class = 2;
         </para>
 
         <para>
-         Acquired by the <command>ALTER TABLE</command>, <command>DROP
-         TABLE</command>, <command>TRUNCATE</command>, <command>REINDEX</command>,
+         Acquired by the <command>DROP TABLE</command>,
+         <command>TRUNCATE</command>, <command>REINDEX</command>,
          <command>CLUSTER</command>, and <command>VACUUM FULL</command>
-         commands.  This is also the default lock mode for <command>LOCK
-         TABLE</command> statements that do not specify a mode explicitly.
+         commands, as well as most variants of <command>ALTER TABLE</>.
+         This is also the default lock mode for <command>LOCK TABLE</command>
+         statements that do not specify a mode explicitly.
         </para>
        </listitem>
       </varlistentry>
index e525a551ffeac70981c0173a4f3627aa75aafbf9..a03fc0d4e2e3037e4bc22ce996966d944a198444 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.43 2010/07/06 19:18:56 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.44 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1346,7 +1346,7 @@ shdepReassignOwned(List *roleids, Oid newrole)
                                         * owned sequences, etc when we happen to visit them
                                         * before their parent table.
                                         */
-                                       ATExecChangeOwner(sdepForm->objid, newrole, true);
+                                       ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
                                        break;
 
                                case ProcedureRelationId:
index cecdfa5c6d4a4434f0dd005be276487fc335c9b4..edf05b6eaa3430c262149f54998720f9ded3c8c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.36 2010/06/13 17:43:12 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.37 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -190,7 +190,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
                case OBJECT_VIEW:
                        CheckRelationOwnership(stmt->relation, true);
                        AlterTableNamespace(stmt->relation, stmt->newschema,
-                                                               stmt->objectType);
+                                                               stmt->objectType, AccessExclusiveLock);
                        break;
 
                case OBJECT_TYPE:
index 2fdce989c01deac5f8ee9c646a1a56dfcb9f65ff..1496b6eb1598aa7bc180f58ea62d981c53585d5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.334 2010/07/25 23:21:21 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.335 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,6 +64,7 @@
 #include "rewrite/rewriteHandler.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
+#include "storage/lock.h"
 #include "storage/smgr.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -237,7 +238,7 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
                                         Oid oldNspOid, Oid newNspOid);
 static void AlterSeqNamespaces(Relation classRel, Relation rel,
                                   Oid oldNspOid, Oid newNspOid,
-                                  const char *newNspName);
+                                  const char *newNspName, LOCKMODE lockmode);
 static int transformColumnNameList(Oid relId, List *colList,
                                                int16 *attnums, Oid *atttypids);
 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
@@ -253,84 +254,84 @@ static void validateForeignKeyConstraint(Constraint *fkconstraint,
                                                         Oid pkindOid, Oid constraintOid);
 static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
                                                 Oid constraintOid, Oid indexOid);
-static void ATController(Relation rel, List *cmds, bool recurse);
+static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
-                 bool recurse, bool recursing);
-static void ATRewriteCatalogs(List **wqueue);
+                 bool recurse, bool recursing, LOCKMODE lockmode);
+static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
-                 AlterTableCmd *cmd);
-static void ATRewriteTables(List **wqueue);
-static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
+                 AlterTableCmd *cmd, LOCKMODE lockmode);
+static void ATRewriteTables(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, bool allowView);
 static void ATSimplePermissionsRelationOrIndex(Relation rel);
 static void ATSimpleRecursion(List **wqueue, Relation rel,
-                                 AlterTableCmd *cmd, bool recurse);
+                                 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
 static void ATOneLevelRecursion(List **wqueue, Relation rel,
-                                       AlterTableCmd *cmd);
+                                       AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
-                               AlterTableCmd *cmd);
+                               AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
-                               ColumnDef *colDef, bool isOid);
+                               ColumnDef *colDef, bool isOid, LOCKMODE lockmode);
 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
 static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
-                         AlterTableCmd *cmd);
-static void ATExecDropNotNull(Relation rel, const char *colName);
+                         AlterTableCmd *cmd, LOCKMODE lockmode);
+static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
 static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
-                                const char *colName);
+                                const char *colName, LOCKMODE lockmode);
 static void ATExecColumnDefault(Relation rel, const char *colName,
-                                       Node *newDefault);
+                                       Node *newDefault, LOCKMODE lockmode);
 static void ATPrepSetStatistics(Relation rel, const char *colName,
-                                       Node *newValue);
+                                       Node *newValue, LOCKMODE lockmode);
 static void ATExecSetStatistics(Relation rel, const char *colName,
-                                       Node *newValue);
+                                       Node *newValue, LOCKMODE lockmode);
 static void ATExecSetOptions(Relation rel, const char *colName,
-                                Node *options, bool isReset);
+                                Node *options, bool isReset, LOCKMODE lockmode);
 static void ATExecSetStorage(Relation rel, const char *colName,
-                                Node *newValue);
+                                Node *newValue, LOCKMODE lockmode);
 static void ATPrepDropColumn(Relation rel, bool recurse, AlterTableCmd *cmd);
 static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
                                 bool recurse, bool recursing,
-                                bool missing_ok);
+                                bool missing_ok, LOCKMODE lockmode);
 static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
-                          IndexStmt *stmt, bool is_rebuild);
+                          IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
 static void ATExecAddConstraint(List **wqueue,
                                        AlteredTableInfo *tab, Relation rel,
-                                       Constraint *newConstraint, bool recurse);
+                                       Constraint *newConstraint, bool recurse, LOCKMODE lockmode);
 static void ATAddCheckConstraint(List **wqueue,
                                         AlteredTableInfo *tab, Relation rel,
                                         Constraint *constr,
-                                        bool recurse, bool recursing);
+                                        bool recurse, bool recursing, LOCKMODE lockmode);
 static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
-                                                 Constraint *fkconstraint);
+                                                 Constraint *fkconstraint, LOCKMODE lockmode);
 static void ATExecDropConstraint(Relation rel, const char *constrName,
                                         DropBehavior behavior,
                                         bool recurse, bool recursing,
-                                        bool missing_ok);
+                                        bool missing_ok, LOCKMODE lockmode);
 static void ATPrepAlterColumnType(List **wqueue,
                                          AlteredTableInfo *tab, Relation rel,
                                          bool recurse, bool recursing,
-                                         AlterTableCmd *cmd);
+                                         AlterTableCmd *cmd, LOCKMODE lockmode);
 static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-                                         const char *colName, TypeName *typeName);
-static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
-static void ATPostAlterTypeParse(char *cmd, List **wqueue);
+                                         const char *colName, TypeName *typeName, LOCKMODE lockmode);
+static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
+static void ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode);
 static void change_owner_recurse_to_sequences(Oid relationOid,
-                                                                 Oid newOwnerId);
-static void ATExecClusterOn(Relation rel, const char *indexName);
-static void ATExecDropCluster(Relation rel);
+                                                                 Oid newOwnerId, LOCKMODE lockmode);
+static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
+static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
-                                       char *tablespacename);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace);
-static void ATExecSetRelOptions(Relation rel, List *defList, bool isReset);
+                                       char *tablespacename, LOCKMODE lockmode);
+static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+static void ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode);
 static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
-                                                  char fires_when, bool skip_system);
+                                                  char fires_when, bool skip_system, LOCKMODE lockmode);
 static void ATExecEnableDisableRule(Relation rel, char *rulename,
-                                               char fires_when);
+                                               char fires_when, LOCKMODE lockmode);
 static void ATPrepAddInherit(Relation child_rel);
-static void ATExecAddInherit(Relation child_rel, RangeVar *parent);
-static void ATExecDropInherit(Relation rel, RangeVar *parent);
+static void ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
+static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
                                   ForkNumber forkNum, bool istemp);
 static const char *storage_name(char c);
@@ -2249,8 +2250,8 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId)
  * Disallow ALTER TABLE (and similar commands) when the current backend has
  * any open reference to the target table besides the one just acquired by
  * the calling command; this implies there's an open cursor or active plan.
- * We need this check because our AccessExclusiveLock doesn't protect us
- * against stomping on our own foot, only other people's feet!
+ * We need this check because our lock doesn't protect us against stomping
+ * on our own foot, only other people's feet!
  *
  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
@@ -2327,11 +2328,27 @@ CheckTableNotInUse(Relation rel, const char *stmt)
  *
  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
  * the whole operation; we don't have to do anything special to clean up.
+ *
+ * We lock the table as the first action, 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. If all subcommands do not require rewrite table
+ * then we may be able to use lower lock levels. 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 may well be higher than required for
+ * that specific subcommand. So we pass down the overall lock requirement,
+ * rather than reassess it at lower levels.
  */
 void
 AlterTable(AlterTableStmt *stmt)
 {
-       Relation        rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+       Relation        rel;
+       LOCKMODE lockmode = AlterTableGetLockLevel(stmt->cmds);
+
+       /*
+        * Acquire same level of lock as already acquired during parsing.
+        */
+       rel = relation_openrv(stmt->relation, lockmode);
 
        CheckTableNotInUse(rel, "ALTER TABLE");
 
@@ -2374,7 +2391,8 @@ AlterTable(AlterTableStmt *stmt)
                        elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
        }
 
-       ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt));
+       ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt),
+                                               lockmode);
 }
 
 /*
@@ -2391,13 +2409,169 @@ AlterTable(AlterTableStmt *stmt)
 void
 AlterTableInternal(Oid relid, List *cmds, bool recurse)
 {
-       Relation        rel = relation_open(relid, AccessExclusiveLock);
+       Relation        rel;
+       LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
 
-       ATController(rel, cmds, recurse);
+       rel = relation_open(relid, lockmode);
+
+       ATController(rel, cmds, recurse, lockmode);
+}
+
+/*
+ * AlterTableGetLockLevel
+ *
+ * Sets the overall lock level required for the supplied list of subcommands.
+ * Policy for doing this set according to needs of AlterTable(), see
+ * comments there for overall explanation.
+ *
+ * Function is called before and after parsing, so it must give same
+ * answer each time it is called. Some subcommands are transformed
+ * into other subcommand types, so the transform must never be made to a
+ * lower lock level than previously assigned. All transforms are noted below.
+ *
+ * Since this is called before we lock the table we cannot use table metadata
+ * to influence the type of lock we acquire.
+ *
+ * There should be no lockmodes hardcoded into the subcommand functions. All
+ * lockmode decisions for ALTER TABLE are made here only. The one exception is
+ * 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.
+ */
+LOCKMODE
+AlterTableGetLockLevel(List *cmds)
+{
+       ListCell   *lcmd;
+       LOCKMODE lockmode = ShareUpdateExclusiveLock;
+
+       foreach(lcmd, cmds)
+       {
+               AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+               LOCKMODE cmd_lockmode  = AccessExclusiveLock; /* default for compiler */
+
+               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.
+                        */
+                       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_AlterColumnType:        /* must rewrite heap */
+                       case AT_DropConstraint:         /* as DROP INDEX */
+                       case AT_AddOids:                        /* must rewrite heap */
+                       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 */
+                       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;
+
+                       /*
+                        * These subcommands affect write operations only.
+                        */
+                       case AT_ColumnDefault:
+                       case AT_ProcessedConstraint:    /* becomes AT_AddConstraint */
+                       case AT_AddConstraintRecurse:   /* becomes AT_AddConstraint */
+                       case AT_EnableTrig:
+                       case AT_EnableAlwaysTrig:
+                       case AT_EnableReplicaTrig:
+                       case AT_EnableTrigAll:
+                       case AT_EnableTrigUser:
+                       case AT_DisableTrig:
+                       case AT_DisableTrigAll:
+                       case AT_DisableTrigUser:
+                       case AT_AddIndex:                               /* from ADD CONSTRAINT */
+                               cmd_lockmode = ShareRowExclusiveLock;
+                               break;
+
+                       case AT_AddConstraint:
+                               if (IsA(cmd->def, Constraint))
+                               {
+                                       Constraint *con = (Constraint *) cmd->def;
+
+                                       switch (con->contype)
+                                       {
+                                               case CONSTR_EXCLUSION:
+                                               case CONSTR_PRIMARY:
+                                               case CONSTR_UNIQUE:
+                                                       /*
+                                                        * 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.
+                                                        */
+                                                       cmd_lockmode = ShareRowExclusiveLock;
+                                                       break;
+                                               case CONSTR_FOREIGN:
+                                                       /*
+                                                        * We add triggers to both tables when we add a
+                                                        * Foreign Key, so the lock level must be at least
+                                                        * as strong as CREATE TRIGGER.
+                                                        */
+                                                       cmd_lockmode = ShareRowExclusiveLock;
+                                                       break;
+
+                                               default:
+                                                       cmd_lockmode = ShareRowExclusiveLock;
+                                       }
+                               }
+                               break;
+
+                       /*
+                        * These subcommands affect inheritance behaviour. Queries 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. In both cases the parent table
+                        * is locked with AccessShareLock.
+                        */
+                       case AT_AddInherit:
+                       case AT_DropInherit:
+                               cmd_lockmode = ShareUpdateExclusiveLock;
+                               break;
+
+                       /*
+                        * These subcommands affect general strategies for performance and maintenance,
+                        * though don't change the semantic results from normal data reads and writes.
+                        * Delaying an ALTER TABLE behind currently active writes only delays the point
+                        * where the new strategy begins to take effect, so there is no benefit in waiting.
+                        * In thise case the minimum restriction 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_SetStorage:
+                               cmd_lockmode = ShareUpdateExclusiveLock;
+                               break;
+
+                       default:                                /* oops */
+                               elog(ERROR, "unrecognized alter table type: %d",
+                                        (int) cmd->subtype);
+                               break;
+               }
+
+               /*
+                * Take the greatest lockmode from any subcommand
+                */
+               if (cmd_lockmode > lockmode)
+                       lockmode = cmd_lockmode;
+       }
+
+       return lockmode;
 }
 
 static void
-ATController(Relation rel, List *cmds, bool recurse)
+ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
 {
        List       *wqueue = NIL;
        ListCell   *lcmd;
@@ -2407,17 +2581,17 @@ ATController(Relation rel, List *cmds, bool recurse)
        {
                AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
 
-               ATPrepCmd(&wqueue, rel, cmd, recurse, false);
+               ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
        }
 
        /* Close the relation, but keep lock until commit */
        relation_close(rel, NoLock);
 
        /* Phase 2: update system catalogs */
-       ATRewriteCatalogs(&wqueue);
+       ATRewriteCatalogs(&wqueue, lockmode);
 
        /* Phase 3: scan/rewrite tables as needed */
-       ATRewriteTables(&wqueue);
+       ATRewriteTables(&wqueue, lockmode);
 }
 
 /*
@@ -2426,12 +2600,12 @@ ATController(Relation rel, List *cmds, bool recurse)
  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
  * recursion and permission checks.
  *
- * Caller must have acquired AccessExclusiveLock on relation already.
+ * Caller must have acquired appropriate lock type on relation already.
  * This lock should be held until commit.
  */
 static void
 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
-                 bool recurse, bool recursing)
+                 bool recurse, bool recursing, LOCKMODE lockmode)
 {
        AlteredTableInfo *tab;
        int                     pass;
@@ -2456,14 +2630,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_AddColumn:              /* ADD COLUMN */
                        ATSimplePermissions(rel, false);
                        /* Performs own recursion */
-                       ATPrepAddColumn(wqueue, rel, recurse, cmd);
+                       ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_AddColumnToView:                /* add column via CREATE OR REPLACE
                                                                                 * VIEW */
                        ATSimplePermissions(rel, true);
                        /* Performs own recursion */
-                       ATPrepAddColumn(wqueue, rel, recurse, cmd);
+                       ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
@@ -2475,26 +2649,26 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                         * rules.
                         */
                        ATSimplePermissions(rel, true);
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
+                       ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
                        break;
                case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
                        ATSimplePermissions(rel, false);
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
+                       ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = AT_PASS_DROP;
                        break;
                case AT_SetNotNull:             /* ALTER COLUMN SET NOT NULL */
                        ATSimplePermissions(rel, false);
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
+                       ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = AT_PASS_ADD_CONSTR;
                        break;
                case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
+                       ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* Performs own permission checks */
-                       ATPrepSetStatistics(rel, cmd->name, cmd->def);
+                       ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
                        pass = AT_PASS_COL_ATTRS;
                        break;
                case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
@@ -2505,7 +2679,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        break;
                case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
                        ATSimplePermissions(rel, false);
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
+                       ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = AT_PASS_COL_ATTRS;
                        break;
@@ -2540,7 +2714,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
                        ATSimplePermissions(rel, false);
                        /* Performs own recursion */
-                       ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd);
+                       ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
                        pass = AT_PASS_ALTER_TYPE;
                        break;
                case AT_ChangeOwner:    /* ALTER OWNER */
@@ -2559,7 +2733,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        ATSimplePermissions(rel, false);
                        /* Performs own recursion */
                        if (!rel->rd_rel->relhasoids || recursing)
-                               ATPrepAddOids(wqueue, rel, recurse, cmd);
+                               ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_DropOids:               /* SET WITHOUT OIDS */
@@ -2572,14 +2746,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                                dropCmd->subtype = AT_DropColumn;
                                dropCmd->name = pstrdup("oid");
                                dropCmd->behavior = cmd->behavior;
-                               ATPrepCmd(wqueue, rel, dropCmd, recurse, false);
+                               ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
                        }
                        pass = AT_PASS_DROP;
                        break;
                case AT_SetTableSpace:  /* SET TABLESPACE */
                        ATSimplePermissionsRelationOrIndex(rel);
                        /* This command never recurses */
-                       ATPrepSetTableSpace(tab, rel, cmd->name);
+                       ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
                        pass = AT_PASS_MISC;    /* doesn't actually matter */
                        break;
                case AT_SetRelOptions:  /* SET (...) */
@@ -2632,7 +2806,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  * conflicts).
  */
 static void
-ATRewriteCatalogs(List **wqueue)
+ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 {
        int                     pass;
        ListCell   *ltab;
@@ -2658,12 +2832,12 @@ ATRewriteCatalogs(List **wqueue)
                                continue;
 
                        /*
-                        * Exclusive lock was obtained by phase 1, needn't get it again
+                        * Appropriate lock was obtained by phase 1, needn't get it again
                         */
                        rel = relation_open(tab->relid, NoLock);
 
                        foreach(lcmd, subcmds)
-                               ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd));
+                               ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
 
                        /*
                         * After the ALTER TYPE pass, do cleanup work (this is not done in
@@ -2671,7 +2845,7 @@ ATRewriteCatalogs(List **wqueue)
                         * multiple columns of a table are altered).
                         */
                        if (pass == AT_PASS_ALTER_TYPE)
-                               ATPostAlterTypeCleanup(wqueue, tab);
+                               ATPostAlterTypeCleanup(wqueue, tab, lockmode);
 
                        relation_close(rel, NoLock);
                }
@@ -2698,86 +2872,86 @@ ATRewriteCatalogs(List **wqueue)
  */
 static void
 ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
-                 AlterTableCmd *cmd)
+                 AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        switch (cmd->subtype)
        {
                case AT_AddColumn:              /* ADD COLUMN */
                case AT_AddColumnToView:                /* add column via CREATE OR REPLACE
                                                                                 * VIEW */
-                       ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false);
+                       ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, lockmode);
                        break;
                case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
-                       ATExecColumnDefault(rel, cmd->name, cmd->def);
+                       ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
                        break;
                case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
-                       ATExecDropNotNull(rel, cmd->name);
+                       ATExecDropNotNull(rel, cmd->name, lockmode);
                        break;
                case AT_SetNotNull:             /* ALTER COLUMN SET NOT NULL */
-                       ATExecSetNotNull(tab, rel, cmd->name);
+                       ATExecSetNotNull(tab, rel, cmd->name, lockmode);
                        break;
                case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
-                       ATExecSetStatistics(rel, cmd->name, cmd->def);
+                       ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
                        break;
                case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
-                       ATExecSetOptions(rel, cmd->name, cmd->def, false);
+                       ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
                        break;
                case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
-                       ATExecSetOptions(rel, cmd->name, cmd->def, true);
+                       ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
                        break;
                case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
-                       ATExecSetStorage(rel, cmd->name, cmd->def);
+                       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);
+                                                        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);
+                                                        cmd->behavior, true, false, cmd->missing_ok, lockmode);
                        break;
                case AT_AddIndex:               /* ADD INDEX */
-                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false);
+                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false, lockmode);
                        break;
                case AT_ReAddIndex:             /* ADD INDEX */
-                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true);
+                       ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true, lockmode);
                        break;
                case AT_AddConstraint:  /* ADD CONSTRAINT */
                        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);
+                                                               true, lockmode);
                        break;
                case AT_DropConstraint: /* DROP CONSTRAINT */
                        ATExecDropConstraint(rel, cmd->name, cmd->behavior,
                                                                 false, false,
-                                                                cmd->missing_ok);
+                                                                cmd->missing_ok, lockmode);
                        break;
                case AT_DropConstraintRecurse:  /* DROP CONSTRAINT with recursion */
                        ATExecDropConstraint(rel, cmd->name, cmd->behavior,
                                                                 true, false,
-                                                                cmd->missing_ok);
+                                                                cmd->missing_ok, lockmode);
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
-                       ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
+                       ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def, lockmode);
                        break;
                case AT_ChangeOwner:    /* ALTER OWNER */
                        ATExecChangeOwner(RelationGetRelid(rel),
                                                          get_roleid_checked(cmd->name),
-                                                         false);
+                                                         false, lockmode);
                        break;
                case AT_ClusterOn:              /* CLUSTER ON */
-                       ATExecClusterOn(rel, cmd->name);
+                       ATExecClusterOn(rel, cmd->name, lockmode);
                        break;
                case AT_DropCluster:    /* SET WITHOUT CLUSTER */
-                       ATExecDropCluster(rel);
+                       ATExecDropCluster(rel, lockmode);
                        break;
                case AT_AddOids:                /* SET WITH OIDS */
                        /* Use the ADD COLUMN code, unless prep decided to do nothing */
                        if (cmd->def != NULL)
-                               ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true);
+                               ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, lockmode);
                        break;
                case AT_DropOids:               /* SET WITHOUT OIDS */
 
@@ -2793,67 +2967,67 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                         */
                        break;
                case AT_SetRelOptions:  /* SET (...) */
-                       ATExecSetRelOptions(rel, (List *) cmd->def, false);
+                       ATExecSetRelOptions(rel, (List *) cmd->def, false, lockmode);
                        break;
                case AT_ResetRelOptions:                /* RESET (...) */
-                       ATExecSetRelOptions(rel, (List *) cmd->def, true);
+                       ATExecSetRelOptions(rel, (List *) cmd->def, true, lockmode);
                        break;
 
                case AT_EnableTrig:             /* ENABLE TRIGGER name */
                        ATExecEnableDisableTrigger(rel, cmd->name,
-                                                                          TRIGGER_FIRES_ON_ORIGIN, false);
+                                                                          TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
                        break;
                case AT_EnableAlwaysTrig:               /* ENABLE ALWAYS TRIGGER name */
                        ATExecEnableDisableTrigger(rel, cmd->name,
-                                                                          TRIGGER_FIRES_ALWAYS, false);
+                                                                          TRIGGER_FIRES_ALWAYS, false, lockmode);
                        break;
                case AT_EnableReplicaTrig:              /* ENABLE REPLICA TRIGGER name */
                        ATExecEnableDisableTrigger(rel, cmd->name,
-                                                                          TRIGGER_FIRES_ON_REPLICA, false);
+                                                                          TRIGGER_FIRES_ON_REPLICA, false, lockmode);
                        break;
                case AT_DisableTrig:    /* DISABLE TRIGGER name */
                        ATExecEnableDisableTrigger(rel, cmd->name,
-                                                                          TRIGGER_DISABLED, false);
+                                                                          TRIGGER_DISABLED, false, lockmode);
                        break;
                case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
                        ATExecEnableDisableTrigger(rel, NULL,
-                                                                          TRIGGER_FIRES_ON_ORIGIN, false);
+                                                                          TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
                        break;
                case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
                        ATExecEnableDisableTrigger(rel, NULL,
-                                                                          TRIGGER_DISABLED, false);
+                                                                          TRIGGER_DISABLED, false, lockmode);
                        break;
                case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
                        ATExecEnableDisableTrigger(rel, NULL,
-                                                                          TRIGGER_FIRES_ON_ORIGIN, true);
+                                                                          TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
                        break;
                case AT_DisableTrigUser:                /* DISABLE TRIGGER USER */
                        ATExecEnableDisableTrigger(rel, NULL,
-                                                                          TRIGGER_DISABLED, true);
+                                                                          TRIGGER_DISABLED, true, lockmode);
                        break;
 
                case AT_EnableRule:             /* ENABLE RULE name */
                        ATExecEnableDisableRule(rel, cmd->name,
-                                                                       RULE_FIRES_ON_ORIGIN);
+                                                                       RULE_FIRES_ON_ORIGIN, lockmode);
                        break;
                case AT_EnableAlwaysRule:               /* ENABLE ALWAYS RULE name */
                        ATExecEnableDisableRule(rel, cmd->name,
-                                                                       RULE_FIRES_ALWAYS);
+                                                                       RULE_FIRES_ALWAYS, lockmode);
                        break;
                case AT_EnableReplicaRule:              /* ENABLE REPLICA RULE name */
                        ATExecEnableDisableRule(rel, cmd->name,
-                                                                       RULE_FIRES_ON_REPLICA);
+                                                                       RULE_FIRES_ON_REPLICA, lockmode);
                        break;
                case AT_DisableRule:    /* DISABLE RULE name */
                        ATExecEnableDisableRule(rel, cmd->name,
-                                                                       RULE_DISABLED);
+                                                                       RULE_DISABLED, lockmode);
                        break;
 
                case AT_AddInherit:
-                       ATExecAddInherit(rel, (RangeVar *) cmd->def);
+                       ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
                        break;
                case AT_DropInherit:
-                       ATExecDropInherit(rel, (RangeVar *) cmd->def);
+                       ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
                        break;
                default:                                /* oops */
                        elog(ERROR, "unrecognized alter table type: %d",
@@ -2872,7 +3046,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
  * ATRewriteTables: ALTER TABLE phase 3
  */
 static void
-ATRewriteTables(List **wqueue)
+ATRewriteTables(List **wqueue, LOCKMODE lockmode)
 {
        ListCell   *ltab;
 
@@ -2933,7 +3107,7 @@ ATRewriteTables(List **wqueue)
                         * modifications, and test the current data within the table
                         * against new constraints generated by ALTER TABLE commands.
                         */
-                       ATRewriteTable(tab, OIDNewHeap);
+                       ATRewriteTable(tab, OIDNewHeap, lockmode);
 
                        /*
                         * Swap the physical files of the old and new heaps, then rebuild
@@ -2952,14 +3126,14 @@ ATRewriteTables(List **wqueue)
                         * generated by ALTER TABLE commands, but don't rebuild data.
                         */
                        if (tab->constraints != NIL || tab->new_notnull)
-                               ATRewriteTable(tab, InvalidOid);
+                               ATRewriteTable(tab, InvalidOid, lockmode);
 
                        /*
                         * If we had SET TABLESPACE but no reason to reconstruct tuples,
                         * just do a block-by-block copy.
                         */
                        if (tab->newTableSpace)
-                               ATExecSetTableSpace(tab->relid, tab->newTableSpace);
+                               ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
                }
        }
 
@@ -2986,12 +3160,14 @@ ATRewriteTables(List **wqueue)
                                Relation        refrel;
 
                                if (rel == NULL)
-                               {
                                        /* Long since locked, no need for another */
                                        rel = heap_open(tab->relid, NoLock);
-                               }
 
-                               refrel = heap_open(con->refrelid, RowShareLock);
+                               /*
+                                * We're adding a trigger to both tables, so the lock level
+                                * here should sensibly reflect that.
+                                */
+                               refrel = heap_open(con->refrelid, ShareRowExclusiveLock);
 
                                validateForeignKeyConstraint(fkconstraint, rel, refrel,
                                                                                         con->refindid,
@@ -3012,7 +3188,7 @@ ATRewriteTables(List **wqueue)
  * OIDNewHeap is InvalidOid if we don't need to rewrite
  */
 static void
-ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
+ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 {
        Relation        oldrel;
        Relation        newrel;
@@ -3036,7 +3212,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
        newTupDesc = RelationGetDescr(oldrel);          /* includes all mods */
 
        if (OidIsValid(OIDNewHeap))
-               newrel = heap_open(OIDNewHeap, AccessExclusiveLock);
+               newrel = heap_open(OIDNewHeap, lockmode);
        else
                newrel = NULL;
 
@@ -3402,7 +3578,7 @@ ATSimplePermissionsRelationOrIndex(Relation rel)
  */
 static void
 ATSimpleRecursion(List **wqueue, Relation rel,
-                                 AlterTableCmd *cmd, bool recurse)
+                                 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
 {
        /*
         * Propagate to children if desired.  Non-table relations never have
@@ -3414,7 +3590,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
                ListCell   *child;
                List       *children;
 
-               children = find_all_inheritors(relid, AccessExclusiveLock, NULL);
+               children = find_all_inheritors(relid, lockmode, NULL);
 
                /*
                 * find_all_inheritors does the recursive search of the inheritance
@@ -3431,7 +3607,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
                        /* find_all_inheritors already got lock */
                        childrel = relation_open(childrelid, NoLock);
                        CheckTableNotInUse(childrel, "ALTER TABLE");
-                       ATPrepCmd(wqueue, childrel, cmd, false, true);
+                       ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
                        relation_close(childrel, NoLock);
                }
        }
@@ -3447,13 +3623,13 @@ ATSimpleRecursion(List **wqueue, Relation rel,
  */
 static void
 ATOneLevelRecursion(List **wqueue, Relation rel,
-                                       AlterTableCmd *cmd)
+                                       AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        Oid                     relid = RelationGetRelid(rel);
        ListCell   *child;
        List       *children;
 
-       children = find_inheritance_children(relid, AccessExclusiveLock);
+       children = find_inheritance_children(relid, lockmode);
 
        foreach(child, children)
        {
@@ -3463,7 +3639,7 @@ ATOneLevelRecursion(List **wqueue, Relation rel,
                /* find_inheritance_children already got lock */
                childrel = relation_open(childrelid, NoLock);
                CheckTableNotInUse(childrel, "ALTER TABLE");
-               ATPrepCmd(wqueue, childrel, cmd, true, true);
+               ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
                relation_close(childrel, NoLock);
        }
 }
@@ -3581,7 +3757,7 @@ find_composite_type_dependencies(Oid typeOid,
  */
 static void
 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
-                               AlterTableCmd *cmd)
+                               AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        if (rel->rd_rel->reloftype)
                ereport(ERROR,
@@ -3604,7 +3780,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
                colDefChild->inhcount = 1;
                colDefChild->is_local = false;
 
-               ATOneLevelRecursion(wqueue, rel, childCmd);
+               ATOneLevelRecursion(wqueue, rel, childCmd, lockmode);
        }
        else
        {
@@ -3621,7 +3797,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
 
 static void
 ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
-                               ColumnDef *colDef, bool isOid)
+                               ColumnDef *colDef, bool isOid, LOCKMODE lockmode)
 {
        Oid                     myrelid = RelationGetRelid(rel);
        Relation        pgclass,
@@ -3902,7 +4078,7 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
  * to cons up a ColumnDef node because the ADD COLUMN code needs one.
  */
 static void
-ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
+ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        /* If we're recursing to a child table, the ColumnDef is already set up */
        if (cmd->def == NULL)
@@ -3917,14 +4093,14 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
                cdef->storage = 0;
                cmd->def = (Node *) cdef;
        }
-       ATPrepAddColumn(wqueue, rel, recurse, cmd);
+       ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
 }
 
 /*
  * ALTER TABLE ALTER COLUMN DROP NOT NULL
  */
 static void
-ATExecDropNotNull(Relation rel, const char *colName)
+ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
 {
        HeapTuple       tuple;
        AttrNumber      attnum;
@@ -4016,7 +4192,7 @@ ATExecDropNotNull(Relation rel, const char *colName)
  */
 static void
 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
-                                const char *colName)
+                                const char *colName, LOCKMODE lockmode)
 {
        HeapTuple       tuple;
        AttrNumber      attnum;
@@ -4068,7 +4244,7 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
  */
 static void
 ATExecColumnDefault(Relation rel, const char *colName,
-                                       Node *newDefault)
+                                       Node *newDefault, LOCKMODE lockmode)
 {
        AttrNumber      attnum;
 
@@ -4117,7 +4293,7 @@ ATExecColumnDefault(Relation rel, const char *colName,
  * ALTER TABLE ALTER COLUMN SET STATISTICS
  */
 static void
-ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue)
+ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 {
        /*
         * We do our own permission checking because (a) we want to allow SET
@@ -4139,7 +4315,7 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue)
 }
 
 static void
-ATExecSetStatistics(Relation rel, const char *colName, Node *newValue)
+ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 {
        int                     newtarget;
        Relation        attrelation;
@@ -4199,7 +4375,7 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue)
 
 static void
 ATExecSetOptions(Relation rel, const char *colName, Node *options,
-                                bool isReset)
+                                bool isReset, LOCKMODE lockmode)
 {
        Relation        attrelation;
        HeapTuple       tuple,
@@ -4263,7 +4439,7 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
  * ALTER TABLE ALTER COLUMN SET STORAGE
  */
 static void
-ATExecSetStorage(Relation rel, const char *colName, Node *newValue)
+ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 {
        char       *storagemode;
        char            newstorage;
@@ -4357,7 +4533,7 @@ static void
 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
                                 bool recurse, bool recursing,
-                                bool missing_ok)
+                                bool missing_ok, LOCKMODE lockmode)
 {
        HeapTuple       tuple;
        Form_pg_attribute targetatt;
@@ -4415,8 +4591,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
         * routines, we have to do this one level of recursion at a time; we can't
         * use find_all_inheritors to do it in one pass.
         */
-       children = find_inheritance_children(RelationGetRelid(rel),
-                                                                                AccessExclusiveLock);
+       children = find_inheritance_children(RelationGetRelid(rel), lockmode);
 
        if (children)
        {
@@ -4456,7 +4631,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                        /* Time to delete this child column, too */
                                        ATExecDropColumn(wqueue, childrel, colName,
                                                                         behavior, true, true,
-                                                                        false);
+                                                                        false, lockmode);
                                }
                                else
                                {
@@ -4551,7 +4726,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
  */
 static void
 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
-                          IndexStmt *stmt, bool is_rebuild)
+                          IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 {
        bool            check_rights;
        bool            skip_build;
@@ -4594,7 +4769,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  */
 static void
 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
-                                       Constraint *newConstraint, bool recurse)
+                                       Constraint *newConstraint, bool recurse, LOCKMODE lockmode)
 {
        Assert(IsA(newConstraint, Constraint));
 
@@ -4607,7 +4782,7 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
        {
                case CONSTR_CHECK:
                        ATAddCheckConstraint(wqueue, tab, rel,
-                                                                newConstraint, recurse, false);
+                                                                newConstraint, recurse, false, lockmode);
                        break;
 
                case CONSTR_FOREIGN:
@@ -4638,7 +4813,7 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                                                                                 RelationGetNamespace(rel),
                                                                                 NIL);
 
-                       ATAddForeignKeyConstraint(tab, rel, newConstraint);
+                       ATAddForeignKeyConstraint(tab, rel, newConstraint, lockmode);
                        break;
 
                default:
@@ -4662,7 +4837,8 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
  */
 static void
 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
-                                        Constraint *constr, bool recurse, bool recursing)
+                                        Constraint *constr, bool recurse, bool recursing,
+                                        LOCKMODE lockmode)
 {
        List       *newcons;
        ListCell   *lcon;
@@ -4717,8 +4893,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
         * routines, we have to do this one level of recursion at a time; we can't
         * use find_all_inheritors to do it in one pass.
         */
-       children = find_inheritance_children(RelationGetRelid(rel),
-                                                                                AccessExclusiveLock);
+       children = find_inheritance_children(RelationGetRelid(rel), lockmode);
 
        /*
         * If we are told not to recurse, there had better not be any child
@@ -4744,7 +4919,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
                /* Recurse to child */
                ATAddCheckConstraint(wqueue, childtab, childrel,
-                                                        constr, recurse, true);
+                                                        constr, recurse, true, lockmode);
 
                heap_close(childrel, NoLock);
        }
@@ -4759,7 +4934,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
  */
 static void
 ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
-                                                 Constraint *fkconstraint)
+                                                 Constraint *fkconstraint, LOCKMODE lockmode)
 {
        Relation        pkrel;
        int16           pkattnum[INDEX_MAX_KEYS];
@@ -4776,14 +4951,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
        Oid                     indexOid;
        Oid                     constrOid;
 
-       /*
-        * 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.)
-        */
-       pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
+       pkrel = heap_openrv(fkconstraint->pktable, lockmode);
 
        /*
         * Validity checks (permission checks wait till we have the column
@@ -5347,7 +5515,7 @@ checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
  * Scan the existing rows in a table to verify they meet a proposed FK
  * constraint.
  *
- * Caller must have opened and locked both relations.
+ * Caller must have opened and locked both relations appropriately.
  */
 static void
 validateForeignKeyConstraint(Constraint *fkconstraint,
@@ -5599,7 +5767,7 @@ static void
 ATExecDropConstraint(Relation rel, const char *constrName,
                                         DropBehavior behavior,
                                         bool recurse, bool recursing,
-                                        bool missing_ok)
+                                        bool missing_ok, LOCKMODE lockmode)
 {
        List       *children;
        ListCell   *child;
@@ -5686,8 +5854,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
         * use find_all_inheritors to do it in one pass.
         */
        if (is_check_constraint)
-               children = find_inheritance_children(RelationGetRelid(rel),
-                                                                                        AccessExclusiveLock);
+               children = find_inheritance_children(RelationGetRelid(rel), lockmode);
        else
                children = NIL;
 
@@ -5743,7 +5910,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
                                        /* Time to delete this child constraint, too */
                                        ATExecDropConstraint(childrel, constrName, behavior,
                                                                                 true, true,
-                                                                                false);
+                                                                                false, lockmode);
                                }
                                else
                                {
@@ -5798,7 +5965,7 @@ static void
 ATPrepAlterColumnType(List **wqueue,
                                          AlteredTableInfo *tab, Relation rel,
                                          bool recurse, bool recursing,
-                                         AlterTableCmd *cmd)
+                                         AlterTableCmd *cmd, LOCKMODE lockmode)
 {
        char       *colName = cmd->name;
        TypeName   *typeName = (TypeName *) cmd->def;
@@ -5925,7 +6092,7 @@ ATPrepAlterColumnType(List **wqueue,
         * alter would put them out of step.
         */
        if (recurse)
-               ATSimpleRecursion(wqueue, rel, cmd, recurse);
+               ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
        else if (!recursing &&
                         find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
                ereport(ERROR,
@@ -5936,7 +6103,7 @@ ATPrepAlterColumnType(List **wqueue,
 
 static void
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
-                                         const char *colName, TypeName *typeName)
+                                         const char *colName, TypeName *typeName, LOCKMODE lockmode)
 {
        HeapTuple       heapTup;
        Form_pg_attribute attTup;
@@ -6284,7 +6451,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
  * and constraints that depend on the altered columns.
  */
 static void
-ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab)
+ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 {
        ObjectAddress obj;
        ListCell   *l;
@@ -6298,9 +6465,9 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab)
         * at the catalogs to detect the existing entry.
         */
        foreach(l, tab->changedIndexDefs)
-               ATPostAlterTypeParse((char *) lfirst(l), wqueue);
+               ATPostAlterTypeParse((char *) lfirst(l), wqueue, lockmode);
        foreach(l, tab->changedConstraintDefs)
-               ATPostAlterTypeParse((char *) lfirst(l), wqueue);
+               ATPostAlterTypeParse((char *) lfirst(l), wqueue, lockmode);
 
        /*
         * Now we can drop the existing constraints and indexes --- constraints
@@ -6333,7 +6500,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab)
 }
 
 static void
-ATPostAlterTypeParse(char *cmd, List **wqueue)
+ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode)
 {
        List       *raw_parsetree_list;
        List       *querytree_list;
@@ -6380,7 +6547,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
                                        IndexStmt  *stmt = (IndexStmt *) stm;
                                        AlterTableCmd *newcmd;
 
-                                       rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+                                       rel = relation_openrv(stmt->relation, lockmode);
                                        tab = ATGetQueueEntry(wqueue, rel);
                                        newcmd = makeNode(AlterTableCmd);
                                        newcmd->subtype = AT_ReAddIndex;
@@ -6395,7 +6562,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
                                        AlterTableStmt *stmt = (AlterTableStmt *) stm;
                                        ListCell   *lcmd;
 
-                                       rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+                                       rel = relation_openrv(stmt->relation, lockmode);
                                        tab = ATGetQueueEntry(wqueue, rel);
                                        foreach(lcmd, stmt->cmds)
                                        {
@@ -6441,7 +6608,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
  * free-standing composite type.
  */
 void
-ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
+ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
 {
        Relation        target_rel;
        Relation        class_rel;
@@ -6452,7 +6619,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
         * Get exclusive lock till end of transaction on the target table. Use
         * relation_open so that we can work on indexes and sequences.
         */
-       target_rel = relation_open(relationOid, AccessExclusiveLock);
+       target_rel = relation_open(relationOid, lockmode);
 
        /* Get its pg_class tuple, too */
        class_rel = heap_open(RelationRelationId, RowExclusiveLock);
@@ -6629,7 +6796,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 
                        /* For each index, recursively change its ownership */
                        foreach(i, index_oid_list)
-                               ATExecChangeOwner(lfirst_oid(i), newOwnerId, true);
+                               ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
 
                        list_free(index_oid_list);
                }
@@ -6639,10 +6806,10 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
                        /* If it has a toast table, recurse to change its ownership */
                        if (tuple_class->reltoastrelid != InvalidOid)
                                ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
-                                                                 true);
+                                                                 true, lockmode);
 
                        /* If it has dependent sequences, recurse to change them too */
-                       change_owner_recurse_to_sequences(relationOid, newOwnerId);
+                       change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
                }
        }
 
@@ -6659,7 +6826,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
  * ownership.
  */
 static void
-change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
+change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
 {
        Relation        depRel;
        SysScanDesc scan;
@@ -6698,18 +6865,18 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
                        continue;
 
                /* Use relation_open just in case it's an index */
-               seqRel = relation_open(depForm->objid, AccessExclusiveLock);
+               seqRel = relation_open(depForm->objid, lockmode);
 
                /* skip non-sequence relations */
                if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
                {
                        /* No need to keep the lock */
-                       relation_close(seqRel, AccessExclusiveLock);
+                       relation_close(seqRel, lockmode);
                        continue;
                }
 
                /* We don't need to close the sequence while we alter it. */
-               ATExecChangeOwner(depForm->objid, newOwnerId, true);
+               ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
 
                /* Now we can close it.  Keep the lock till end of transaction. */
                relation_close(seqRel, NoLock);
@@ -6726,7 +6893,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
  * The only thing we have to do is to change the indisclustered bits.
  */
 static void
-ATExecClusterOn(Relation rel, const char *indexName)
+ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
 {
        Oid                     indexOid;
 
@@ -6752,7 +6919,7 @@ ATExecClusterOn(Relation rel, const char *indexName)
  * set and turn it off.
  */
 static void
-ATExecDropCluster(Relation rel)
+ATExecDropCluster(Relation rel, LOCKMODE lockmode)
 {
        mark_index_clustered(rel, InvalidOid);
 }
@@ -6761,7 +6928,7 @@ ATExecDropCluster(Relation rel)
  * ALTER TABLE SET TABLESPACE
  */
 static void
-ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename)
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
 {
        Oid                     tablespaceId;
        AclResult       aclresult;
@@ -6790,7 +6957,7 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename)
  * ALTER TABLE/INDEX SET (...) or RESET (...)
  */
 static void
-ATExecSetRelOptions(Relation rel, List *defList, bool isReset)
+ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode)
 {
        Oid                     relid;
        Relation        pgclass;
@@ -6871,7 +7038,7 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset)
                Relation        toastrel;
                Oid                     toastid = rel->rd_rel->reltoastrelid;
 
-               toastrel = heap_open(toastid, AccessExclusiveLock);
+               toastrel = heap_open(toastid, lockmode);
 
                /* Get the old reloptions */
                tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
@@ -6918,7 +7085,7 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset)
  * rewriting to be done, so we just want to copy the data as fast as possible.
  */
 static void
-ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
+ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 {
        Relation        rel;
        Oid                     oldTableSpace;
@@ -6935,7 +7102,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
        /*
         * Need lock here in case we are recursing to toast table or index
         */
-       rel = relation_open(tableOid, AccessExclusiveLock);
+       rel = relation_open(tableOid, lockmode);
 
        /*
         * No work if no change in tablespace.
@@ -7049,9 +7216,9 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
 
        /* Move associated toast relation and/or index, too */
        if (OidIsValid(reltoastrelid))
-               ATExecSetTableSpace(reltoastrelid, newTableSpace);
+               ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
        if (OidIsValid(reltoastidxid))
-               ATExecSetTableSpace(reltoastidxid, newTableSpace);
+               ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
 }
 
 /*
@@ -7119,7 +7286,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
  */
 static void
 ATExecEnableDisableTrigger(Relation rel, char *trigname,
-                                                  char fires_when, bool skip_system)
+                                                  char fires_when, bool skip_system, LOCKMODE lockmode)
 {
        EnableDisableTrigger(rel, trigname, fires_when, skip_system);
 }
@@ -7131,7 +7298,7 @@ ATExecEnableDisableTrigger(Relation rel, char *trigname,
  */
 static void
 ATExecEnableDisableRule(Relation rel, char *trigname,
-                                               char fires_when)
+                                               char fires_when, LOCKMODE lockmode)
 {
        EnableDisableRule(rel, trigname, fires_when);
 }
@@ -7153,7 +7320,7 @@ ATPrepAddInherit(Relation child_rel)
 }
 
 static void
-ATExecAddInherit(Relation child_rel, RangeVar *parent)
+ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 {
        Relation        parent_rel,
                                catalogRelation;
@@ -7514,7 +7681,7 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  * exactly the same way.
  */
 static void
-ATExecDropInherit(Relation rel, RangeVar *parent)
+ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
 {
        Relation        parent_rel;
        Relation        catalogRelation;
@@ -7738,7 +7905,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent)
  */
 void
 AlterTableNamespace(RangeVar *relation, const char *newschema,
-                                       ObjectType stmttype)
+                                       ObjectType stmttype, LOCKMODE lockmode)
 {
        Relation        rel;
        Oid                     relid;
@@ -7746,7 +7913,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
        Oid                     nspOid;
        Relation        classRel;
 
-       rel = relation_openrv(relation, AccessExclusiveLock);
+       rel = relation_openrv(relation, lockmode);
 
        relid = RelationGetRelid(rel);
        oldNspOid = RelationGetNamespace(rel);
@@ -7855,7 +8022,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
        if (rel->rd_rel->relkind == RELKIND_RELATION)
        {
                AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid);
-               AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, newschema);
+               AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, newschema, lockmode);
                AlterConstraintNamespaces(relid, oldNspOid, nspOid, false);
        }
 
@@ -7951,7 +8118,7 @@ AlterIndexNamespaces(Relation classRel, Relation rel,
  */
 static void
 AlterSeqNamespaces(Relation classRel, Relation rel,
-                                  Oid oldNspOid, Oid newNspOid, const char *newNspName)
+                                  Oid oldNspOid, Oid newNspOid, const char *newNspName, LOCKMODE lockmode)
 {
        Relation        depRel;
        SysScanDesc scan;
@@ -7990,13 +8157,13 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
                        continue;
 
                /* Use relation_open just in case it's an index */
-               seqRel = relation_open(depForm->objid, AccessExclusiveLock);
+               seqRel = relation_open(depForm->objid, lockmode);
 
                /* skip non-sequence relations */
                if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
                {
                        /* No need to keep the lock */
-                       relation_close(seqRel, AccessExclusiveLock);
+                       relation_close(seqRel, lockmode);
                        continue;
                }
 
index aef7838eb6e54058df488b1a5e952fada491f4d5..643b7b3a6f1c56d6737cd3bb5e7f087e9a804871 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.262 2010/02/26 02:00:39 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.263 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,7 +141,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        ObjectAddress myself,
                                referenced;
 
-       rel = heap_openrv(stmt->relation, AccessExclusiveLock);
+       /*
+        * ShareRowExclusiveLock is sufficient to prevent concurrent write activity
+        * to the relation, and thus to lock out any operations that might want to
+        * fire triggers on the relation.  If we had ON SELECT triggers we would
+        * need to take an AccessExclusiveLock to add one of those, just as we do
+        * with ON SELECT rules.
+        */
+       rel = heap_openrv(stmt->relation, ShareRowExclusiveLock);
 
        if (rel->rd_rel->relkind != RELKIND_RELATION)
                ereport(ERROR,
@@ -417,7 +424,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
         * can skip this for internally generated triggers, since the name
         * modification above should be sufficient.
         *
-        * NOTE that this is cool only because we have AccessExclusiveLock on the
+        * NOTE that this is cool only because we have ShareRowExclusiveLock on the
         * relation, so the trigger set won't be changing underneath us.
         */
        if (!isInternal)
@@ -1051,11 +1058,14 @@ RemoveTriggerById(Oid trigOid)
                elog(ERROR, "could not find tuple for trigger %u", trigOid);
 
        /*
-        * Open and exclusive-lock the relation the trigger belongs to.
+        * Open and lock the relation the trigger belongs to.  As in
+        * CreateTrigger, this is sufficient to lock out all operations that
+        * could fire or add triggers; but it would need to be revisited if
+        * we had ON SELECT triggers.
         */
        relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
 
-       rel = heap_open(relid, AccessExclusiveLock);
+       rel = heap_open(relid, ShareRowExclusiveLock);
 
        if (rel->rd_rel->relkind != RELKIND_RELATION)
                ereport(ERROR,
index 5d94445910c58c108ef4ed2af3596eb7d872c9a5..19d61a725681834a2da9c36a898f741c6578b70a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.149 2010/07/25 23:21:21 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.150 2010/07/28 05:22:24 sriggs Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
@@ -2638,7 +2638,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
                 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
                 */
                if (typTup->typtype == TYPTYPE_COMPOSITE)
-                       ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
+                       ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
                else
                {
                        /*
index 1657096c6d83c47bfddd56cb6f8b0ecead97ced5..a7e875b380cf0bc81c5c3ebd6f3be6b785dbc525 100644 (file)
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.40 2010/02/26 02:00:53 momjian Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.41 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,7 @@
 #include "parser/parse_utilcmd.h"
 #include "parser/parser.h"
 #include "rewrite/rewriteManip.h"
+#include "storage/lock.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -1528,7 +1529,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
 }
 
 /*
- * transformIndexStmt - parse analysis for CREATE INDEX
+ * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE
  *
  * Note: this is a no-op for an index not using either index expressions or
  * a predicate expression.     There are several code paths that create indexes
@@ -1554,7 +1555,8 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
         * because addRangeTableEntry() would acquire only AccessShareLock,
         * leaving DefineIndex() needing to do a lock upgrade with consequent risk
         * of deadlock.  Make sure this stays in sync with the type of lock
-        * DefineIndex() wants.
+        * DefineIndex() wants. If we are being called by ALTER TABLE, we will
+        * already hold a higher lock.
         */
        rel = heap_openrv(stmt->relation,
                                  (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
@@ -1919,6 +1921,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
        List       *newcmds = NIL;
        bool            skipValidation = true;
        AlterTableCmd *newcmd;
+       LOCKMODE        lockmode;
 
        /*
         * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
@@ -1927,13 +1930,19 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
        stmt = (AlterTableStmt *) copyObject(stmt);
 
        /*
-        * Acquire exclusive lock on the target relation, which will be held until
+        * Assign the appropriate lock level for this list of subcommands.
+        */
+       lockmode = AlterTableGetLockLevel(stmt->cmds);
+
+       /*
+        * Acquire appropriate lock on the target relation, which will be held until
         * end of transaction.  This ensures any decisions we make here based on
         * the state of the relation will still be good at execution. We must get
-        * exclusive lock now because execution will; taking a lower grade lock
-        * now and trying to upgrade later risks deadlock.
+        * lock now because execution will later require it; taking a lower grade lock
+        * now and trying to upgrade later risks deadlock.  Any new commands we add
+        * after this must not upgrade the lock level requested here.
         */
-       rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+       rel = relation_openrv(stmt->relation, lockmode);
 
        /* Set up pstate */
        pstate = make_parsestate(NULL);
index 06469b31cf0a0a5b818684bca9e9c427163d2667..8e32aed13ec0b76ff95ef44824397159f88a31e9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.141 2010/02/14 18:42:15 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.142 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -236,11 +236,14 @@ DefineQueryRewrite(char *rulename,
        /*
         * If we are installing an ON SELECT rule, we had better grab
         * AccessExclusiveLock to ensure no SELECTs are currently running on the
-        * event relation.      For other types of rules, it might be sufficient to
-        * grab ShareLock to lock out insert/update/delete actions.  But for now,
-        * let's just grab AccessExclusiveLock all the time.
+        * event relation.      For other types of rules, it is sufficient to
+        * grab ShareRowExclusiveLock to lock out insert/update/delete actions
+        * and to ensure that we lock out current CREATE RULE statements.
         */
-       event_relation = heap_open(event_relid, AccessExclusiveLock);
+       if (event_type == CMD_SELECT)
+               event_relation = heap_open(event_relid, AccessExclusiveLock);
+       else
+               event_relation = heap_open(event_relid, ShareRowExclusiveLock);
 
        /*
         * Verify relation is of a type that rules can sensibly be applied to.
index 080cb9c1ac28200d8b2fdc89f93320251027c809..14d183d2a510b52c7e94caca8926f9a94e3c2997 100644 (file)
@@ -15,7 +15,7 @@
  *
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.119 2010/07/22 00:47:52 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.120 2010/07/28 05:22:24 sriggs Exp $
  *
  * ----------
  */
@@ -2608,7 +2608,7 @@ RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
  *     This is not a trigger procedure, but is called during ALTER TABLE
  *     ADD FOREIGN KEY to validate the initial table contents.
  *
- *     We expect that an exclusive lock has been taken on rel and pkrel;
+ *     We expect that a ShareRowExclusiveLock or higher has been taken on rel and pkrel;
  *     hence, we do not need to lock individual rows for the check.
  *
  *     If the check fails because the current user doesn't have permissions
index 45d1835548abad8937239f7b7f0aa3887b4638e5..c94a46027ed40da58ac77912d01a390af6f7bbff 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.46 2010/02/01 19:28:56 rhaas Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.47 2010/07/28 05:22:24 sriggs Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define TABLECMDS_H
 
 #include "nodes/parsenodes.h"
+#include "storage/lock.h"
 #include "utils/relcache.h"
 
 
@@ -24,12 +25,14 @@ extern void RemoveRelations(DropStmt *drop);
 
 extern void AlterTable(AlterTableStmt *stmt);
 
-extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
+extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+
+extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
 
 extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
 
 extern void AlterTableNamespace(RangeVar *relation, const char *newschema,
-                                       ObjectType stmttype);
+                                       ObjectType stmttype, LOCKMODE lockmode);
 
 extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
                                                           Oid oldNspOid, Oid newNspOid,