diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 467a5cf7de3975223533af95ce35804d539949ac..cdd76afb55d06040cb4d5c3b5276bd35221d19cd 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.78 2001/05/21 16:54:46 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.79 2001/06/01 17:49:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,10 +65,8 @@ Datum
 bpcharin(PG_FUNCTION_ARGS)
 {
 	char	   *s = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
 	Oid			typelem = PG_GETARG_OID(1);
-
 #endif
 	int32		atttypmod = PG_GETARG_INT32(2);
 	BpChar	   *result;
@@ -77,45 +75,46 @@ bpcharin(PG_FUNCTION_ARGS)
 	int			i;
 
 	len = strlen(s);
-	maxlen = atttypmod - VARHDRSZ;
 
-	if (atttypmod >= (int32) VARHDRSZ && len > maxlen)
+	/* If typmod is -1 (or invalid), use the actual string length */
+	if (atttypmod < (int32) VARHDRSZ)
+		maxlen = len;
+	else
+		maxlen = atttypmod - VARHDRSZ;
+
+	if (len > maxlen)
 	{
+		/* Verify that extra characters are spaces, and clip them off */
 #ifdef MULTIBYTE
 		size_t mbmaxlen = pg_mbcliplen(s, len, maxlen);
 
 		if (strspn(s + mbmaxlen, " ") == len - mbmaxlen)
 			len = mbmaxlen;
+		else
+			elog(ERROR, "value too long for type character(%d)", maxlen);
+		Assert(len <= maxlen);
 #else
 		if (strspn(s + maxlen, " ") == len - maxlen)
-			/* clip extra spaces */
 			len = maxlen;
-#endif
 		else
 			elog(ERROR, "value too long for type character(%d)", maxlen);
+#endif
 	}
-	else
-		/* If typmod is -1 (or invalid), use the actual string length */
-		maxlen = len;
 
 	result = palloc(maxlen + VARHDRSZ);
 	VARATT_SIZEP(result) = maxlen + VARHDRSZ;
 	r = VARDATA(result);
-	for (i = 0; i < len; i++, r++, s++)
-	{
-		*r = *s;
-		if (*r == '\0')
-			break;
-	}
-
-#ifdef CYR_RECODE
-	convertstr(VARDATA(result), len, 0);
-#endif
+	for (i = 0; i < len; i++)
+		*r++ = *s++;
 
 	/* blank pad the string if necessary */
 	for (; i < maxlen; i++)
 		*r++ = ' ';
 
+#ifdef CYR_RECODE
+	convertstr(VARDATA(result), len, 0);
+#endif
+
 	PG_RETURN_BPCHAR_P(result);
 }
 
@@ -162,11 +161,12 @@ bpchar(PG_FUNCTION_ARGS)
 
 	len = VARSIZE(source);
 	/* No work if typmod is invalid or supplied data matches it already */
-	if (len < (int32) VARHDRSZ || len == maxlen)
+	if (maxlen < (int32) VARHDRSZ || len == maxlen)
 		PG_RETURN_BPCHAR_P(source);
 
 	if (len > maxlen)
 	{
+		/* Verify that extra characters are spaces, and clip them off */
 #ifdef MULTIBYTE
 		size_t		maxmblen;
 
@@ -179,13 +179,13 @@ bpchar(PG_FUNCTION_ARGS)
 					 maxlen - VARHDRSZ);
 
 		len = maxmblen;
+		Assert(len <= maxlen);
 #else
 		for (i = maxlen - VARHDRSZ; i < len - VARHDRSZ; i++)
 			if (*(VARDATA(source) + i) != ' ')
 				elog(ERROR, "value too long for type character(%d)",
 					 maxlen - VARHDRSZ);
 
-		/* clip extra spaces */
 		len = maxlen;
 #endif
 	}
@@ -196,7 +196,7 @@ bpchar(PG_FUNCTION_ARGS)
 	VARATT_SIZEP(result) = maxlen;
 	r = VARDATA(result);
 
-	for (i = 0; (i < maxlen - VARHDRSZ) && (i < len - VARHDRSZ); i++)
+	for (i = 0; i < len - VARHDRSZ; i++)
 		*r++ = *s++;
 
 	/* blank pad the string if necessary */
@@ -340,10 +340,8 @@ Datum
 varcharin(PG_FUNCTION_ARGS)
 {
 	char	   *s = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
 	Oid			typelem = PG_GETARG_OID(1);
-
 #endif
 	int32		atttypmod = PG_GETARG_INT32(2);
 	VarChar    *result;
@@ -354,6 +352,7 @@ varcharin(PG_FUNCTION_ARGS)
 
 	if (atttypmod >= (int32) VARHDRSZ && len > maxlen)
 	{
+		/* Verify that extra characters are spaces, and clip them off */
 #ifdef MULTIBYTE
 		size_t mbmaxlen = pg_mbcliplen(s, len, maxlen);
 
@@ -361,7 +360,6 @@ varcharin(PG_FUNCTION_ARGS)
 			len = mbmaxlen;
 #else
 		if (strspn(s + maxlen, " ") == len - maxlen)
-			/* clip extra spaces */
 			len = maxlen;
 #endif
 		else
@@ -419,6 +417,7 @@ varchar(PG_FUNCTION_ARGS)
 	int			i;
 
 	len = VARSIZE(source);
+	/* No work if typmod is invalid or supplied data fits it already */
 	if (maxlen < (int32) VARHDRSZ || len <= maxlen)
 		PG_RETURN_VARCHAR_P(source);
 
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 7562a25fdca5399ae0c4b745243c76a38b4e1afe..42df7c06df2e698b8357878b0053684be81b6ef8 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -481,10 +481,16 @@ SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
  text and unknown
 (1 row)
 
+SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
+ Concat char to unknown type  
+------------------------------
+ characters          and text
+(1 row)
+
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
- Concat text to char 
----------------------
- text and characters
+   Concat text to char    
+--------------------------
+ text and characters     
 (1 row)
 
 SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 56510f83dddc9eee78dadaae42adfbc6340d8933..b7f214f4d89c30c573cdb7c13a9149f302f5d5d9 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -160,6 +160,8 @@ SELECT 'unknown' || ' and unknown' AS "Concat unknown types";
 
 SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
 
+SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
+
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
 
 SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";