Commit 656df624 authored by Peter Eisentraut's avatar Peter Eisentraut

Add overflow checks to money type input function

The money type input function did not have any overflow checks at all.
There were some regression tests that purported to check for overflow,
but they actually checked for the overflow behavior of the int8 type
before casting to money.  Remove those unnecessary checks and add some
that actually check the money input function.
Reviewed-by: default avatarFabien COELHO <coelho@cri.ensmp.fr>
parent 0dac5b51
...@@ -189,13 +189,30 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -189,13 +189,30 @@ cash_in(PG_FUNCTION_ARGS)
printf("cashin- string is '%s'\n", s); printf("cashin- string is '%s'\n", s);
#endif #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++) for (; *s; s++)
{ {
/* we look for digits as long as we have found less */ /* we look for digits as long as we have found less */
/* than the required number of decimal places */ /* than the required number of decimal places */
if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint)) 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) if (seen_dot)
dec++; dec++;
...@@ -214,11 +231,27 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -214,11 +231,27 @@ cash_in(PG_FUNCTION_ARGS)
/* round off if there's another digit */ /* round off if there's another digit */
if (isdigit((unsigned char) *s) && *s >= '5') 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 */ /* adjust for less than required decimal places */
for (; dec < fpoint; dec++) 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, * should only be trailing digits followed by whitespace, right paren,
...@@ -247,7 +280,19 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -247,7 +280,19 @@ cash_in(PG_FUNCTION_ARGS)
str))); 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 #ifdef CASHDEBUG
printf("cashin- result is " INT64_FORMAT "\n", result); printf("cashin- result is " INT64_FORMAT "\n", result);
......
...@@ -185,6 +185,96 @@ SELECT * FROM money_data; ...@@ -185,6 +185,96 @@ SELECT * FROM money_data;
$123.46 $123.46
(1 row) (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 -- Cast int4/int8 to money
SELECT 1234567890::money; SELECT 1234567890::money;
money money
...@@ -198,10 +288,6 @@ SELECT 12345678901234567::money; ...@@ -198,10 +288,6 @@ SELECT 12345678901234567::money;
$12,345,678,901,234,567.00 $12,345,678,901,234,567.00
(1 row) (1 row)
SELECT 123456789012345678::money;
ERROR: bigint out of range
SELECT 9223372036854775807::money;
ERROR: bigint out of range
SELECT (-12345)::money; SELECT (-12345)::money;
money money
------------- -------------
...@@ -220,10 +306,6 @@ SELECT (-12345678901234567)::money; ...@@ -220,10 +306,6 @@ SELECT (-12345678901234567)::money;
-$12,345,678,901,234,567.00 -$12,345,678,901,234,567.00
(1 row) (1 row)
SELECT (-123456789012345678)::money;
ERROR: bigint out of range
SELECT (-9223372036854775808)::money;
ERROR: bigint out of range
SELECT 1234567890::int4::money; SELECT 1234567890::int4::money;
money money
------------------- -------------------
......
...@@ -57,16 +57,38 @@ DELETE FROM money_data; ...@@ -57,16 +57,38 @@ DELETE FROM money_data;
INSERT INTO money_data VALUES ('$123.459'); INSERT INTO money_data VALUES ('$123.459');
SELECT * FROM money_data; 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 -- Cast int4/int8 to money
SELECT 1234567890::money; SELECT 1234567890::money;
SELECT 12345678901234567::money; SELECT 12345678901234567::money;
SELECT 123456789012345678::money;
SELECT 9223372036854775807::money;
SELECT (-12345)::money; SELECT (-12345)::money;
SELECT (-1234567890)::money; SELECT (-1234567890)::money;
SELECT (-12345678901234567)::money; SELECT (-12345678901234567)::money;
SELECT (-123456789012345678)::money;
SELECT (-9223372036854775808)::money;
SELECT 1234567890::int4::money; SELECT 1234567890::int4::money;
SELECT 12345678901234567::int8::money; SELECT 12345678901234567::int8::money;
SELECT (-1234567890)::int4::money; SELECT (-1234567890)::int4::money;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment