diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 263e5024b62148863c139a523222af253871288a..e5856d9bfb4cea718b2e886313267f96f6cf7b92 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.73 2004/07/11 23:13:51 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.74 2004/10/22 17:20:04 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -459,6 +459,22 @@ ALTER TABLE table ALTER COLUMN anycol TYPE anytype;
     data.
    </para>
 
+   <para>
+    The <literal>USING</literal> option of <literal>ALTER TYPE</> can actually
+    specify any expression involving the old values of the row; that is, it
+    can refer to other columns as well as the one being converted.  This allows
+    very general conversions to be done with the <literal>ALTER TYPE</>
+    syntax.  Because of this flexibility, the <literal>USING</literal>
+    expression is not applied to the column's default value (if any); the
+    result might not be a constant expression as required for a default.
+    This means that when there is no implicit or assignment cast from old to
+    new type, <literal>ALTER TYPE</> may fail to convert the default even
+    though a <literal>USING</literal> clause is supplied.  In such cases,
+    drop the default with <literal>DROP DEFAULT</>, perform the <literal>ALTER
+    TYPE</>, and then use <literal>SET DEFAULT</> to add a suitable new
+    default.
+   </para>
+
    <para>
     If a table has any descendant tables, it is not permitted to add,
     rename, or change the type of a column in the parent table without doing
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 463d2506c2e39554a8d8fb7ab600cfdb2905f9f2..3616275858392c370bdc5457557b48f701d08101 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.136 2004/10/21 21:33:59 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.137 2004/10/22 17:20:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4738,13 +4738,20 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	 * changing the column type, because build_column_default itself will
 	 * try to coerce, and will not issue the error message we want if it
 	 * fails.)
+	 *
+	 * We remove any implicit coercion steps at the top level of the old
+	 * default expression; this has been agreed to satisfy the principle
+	 * of least surprise.  (The conversion to the new column type should
+	 * act like it started from what the user sees as the stored expression,
+	 * and the implicit coercions aren't going to be shown.)
 	 */
 	if (attTup->atthasdef)
 	{
 		defaultexpr = build_column_default(rel, attnum);
 		Assert(defaultexpr);
+		defaultexpr = strip_implicit_coercions(defaultexpr);
 		defaultexpr = coerce_to_target_type(NULL,		/* no UNKNOWN params */
-									  defaultexpr, exprType(defaultexpr),
+											defaultexpr, exprType(defaultexpr),
 											targettype, typename->typmod,
 											COERCION_ASSIGNMENT,
 											COERCE_IMPLICIT_CAST);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index b6ac7786aa7a96d7a95912271cf1cbd9418a7094..0f7d8dc31fcae1c2640cb4e2d91c5059bc36f17d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.182 2004/10/07 18:38:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.183 2004/10/22 17:20:05 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1021,6 +1021,41 @@ CommuteClause(OpExpr *clause)
 	lsecond(clause->args) = temp;
 }
 
+/*
+ * strip_implicit_coercions: remove implicit coercions at top level of tree
+ *
+ * Note: there isn't any useful thing we can do with a RowExpr here, so
+ * just return it unchanged, even if it's marked as an implicit coercion.
+ */
+Node *
+strip_implicit_coercions(Node *node)
+{
+	if (node == NULL)
+		return NULL;
+	if (IsA(node, FuncExpr))
+	{
+		FuncExpr   *f = (FuncExpr *) node;
+
+		if (f->funcformat == COERCE_IMPLICIT_CAST)
+			return strip_implicit_coercions(linitial(f->args));
+	}
+	else if (IsA(node, RelabelType))
+	{
+		RelabelType *r = (RelabelType *) node;
+
+		if (r->relabelformat == COERCE_IMPLICIT_CAST)
+			return strip_implicit_coercions((Node *) r->arg);
+	}
+	else if (IsA(node, CoerceToDomain))
+	{
+		CoerceToDomain *c = (CoerceToDomain *) node;
+
+		if (c->coercionformat == COERCE_IMPLICIT_CAST)
+			return strip_implicit_coercions((Node *) c->arg);
+	}
+	return node;
+}
+
 /*
  * set_coercionform_dontcare: set all CoercionForm fields to COERCE_DONTCARE
  *
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index d3fec6c444c02ab7c4f8db923937f461c894dbbc..0f452755e5ae51f77144a6d993b92a702ce35c21 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.75 2004/08/29 04:13:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.76 2004/10/22 17:20:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,6 +63,8 @@ extern bool has_distinct_on_clause(Query *query);
 extern int	NumRelids(Node *clause);
 extern void CommuteClause(OpExpr *clause);
 
+extern Node *strip_implicit_coercions(Node *node);
+
 extern void set_coercionform_dontcare(Node *node);
 
 extern Node *eval_const_expressions(Node *node);