diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 2efdd136005ce3a181bd6685f40ee7b49950eeda..d0ac448732e6d71ae384519f57ba28caffbadbda 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.70 2000/02/21 18:47:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.71 2000/02/26 21:11:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
 #include "nodes/makefuncs.h"
 #include "nodes/params.h"
 #include "nodes/relation.h"
@@ -29,6 +30,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "utils/builtins.h"
+#include "utils/syscache.h"
 
 static Node *parser_typecast_constant(Value *expr, TypeName *typename);
 static Node *parser_typecast_expression(ParseState *pstate,
@@ -701,6 +703,15 @@ exprTypmod(Node *expr)
 				}
 			}
 			break;
+		case T_Expr:
+			{
+				int32	coercedTypmod;
+
+				/* Be smart about length-coercion functions... */
+				if (exprIsLengthCoercion(expr, &coercedTypmod))
+					return coercedTypmod;
+			}
+			break;
 		case T_RelabelType:
 			return ((RelabelType *) expr)->resulttypmod;
 			break;
@@ -710,6 +721,97 @@ exprTypmod(Node *expr)
 	return -1;
 }
 
+/*
+ * exprIsLengthCoercion
+ *		Detect whether an expression tree is an application of a datatype's
+ *		typmod-coercion function.  Optionally extract the result's typmod.
+ *
+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
+ * is a length-coercion function, else -1 is stored there.
+ *
+ * We assume that a two-argument function named for a datatype, whose
+ * output and first argument types are that datatype, and whose second
+ * input is an int32 constant, represents a forced length coercion.
+ * XXX It'd be better if the parsetree retained some explicit indication
+ * of the coercion, so we didn't need these heuristics.
+ */
+bool
+exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+{
+	Func	   *func;
+	Const	   *second_arg;
+	HeapTuple	tup;
+	Form_pg_proc procStruct;
+	Form_pg_type typeStruct;
+
+	if (coercedTypmod != NULL)
+		*coercedTypmod = -1;	/* default result on failure */
+
+	/* Is it a function-call at all? */
+	if (expr == NULL ||
+		! IsA(expr, Expr) ||
+		((Expr *) expr)->opType != FUNC_EXPR)
+		return false;
+	func = (Func *) (((Expr *) expr)->oper);
+	Assert(IsA(func, Func));
+
+	/*
+	 * If it's not a two-argument function with the second argument being
+	 * an int4 constant, it can't have been created from a length coercion.
+	 */
+	if (length(((Expr *) expr)->args) != 2)
+		return false;
+	second_arg = (Const *) lsecond(((Expr *) expr)->args);
+	if (! IsA(second_arg, Const) ||
+		second_arg->consttype != INT4OID ||
+		second_arg->constisnull)
+		return false;
+
+	/*
+	 * Lookup the function in pg_proc
+	 */
+	tup = SearchSysCacheTuple(PROCOID,
+							  ObjectIdGetDatum(func->funcid),
+							  0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "cache lookup for proc %u failed", func->funcid);
+	procStruct = (Form_pg_proc) GETSTRUCT(tup);
+
+	/*
+	 * It must be a function with two arguments where the first is of
+	 * the same type as the return value and the second is an int4.
+	 * Also, just to be sure, check return type agrees with expr node.
+	 */
+	if (procStruct->pronargs != 2 ||
+		procStruct->prorettype != procStruct->proargtypes[0] ||
+		procStruct->proargtypes[1] != INT4OID ||
+		procStruct->prorettype != ((Expr *) expr)->typeOid)
+		return false;
+
+	/*
+	 * Furthermore, the name of the function must be the same
+	 * as the argument/result type's name.
+	 */
+	tup = SearchSysCacheTuple(TYPEOID,
+							  ObjectIdGetDatum(procStruct->prorettype),
+							  0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "cache lookup for type %u failed",
+			 procStruct->prorettype);
+	typeStruct = (Form_pg_type) GETSTRUCT(tup);
+	if (strncmp(NameStr(procStruct->proname),
+				NameStr(typeStruct->typname),
+				NAMEDATALEN) != 0)
+		return false;
+
+	/*
+	 * OK, it is indeed a length-coercion function.
+	 */
+	if (coercedTypmod != NULL)
+		*coercedTypmod = DatumGetInt32(second_arg->constvalue);
+	return true;
+}
+
 /*
  * Produce an appropriate Const node from a constant value produced
  * by the parser and an explicit type name to cast to.
diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h
index 5d20173745c2e548f2b9c55211c07194cf96b80e..50d7b284bb5508fc76a40e8a146dbc37b04830da 100644
--- a/src/include/parser/parse_expr.h
+++ b/src/include/parser/parse_expr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_expr.h,v 1.16 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parse_expr.h,v 1.17 2000/02/26 21:11:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,5 +23,6 @@
 extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
 extern Oid	exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
+extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
 
 #endif	 /* PARSE_EXPR_H */