<para>
     Adding a column with a non-null default or changing the type of an
     existing column will require the entire table and indexes to be rewritten.
-    This might take a significant amount of time for a large table; and it will
-    temporarily require double the disk space.  Adding or removing a system
-    <literal>oid</> column likewise requires rewriting the entire table.
+    As an exception, if the old type type is binary coercible to the new
+    type and the <literal>USING</> clause does not change the column contents,
+    a table rewrite is not needed, but any indexes on the affected columns 
+    must still be rebuilt.  Adding or removing a system <literal>oid</> column
+    also requires rewriting the entire table.  Table and/or index rebuilds may
+    take a significant amount of time for a large table; and will temporarily
+    require as much as double the disk space.
    </para>
 
    <para>
    <para>
     To force an immediate rewrite of the table, you can use
     <link linkend="SQL-VACUUM">VACUUM FULL</>, <xref linkend="SQL-CLUSTER">
-    or one of the forms of ALTER TABLE that forces a rewrite, such as
-    SET DATA TYPE.  This results in no semantically-visible change in the
-    table, but gets rid of no-longer-useful data.
+    or one of the forms of ALTER TABLE that forces a rewrite.  This results in
+    no semantically-visible change in the table, but gets rid of
+    no-longer-useful data.
    </para>
 
    <para>
 
    List       *constraints;    /* List of NewConstraint */
    List       *newvals;        /* List of NewColumnValue */
    bool        new_notnull;    /* T if we added new NOT NULL constraints */
-   bool        new_changeoids; /* T if we added/dropped the OID column */
+   bool        rewrite;        /* T if we a rewrite is forced */
    Oid         newTableSpace;  /* new tablespace; 0 means no change */
    /* Objects to rebuild after completing ALTER TYPE operations */
    List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
                      AlteredTableInfo *tab, Relation rel,
                      bool recurse, bool recursing,
                      AlterTableCmd *cmd, LOCKMODE lockmode);
+static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                      const char *colName, TypeName *typeName, LOCKMODE lockmode);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
        if (tab->relkind == RELKIND_FOREIGN_TABLE)
            continue;
 
+       /*
+        * If we change column data types or add/remove OIDs, the operation
+        * has to be propagated to tables that use this table's rowtype as a
+        * column type.  tab->newvals will also be non-NULL in the case where
+        * we're adding a column with a default.  We choose to forbid that
+        * case as well, since composite types might eventually support
+        * defaults.
+        *
+        * (Eventually we'll probably need to check for composite type
+        * dependencies even when we're just scanning the table without a
+        * rewrite, but at the moment a composite type does not enforce any
+        * constraints, so it's not necessary/appropriate to enforce them
+        * just during ALTER.)
+        */
+       if (tab->newvals != NIL || tab->rewrite)
+       {
+           Relation    rel;
+
+           rel = heap_open(tab->relid, NoLock);
+           find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
+           heap_close(rel, NoLock);
+       }
+
        /*
         * We only need to rewrite the table if at least one column needs to
         * be recomputed, or we are adding/removing the OID column.
         */
-       if (tab->newvals != NIL || tab->new_changeoids)
+       if (tab->rewrite)
        {
            /* Build a temporary relation and copy data */
            Relation    OldHeap;
        hi_options = 0;
    }
 
-   /*
-    * If we change column data types or add/remove OIDs, the operation has to
-    * be propagated to tables that use this table's rowtype as a column type.
-    * newrel will also be non-NULL in the case where we're adding a column
-    * with a default.  We choose to forbid that case as well, since composite
-    * types might eventually support defaults.
-    *
-    * (Eventually we'll probably need to check for composite type
-    * dependencies even when we're just scanning the table without a rewrite,
-    * but at the moment a composite type does not enforce any constraints,
-    * so it's not necessary/appropriate to enforce them just during ALTER.)
-    */
-   if (newrel)
-       find_composite_type_dependencies(oldrel->rd_rel->reltype,
-                                        oldrel, NULL);
-
    /*
     * Generate the constraint and default execution states
     */
        List       *dropped_attrs = NIL;
        ListCell   *lc;
 
+       if (newrel)
+           ereport(DEBUG1,
+                   (errmsg("rewriting table \"%s\"",
+                           RelationGetRelationName(oldrel))));
+       else
+           ereport(DEBUG1,
+                   (errmsg("verifying table \"%s\"",
+                           RelationGetRelationName(oldrel))));
+
        econtext = GetPerTupleExprContext(estate);
 
        /*
 
        while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
        {
-           if (newrel)
+           if (tab->rewrite)
            {
                Oid         tupOid = InvalidOid;
 
            newval->expr = defval;
 
            tab->newvals = lappend(tab->newvals, newval);
+           tab->rewrite = true;
        }
 
        /*
     * table to fix that.
     */
    if (isOid)
-       tab->new_changeoids = true;
+       tab->rewrite = true;
 
    /*
     * Add needed dependency entries for the new column.
        tab = ATGetQueueEntry(wqueue, rel);
 
        /* Tell Phase 3 to physically remove the OID column */
-       tab->new_changeoids = true;
+       tab->rewrite = true;
    }
 }
 
    /* suppress schema rights check when rebuilding existing index */
    check_rights = !is_rebuild;
    /* skip index build if phase 3 will have to rewrite table anyway */
-   skip_build = (tab->newvals != NIL);
+   skip_build = tab->rewrite;
    /* suppress notices when rebuilding existing index */
    quiet = is_rebuild;
 
    HeapTuple   tuple;
    Trigger     trig;
 
+   ereport(DEBUG1,
+           (errmsg("validating foreign key constraint \"%s\"", conname)));
+
    /*
     * Build a trigger call structure; we'll need it either way.
     */
        newval->expr = (Expr *) transform;
 
        tab->newvals = lappend(tab->newvals, newval);
+       if (ATColumnChangeRequiresRewrite(transform, attnum))
+           tab->rewrite = true;
    }
    else if (tab->relkind == RELKIND_FOREIGN_TABLE)
    {
        ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
 }
 
+/*
+ * When the data type of a column is changed, a rewrite might not be require
+ * if the data type is being changed to its current type, or more interestingly
+ * to a type to which it is binary coercible.  But we must check carefully that
+ * the USING clause isn't trying to insert some other value.
+ */
+static bool
+ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
+{
+   Assert(expr != NULL);
+
+   for (;;)
+   {
+       /* only one varno, so no need to check that */
+       if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
+           return false;
+       else if (IsA(expr, RelabelType))
+           expr = (Node *) ((RelabelType *) expr)->arg;
+       else
+           return true;
+   }
+}
+
 static void
 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                      const char *colName, TypeName *typeName, LOCKMODE lockmode)