List       *changedIndexOids;       /* OIDs of indexes to rebuild */
    List       *changedIndexDefs;       /* string definitions of same */
    char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
+   char       *clusterOnIndex; /* index to use for CLUSTER */
 } AlteredTableInfo;
 
 /* Struct describing one new constraint to check in Phase 3 scan */
    tab->replicaIdentityIndex = get_rel_name(indoid);
 }
 
+/*
+ * Subroutine for ATExecAlterColumnType: remember any clustered index.
+ */
+static void
+RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
+{
+   if (!get_index_isclustered(indoid))
+       return;
+
+   if (tab->clusterOnIndex)
+       elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
+
+   tab->clusterOnIndex = get_rel_name(indoid);
+}
+
 /*
  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
  * to be rebuilt (which we might already know).
                                                 defstring);
        }
 
+       /*
+        * For the index of a constraint, if any, remember if it is used for
+        * the table's replica identity or if it is a clustered index, so that
+        * ATPostAlterTypeCleanup() can queue up commands necessary to restore
+        * those properties.
+        */
        indoid = get_constraint_index(conoid);
        if (OidIsValid(indoid))
+       {
            RememberReplicaIdentityForRebuilding(indoid, tab);
+           RememberClusterOnForRebuilding(indoid, tab);
+       }
    }
 }
 
            tab->changedIndexDefs = lappend(tab->changedIndexDefs,
                                            defstring);
 
+           /*
+            * Remember if this index is used for the table's replica identity
+            * or if it is a clustered index, so that ATPostAlterTypeCleanup()
+            * can queue up commands necessary to restore those properties.
+            */
            RememberReplicaIdentityForRebuilding(indoid, tab);
+           RememberClusterOnForRebuilding(indoid, tab);
        }
    }
 }
            lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
    }
 
+   /*
+    * Queue up command to restore marking of index used for cluster.
+    */
+   if (tab->clusterOnIndex)
+   {
+       AlterTableCmd *cmd = makeNode(AlterTableCmd);
+
+       cmd->subtype = AT_ClusterOn;
+       cmd->name = tab->clusterOnIndex;
+
+       /* do it after indexes and constraints */
+       tab->subcmds[AT_PASS_OLD_CONSTR] =
+           lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+   }
+
    /*
     * Now we can drop the existing constraints and indexes --- constraints
     * first, since some of them might depend on the indexes.  In fact, we
 
 
    return result;
 }
+
+/*
+ * get_index_isclustered
+ *
+ *     Given the index OID, return pg_index.indisclustered.
+ */
+bool
+get_index_isclustered(Oid index_oid)
+{
+   bool        isclustered;
+   HeapTuple   tuple;
+   Form_pg_index rd_index;
+
+   tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+   if (!HeapTupleIsValid(tuple))
+       elog(ERROR, "cache lookup failed for index %u", index_oid);
+
+   rd_index = (Form_pg_index) GETSTRUCT(tuple);
+   isclustered = rd_index->indisclustered;
+   ReleaseSysCache(tuple);
+
+   return isclustered;
+}
 
 extern Oid get_range_subtype(Oid rangeOid);
 extern Oid get_range_collation(Oid rangeOid);
 extern bool    get_index_isreplident(Oid index_oid);
+extern bool get_index_isclustered(Oid index_oid);
 
 #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
 /* type_is_array_domain accepts both plain arrays and domains over arrays */
 
  c4     | integer | 
 
 DROP TABLE test_add_column;
+-- Test that ALTER TABLE rewrite preserves a clustered index
+-- for normal indexes and indexes on constraints.
+create table alttype_cluster (a int);
+alter table alttype_cluster add primary key (a);
+create index alttype_cluster_ind on alttype_cluster (a);
+alter table alttype_cluster cluster on alttype_cluster_ind;
+-- Normal index remains clustered.
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+      indexrelid      | indisclustered 
+----------------------+----------------
+ alttype_cluster_ind  | t
+ alttype_cluster_pkey | f
+(2 rows)
+
+alter table alttype_cluster alter a type bigint;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+      indexrelid      | indisclustered 
+----------------------+----------------
+ alttype_cluster_ind  | t
+ alttype_cluster_pkey | f
+(2 rows)
+
+-- Constraint index remains clustered.
+alter table alttype_cluster cluster on alttype_cluster_pkey;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+      indexrelid      | indisclustered 
+----------------------+----------------
+ alttype_cluster_ind  | f
+ alttype_cluster_pkey | t
+(2 rows)
+
+alter table alttype_cluster alter a type int;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+      indexrelid      | indisclustered 
+----------------------+----------------
+ alttype_cluster_ind  | f
+ alttype_cluster_pkey | t
+(2 rows)
+
+drop table alttype_cluster;
 
    ADD COLUMN c4 integer;
 \d test_add_column
 DROP TABLE test_add_column;
+
+-- Test that ALTER TABLE rewrite preserves a clustered index
+-- for normal indexes and indexes on constraints.
+create table alttype_cluster (a int);
+alter table alttype_cluster add primary key (a);
+create index alttype_cluster_ind on alttype_cluster (a);
+alter table alttype_cluster cluster on alttype_cluster_ind;
+-- Normal index remains clustered.
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+alter table alttype_cluster alter a type bigint;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+-- Constraint index remains clustered.
+alter table alttype_cluster cluster on alttype_cluster_pkey;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+alter table alttype_cluster alter a type int;
+select indexrelid::regclass, indisclustered from pg_index
+  where indrelid = 'alttype_cluster'::regclass
+  order by indexrelid::regclass::text;
+drop table alttype_cluster;