lockmode);
            break;
        case AT_AddConstraint:  /* ADD CONSTRAINT */
-           cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
-                                     cur_pass, context);
-           /* Might not have gotten AddConstraint back from parse transform */
+           /* Transform the command only during initial examination */
+           if (cur_pass == AT_PASS_ADD_CONSTR)
+               cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
+                                         false, lockmode,
+                                         cur_pass, context);
+           /* Depending on constraint type, might be no more work to do now */
            if (cmd != NULL)
                address =
                    ATExecAddConstraint(wqueue, tab, rel,
                                        false, false, lockmode);
            break;
        case AT_AddConstraintRecurse:   /* ADD CONSTRAINT with recursion */
-           cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, true, lockmode,
-                                     cur_pass, context);
-           /* Might not have gotten AddConstraint back from parse transform */
+           /* Transform the command only during initial examination */
+           if (cur_pass == AT_PASS_ADD_CONSTR)
+               cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
+                                         true, lockmode,
+                                         cur_pass, context);
+           /* Depending on constraint type, might be no more work to do now */
            if (cmd != NULL)
                address =
                    ATExecAddConstraint(wqueue, tab, rel,
    foreach(lc, atstmt->cmds)
    {
        AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
+       int         pass;
+
+       /*
+        * This switch need only cover the subcommand types that can be added
+        * by parse_utilcmd.c; otherwise, we'll use the default strategy of
+        * executing the subcommand immediately, as a substitute for the
+        * original subcommand.  (Note, however, that this does cause
+        * AT_AddConstraint subcommands to be rescheduled into later passes,
+        * which is important for index and foreign key constraints.)
+        *
+        * We assume we needn't do any phase-1 checks for added subcommands.
+        */
+       switch (cmd2->subtype)
+       {
+           case AT_SetNotNull:
+               /* Need command-specific recursion decision */
+               ATPrepSetNotNull(wqueue, rel, cmd2,
+                                recurse, false,
+                                lockmode, context);
+               pass = AT_PASS_COL_ATTRS;
+               break;
+           case AT_AddIndex:
+               /* This command never recurses */
+               /* No command-specific prep needed */
+               pass = AT_PASS_ADD_INDEX;
+               break;
+           case AT_AddIndexConstraint:
+               /* This command never recurses */
+               /* No command-specific prep needed */
+               pass = AT_PASS_ADD_INDEXCONSTR;
+               break;
+           case AT_AddConstraint:
+               /* Recursion occurs during execution phase */
+               if (recurse)
+                   cmd2->subtype = AT_AddConstraintRecurse;
+               switch (castNode(Constraint, cmd2->def)->contype)
+               {
+                   case CONSTR_PRIMARY:
+                   case CONSTR_UNIQUE:
+                   case CONSTR_EXCLUSION:
+                       pass = AT_PASS_ADD_INDEXCONSTR;
+                       break;
+                   default:
+                       pass = AT_PASS_ADD_OTHERCONSTR;
+                       break;
+               }
+               break;
+           case AT_AlterColumnGenericOptions:
+               /* This command never recurses */
+               /* No command-specific prep needed */
+               pass = AT_PASS_MISC;
+               break;
+           default:
+               pass = cur_pass;
+               break;
+       }
 
-       if (newcmd == NULL &&
-           (cmd->subtype == cmd2->subtype ||
-            (cmd->subtype == AT_AddConstraintRecurse &&
-             cmd2->subtype == AT_AddConstraint)))
+       if (pass < cur_pass)
+       {
+           /* Cannot schedule into a pass we already finished */
+           elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
+                pass);
+       }
+       else if (pass > cur_pass)
        {
-           /* Found the transformed version of our subcommand */
-           cmd2->subtype = cmd->subtype;   /* copy recursion flag */
-           newcmd = cmd2;
+           /* OK, queue it up for later */
+           tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
        }
        else
        {
-           int         pass;
-
            /*
-            * Schedule added subcommand appropriately.  We assume we needn't
-            * do any phase-1 checks for it.  This switch only has to cover
-            * the subcommand types that can be added by parse_utilcmd.c.
+            * We should see at most one subcommand for the current pass,
+            * which is the transformed version of the original subcommand.
             */
-           switch (cmd2->subtype)
+           if (newcmd == NULL && cmd->subtype == cmd2->subtype)
            {
-               case AT_SetNotNull:
-                   /* Need command-specific recursion decision */
-                   ATPrepSetNotNull(wqueue, rel, cmd2,
-                                    recurse, false,
-                                    lockmode, context);
-                   pass = AT_PASS_COL_ATTRS;
-                   break;
-               case AT_AddIndex:
-                   /* This command never recurses */
-                   /* No command-specific prep needed */
-                   pass = AT_PASS_ADD_INDEX;
-                   break;
-               case AT_AddIndexConstraint:
-                   /* This command never recurses */
-                   /* No command-specific prep needed */
-                   pass = AT_PASS_ADD_INDEXCONSTR;
-                   break;
-               case AT_AddConstraint:
-                   /* Recursion occurs during execution phase */
-                   if (recurse)
-                       cmd2->subtype = AT_AddConstraintRecurse;
-                   switch (castNode(Constraint, cmd2->def)->contype)
-                   {
-                       case CONSTR_PRIMARY:
-                       case CONSTR_UNIQUE:
-                       case CONSTR_EXCLUSION:
-                           pass = AT_PASS_ADD_INDEXCONSTR;
-                           break;
-                       default:
-                           pass = AT_PASS_ADD_OTHERCONSTR;
-                           break;
-                   }
-                   break;
-               case AT_AlterColumnGenericOptions:
-                   /* This command never recurses */
-                   /* No command-specific prep needed */
-                   pass = AT_PASS_MISC;
-                   break;
-               default:
-                   elog(ERROR, "unexpected AlterTableType: %d",
-                        (int) cmd2->subtype);
-                   pass = AT_PASS_UNSET;
-                   break;
+               /* Found the transformed version of our subcommand */
+               newcmd = cmd2;
            }
-           /* Must be for a later pass than we're currently doing */
-           if (pass <= cur_pass)
-               elog(ERROR, "ALTER TABLE scheduling failure");
-           tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
+           else
+               elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
+                    pass);
        }
    }
 
 
 Indexes:
     "ataddindex_expr_excl" EXCLUDE USING btree ((f1 ~~ 'a'::text) WITH =)
 
+DROP TABLE ataddindex;
+CREATE TABLE ataddindex(id int, ref_id int);
+ALTER TABLE ataddindex
+  ADD PRIMARY KEY (id),
+  ADD FOREIGN KEY (ref_id) REFERENCES ataddindex;
+\d ataddindex
+             Table "public.ataddindex"
+ Column |  Type   | Collation | Nullable | Default 
+--------+---------+-----------+----------+---------
+ id     | integer |           | not null | 
+ ref_id | integer |           |          | 
+Indexes:
+    "ataddindex_pkey" PRIMARY KEY, btree (id)
+Foreign-key constraints:
+    "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id)
+Referenced by:
+    TABLE "ataddindex" CONSTRAINT "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id)
+
+DROP TABLE ataddindex;
+CREATE TABLE ataddindex(id int, ref_id int);
+ALTER TABLE ataddindex
+  ADD UNIQUE (id),
+  ADD FOREIGN KEY (ref_id) REFERENCES ataddindex (id);
+\d ataddindex
+             Table "public.ataddindex"
+ Column |  Type   | Collation | Nullable | Default 
+--------+---------+-----------+----------+---------
+ id     | integer |           |          | 
+ ref_id | integer |           |          | 
+Indexes:
+    "ataddindex_id_key" UNIQUE CONSTRAINT, btree (id)
+Foreign-key constraints:
+    "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id)
+Referenced by:
+    TABLE "ataddindex" CONSTRAINT "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id)
+
 DROP TABLE ataddindex;
 -- unsupported constraint types for partitioned tables
 CREATE TABLE partitioned (