diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 98519ef..83dc6df 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -10600,11 +10600,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	 * performed all the individual ALTER TYPE operations.  We have to save
 	 * the info before executing ALTER TYPE, though, else the deparser will
 	 * get confused.
-	 *
-	 * There could be multiple entries for the same object, so we must check
-	 * to ensure we process each one only once.  Note: we assume that an index
-	 * that implements a constraint will not show a direct dependency on the
-	 * column.
 	 */
 	depRel = table_open(DependRelationId, RowExclusiveLock);
 
@@ -10646,6 +10641,14 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 					if (relKind == RELKIND_INDEX ||
 						relKind == RELKIND_PARTITIONED_INDEX)
 					{
+						/*
+						 * This de-duplication check is critical for two
+						 * independent reasons: we mustn't try to create the
+						 * same index twice, and if an index depends on more
+						 * than one column whose type is to be altered, we
+						 * must capture its definition string before applying
+						 * any of the column type changes.
+						 */
 						Assert(foundObject.objectSubId == 0);
 						if (!list_member_oid(tab->changedIndexOids, foundObject.objectId))
 						{
@@ -10688,6 +10691,12 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 				}
 
 			case OCLASS_CONSTRAINT:
+
+				/*
+				 * As with indexes, de-duplication is critical, and it's
+				 * needed because the same constraint could depend on multiple
+				 * target columns.
+				 */
 				Assert(foundObject.objectSubId == 0);
 				if (!list_member_oid(tab->changedConstraintOids,
 									 foundObject.objectId))
@@ -11198,8 +11207,25 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 			def_item, tab->changedIndexDefs)
 	{
 		Oid			oldId = lfirst_oid(oid_item);
+		Oid			conoid;
 		Oid			relid;
 
+		/*
+		 * We must check each index to see if it belongs to a constraint that
+		 * we already processed above.  Typically this check does not fire
+		 * because constraint indexes normally have only dependencies on their
+		 * constraint, and thus would not have been found by the dependency
+		 * scan in ATExecAlterColumnType.  But it's possible for such an index
+		 * to also have direct dependencies on the table, for example with a
+		 * partial exclusion constraint.  In that case we'd have list entries
+		 * for both the index and the constraint, and we must disregard the
+		 * one for the index.
+		 */
+		conoid = get_index_constraint(oldId);
+		if (OidIsValid(conoid) &&
+			list_member_oid(tab->changedConstraintOids, conoid))
+			continue;
+
 		relid = IndexGetRelation(oldId, false);
 		ATPostAlterTypeParse(oldId, relid, InvalidOid,
 							 (char *) lfirst(def_item),
