aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/cash.c53
-rw-r--r--src/test/regress/expected/money.out98
-rw-r--r--src/test/regress/sql/money.sql30
3 files changed, 165 insertions, 16 deletions
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index b336185df7e..a146b0a0bc8 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 538235c4cc2..5695f875006 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 09b9476b706..561ccb527f8 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;