diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index b336185df7e16439e61f990789569b5ac4bbd01b..a146b0a0bc8461f863ada546f6a1137bdc751c38 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -189,13 +189,30 @@ cash_in(PG_FUNCTION_ARGS)
 	printf("cashin- string is '%s'\n", s);
 #endif
 
+	/*
+	 * We accumulate the absolute amount in "value" and then apply the sign at
+	 * the end.  (The sign can appear before or after the digits, so it would
+	 * be more complicated to do otherwise.)  Because of the larger range of
+	 * negative signed integers, we build "value" in the negative and then
+	 * flip the sign at the end, catching most-negative-number overflow if
+	 * necessary.
+	 */
+
 	for (; *s; s++)
 	{
 		/* we look for digits as long as we have found less */
 		/* than the required number of decimal places */
 		if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
 		{
-			value = (value * 10) + (*s - '0');
+			Cash newvalue = (value * 10) - (*s - '0');
+
+			if (newvalue / 10 != value)
+				ereport(ERROR,
+						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+						 errmsg("value \"%s\" is out of range for type money",
+								str)));
+
+			value = newvalue;
 
 			if (seen_dot)
 				dec++;
@@ -214,11 +231,27 @@ cash_in(PG_FUNCTION_ARGS)
 
 	/* round off if there's another digit */
 	if (isdigit((unsigned char) *s) && *s >= '5')
-		value++;
+		value--;  /* remember we build the value in the negative */
+
+	if (value > 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value \"%s\" is out of range for type money",
+						str)));
 
 	/* adjust for less than required decimal places */
 	for (; dec < fpoint; dec++)
-		value *= 10;
+	{
+		Cash newvalue = value * 10;
+
+		if (newvalue / 10 != value)
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("value \"%s\" is out of range for type money",
+							str)));
+
+		value = newvalue;
+	}
 
 	/*
 	 * should only be trailing digits followed by whitespace, right paren,
@@ -247,7 +280,19 @@ cash_in(PG_FUNCTION_ARGS)
 							str)));
 	}
 
-	result = value * sgn;
+	/* If the value is supposed to be positive, flip the sign, but check for
+	 * the most negative number. */
+	if (sgn > 0)
+	{
+		result = -value;
+		if (result < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("value \"%s\" is out of range for type money",
+							str)));
+	}
+	else
+		result = value;
 
 #ifdef CASHDEBUG
 	printf("cashin- result is " INT64_FORMAT "\n", result);
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index 538235c4cc26820660e95fc88474f0bbbc197c45..5695f87500671b4593834c09137fa2bcc252e42f 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -185,6 +185,96 @@ SELECT * FROM money_data;
  $123.46
 (1 row)
 
+-- input checks
+SELECT '1234567890'::money;
+       money       
+-------------------
+ $1,234,567,890.00
+(1 row)
+
+SELECT '12345678901234567'::money;
+           money            
+----------------------------
+ $12,345,678,901,234,567.00
+(1 row)
+
+SELECT '123456789012345678'::money;
+ERROR:  value "123456789012345678" is out of range for type money
+LINE 1: SELECT '123456789012345678'::money;
+               ^
+SELECT '9223372036854775807'::money;
+ERROR:  value "9223372036854775807" is out of range for type money
+LINE 1: SELECT '9223372036854775807'::money;
+               ^
+SELECT '-12345'::money;
+    money    
+-------------
+ -$12,345.00
+(1 row)
+
+SELECT '-1234567890'::money;
+       money        
+--------------------
+ -$1,234,567,890.00
+(1 row)
+
+SELECT '-12345678901234567'::money;
+            money            
+-----------------------------
+ -$12,345,678,901,234,567.00
+(1 row)
+
+SELECT '-123456789012345678'::money;
+ERROR:  value "-123456789012345678" is out of range for type money
+LINE 1: SELECT '-123456789012345678'::money;
+               ^
+SELECT '-9223372036854775808'::money;
+ERROR:  value "-9223372036854775808" is out of range for type money
+LINE 1: SELECT '-9223372036854775808'::money;
+               ^
+-- special characters
+SELECT '(1)'::money;
+ money  
+--------
+ -$1.00
+(1 row)
+
+SELECT '($123,456.78)'::money;
+    money     
+--------------
+ -$123,456.78
+(1 row)
+
+-- documented minimums and maximums
+SELECT '-92233720368547758.08'::money;
+            money            
+-----------------------------
+ -$92,233,720,368,547,758.08
+(1 row)
+
+SELECT '92233720368547758.07'::money;
+           money            
+----------------------------
+ $92,233,720,368,547,758.07
+(1 row)
+
+SELECT '-92233720368547758.09'::money;
+ERROR:  value "-92233720368547758.09" is out of range for type money
+LINE 1: SELECT '-92233720368547758.09'::money;
+               ^
+SELECT '92233720368547758.08'::money;
+ERROR:  value "92233720368547758.08" is out of range for type money
+LINE 1: SELECT '92233720368547758.08'::money;
+               ^
+-- rounding
+SELECT '-92233720368547758.085'::money;
+ERROR:  value "-92233720368547758.085" is out of range for type money
+LINE 1: SELECT '-92233720368547758.085'::money;
+               ^
+SELECT '92233720368547758.075'::money;
+ERROR:  value "92233720368547758.075" is out of range for type money
+LINE 1: SELECT '92233720368547758.075'::money;
+               ^
 -- Cast int4/int8 to money
 SELECT 1234567890::money;
        money       
@@ -198,10 +288,6 @@ SELECT 12345678901234567::money;
  $12,345,678,901,234,567.00
 (1 row)
 
-SELECT 123456789012345678::money;
-ERROR:  bigint out of range
-SELECT 9223372036854775807::money;
-ERROR:  bigint out of range
 SELECT (-12345)::money;
     money    
 -------------
@@ -220,10 +306,6 @@ SELECT (-12345678901234567)::money;
  -$12,345,678,901,234,567.00
 (1 row)
 
-SELECT (-123456789012345678)::money;
-ERROR:  bigint out of range
-SELECT (-9223372036854775808)::money;
-ERROR:  bigint out of range
 SELECT 1234567890::int4::money;
        money       
 -------------------
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 09b9476b706172f2e2d1758e95f8c684029f06dc..561ccb527f8fd8a22431212bc5f73f9924e6cc2f 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -57,16 +57,38 @@ DELETE FROM money_data;
 INSERT INTO money_data VALUES ('$123.459');
 SELECT * FROM money_data;
 
+-- input checks
+SELECT '1234567890'::money;
+SELECT '12345678901234567'::money;
+SELECT '123456789012345678'::money;
+SELECT '9223372036854775807'::money;
+SELECT '-12345'::money;
+SELECT '-1234567890'::money;
+SELECT '-12345678901234567'::money;
+SELECT '-123456789012345678'::money;
+SELECT '-9223372036854775808'::money;
+
+-- special characters
+SELECT '(1)'::money;
+SELECT '($123,456.78)'::money;
+
+-- documented minimums and maximums
+SELECT '-92233720368547758.08'::money;
+SELECT '92233720368547758.07'::money;
+
+SELECT '-92233720368547758.09'::money;
+SELECT '92233720368547758.08'::money;
+
+-- rounding
+SELECT '-92233720368547758.085'::money;
+SELECT '92233720368547758.075'::money;
+
 -- Cast int4/int8 to money
 SELECT 1234567890::money;
 SELECT 12345678901234567::money;
-SELECT 123456789012345678::money;
-SELECT 9223372036854775807::money;
 SELECT (-12345)::money;
 SELECT (-1234567890)::money;
 SELECT (-12345678901234567)::money;
-SELECT (-123456789012345678)::money;
-SELECT (-9223372036854775808)::money;
 SELECT 1234567890::int4::money;
 SELECT 12345678901234567::int8::money;
 SELECT (-1234567890)::int4::money;