diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7ed028512da176341331b0ee11ff980adddb696b..a5bff31c89fd684c7d8b5c643063f3f17f98dfaf 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.113 2003/12/17 19:49:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.114 2004/03/15 01:13:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,50 +63,6 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 	if (can_coerce_type(1, &exprtype, &targettype, ccontext))
 		expr = coerce_type(pstate, expr, exprtype, targettype,
 						   ccontext, cformat);
-	else if (ccontext >= COERCION_ASSIGNMENT)
-	{
-		/*
-		 * String hacks to get transparent conversions for char and
-		 * varchar: if a coercion to text is available, use it for forced
-		 * coercions to char(n) or varchar(n) or domains thereof.
-		 *
-		 * This is pretty grotty, but seems easier to maintain than providing
-		 * entries in pg_cast that parallel all the ones for text.
-		 */
-		Oid			targetbasetype = getBaseType(targettype);
-
-		if (targetbasetype == BPCHAROID || targetbasetype == VARCHAROID)
-		{
-			Oid			text_id = TEXTOID;
-
-			if (can_coerce_type(1, &exprtype, &text_id, ccontext))
-			{
-				expr = coerce_type(pstate, expr, exprtype, text_id,
-								   ccontext, cformat);
-				if (targetbasetype != targettype)
-				{
-					/* need to coerce to domain over char or varchar */
-					expr = coerce_to_domain(expr, targetbasetype, targettype,
-											cformat);
-				}
-				else
-				{
-					/*
-					 * need a RelabelType if no typmod coercion will be
-					 * performed
-					 */
-					if (targettypmod < 0)
-						expr = (Node *) makeRelabelType((Expr *) expr,
-														targettype, -1,
-														cformat);
-				}
-			}
-			else
-				expr = NULL;
-		}
-		else
-			expr = NULL;
-	}
 	else
 		expr = NULL;
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index a32809d9d4cd9d1ab50030214a21394325d8dc12..8f30e0bf5949400155d1af5de251b4c4d6a45e92 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.219 2004/02/14 20:16:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.220 2004/03/15 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200402141
+#define CATALOG_VERSION_NO	200403141
 
 #endif
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 4f48f1fecb14987983a6a7175a19425ccd1fe303..549fa5f80db9c7ede4f6c761e7a9af1a1aeeae85 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.10 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.11 2004/03/15 01:13:41 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -168,11 +168,14 @@ DATA(insert ( 1042 1043  401 i ));
 DATA(insert ( 1043	 25    0 i ));
 DATA(insert ( 1043 1042    0 i ));
 DATA(insert (	18	 25  946 i ));
-DATA(insert (	18 1042  860 i ));
+DATA(insert (	18 1042  860 a ));
+DATA(insert (	18 1043  946 a ));
 DATA(insert (	19	 25  406 i ));
-DATA(insert (	19 1042  408 i ));
-DATA(insert (	19 1043 1401 i ));
+DATA(insert (	19 1042  408 a ));
+DATA(insert (	19 1043 1401 a ));
 DATA(insert (	25	 18  944 a ));
+DATA(insert ( 1042	 18  944 a ));
+DATA(insert ( 1043	 18  944 a ));
 DATA(insert (	25	 19  407 i ));
 DATA(insert ( 1042	 19  409 i ));
 DATA(insert ( 1043	 19 1400 i ));
@@ -281,4 +284,81 @@ DATA(insert (	25 1266  938 e ));
 DATA(insert ( 1700	 25 1688 i ));
 DATA(insert (	25 1700 1686 e ));
 
+/*
+ * Cross-category casts to and from VARCHAR
+ *
+ * We support all the same casts as for TEXT, but none are implicit.
+ */
+DATA(insert (	20 1043 1289 a ));
+DATA(insert ( 1043	 20 1290 e ));
+DATA(insert (	21 1043  113 a ));
+DATA(insert ( 1043	 21  818 e ));
+DATA(insert (	23 1043  112 a ));
+DATA(insert ( 1043	 23  819 e ));
+DATA(insert (	26 1043  114 a ));
+DATA(insert ( 1043	 26  817 e ));
+DATA(insert ( 1043	650 1714 e ));
+DATA(insert (  700 1043  841 a ));
+DATA(insert ( 1043	700  839 e ));
+DATA(insert (  701 1043  840 a ));
+DATA(insert ( 1043	701  838 e ));
+DATA(insert (  829 1043  752 e ));
+DATA(insert ( 1043	829  767 e ));
+DATA(insert (  650 1043  730 e ));
+DATA(insert (  869 1043  730 e ));
+DATA(insert ( 1043	869 1713 e ));
+DATA(insert ( 1082 1043  749 a ));
+DATA(insert ( 1043 1082  748 e ));
+DATA(insert ( 1083 1043  948 a ));
+DATA(insert ( 1043 1083  837 e ));
+DATA(insert ( 1114 1043 2034 a ));
+DATA(insert ( 1043 1114 2022 e ));
+DATA(insert ( 1184 1043 1192 a ));
+DATA(insert ( 1043 1184 1191 e ));
+DATA(insert ( 1186 1043 1193 a ));
+DATA(insert ( 1043 1186 1263 e ));
+DATA(insert ( 1266 1043  939 a ));
+DATA(insert ( 1043 1266  938 e ));
+DATA(insert ( 1700 1043 1688 a ));
+DATA(insert ( 1043 1700 1686 e ));
+
+/*
+ * Cross-category casts to and from BPCHAR
+ *
+ * A function supporting cast to TEXT/VARCHAR can be used for cast to BPCHAR,
+ * but the other direction is okay only if the function treats trailing
+ * blanks as insignificant.  So this is a subset of the VARCHAR list.
+ * (Arguably the holdouts should be fixed, but I'm not doing that now...)
+ */
+DATA(insert (	20 1042 1289 a ));
+DATA(insert ( 1042	 20 1290 e ));
+DATA(insert (	21 1042  113 a ));
+DATA(insert ( 1042	 21  818 e ));
+DATA(insert (	23 1042  112 a ));
+DATA(insert ( 1042	 23  819 e ));
+DATA(insert (	26 1042  114 a ));
+DATA(insert ( 1042	 26  817 e ));
+DATA(insert (  700 1042  841 a ));
+DATA(insert ( 1042	700  839 e ));
+DATA(insert (  701 1042  840 a ));
+DATA(insert ( 1042	701  838 e ));
+DATA(insert (  829 1042  752 e ));
+DATA(insert ( 1042	829  767 e ));
+DATA(insert (  650 1042  730 e ));
+DATA(insert (  869 1042  730 e ));
+DATA(insert ( 1082 1042  749 a ));
+DATA(insert ( 1042 1082  748 e ));
+DATA(insert ( 1083 1042  948 a ));
+DATA(insert ( 1042 1083  837 e ));
+DATA(insert ( 1114 1042 2034 a ));
+DATA(insert ( 1042 1114 2022 e ));
+DATA(insert ( 1184 1042 1192 a ));
+DATA(insert ( 1042 1184 1191 e ));
+DATA(insert ( 1186 1042 1193 a ));
+DATA(insert ( 1042 1186 1263 e ));
+DATA(insert ( 1266 1042  939 a ));
+DATA(insert ( 1042 1266  938 e ));
+DATA(insert ( 1700 1042 1688 a ));
+DATA(insert ( 1042 1700 1686 e ));
+
 #endif   /* PG_CAST_H */
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 4e2c76de5c2fb7041f40862219ad22a5851007cf..b76cc695f448ec4517e7b2104ad9cb321c008946 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -239,11 +239,17 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
 -- Look for cast functions that don't have the right signature.  The
 -- argument and result types in pg_proc must be the same as, or binary
 -- compatible with, what it says in pg_cast.
+-- As a special case, we allow casts from CHAR(n) that use functions
+-- declared to take TEXT.  This does not pass the binary-coercibility test
+-- because CHAR(n)-to-TEXT normally invokes rtrim().  However, the results
+-- are the same, so long as the function is one that ignores trailing blanks.
 SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
+             OR (c.castsource = 'character'::regtype AND
+                 p.proargtypes[0] = 'text'::regtype))
      OR NOT binary_coercible(p.prorettype, c.casttarget));
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 448f3ff0c5b74463029aae2648b238144464540e..82a294db6d624750c9a4b6796bca863227c35a57 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -196,12 +196,18 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
 -- Look for cast functions that don't have the right signature.  The
 -- argument and result types in pg_proc must be the same as, or binary
 -- compatible with, what it says in pg_cast.
+-- As a special case, we allow casts from CHAR(n) that use functions
+-- declared to take TEXT.  This does not pass the binary-coercibility test
+-- because CHAR(n)-to-TEXT normally invokes rtrim().  However, the results
+-- are the same, so long as the function is one that ignores trailing blanks.
 
 SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
+             OR (c.castsource = 'character'::regtype AND
+                 p.proargtypes[0] = 'text'::regtype))
      OR NOT binary_coercible(p.prorettype, c.casttarget));
 
 -- Look for binary compatible casts that do not have the reverse