Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 656df62

Browse files
committed
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: Fabien COELHO <coelho@cri.ensmp.fr>
1 parent 0dac5b5 commit 656df62

File tree

3 files changed

+165
-16
lines changed

3 files changed

+165
-16
lines changed

src/backend/utils/adt/cash.c

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,30 @@ cash_in(PG_FUNCTION_ARGS)
189189
printf("cashin- string is '%s'\n", s);
190190
#endif
191191

192+
/*
193+
* We accumulate the absolute amount in "value" and then apply the sign at
194+
* the end. (The sign can appear before or after the digits, so it would
195+
* be more complicated to do otherwise.) Because of the larger range of
196+
* negative signed integers, we build "value" in the negative and then
197+
* flip the sign at the end, catching most-negative-number overflow if
198+
* necessary.
199+
*/
200+
192201
for (; *s; s++)
193202
{
194203
/* we look for digits as long as we have found less */
195204
/* than the required number of decimal places */
196205
if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
197206
{
198-
value = (value * 10) + (*s - '0');
207+
Cash newvalue = (value * 10) - (*s - '0');
208+
209+
if (newvalue / 10 != value)
210+
ereport(ERROR,
211+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
212+
errmsg("value \"%s\" is out of range for type money",
213+
str)));
214+
215+
value = newvalue;
199216

200217
if (seen_dot)
201218
dec++;
@@ -214,11 +231,27 @@ cash_in(PG_FUNCTION_ARGS)
214231

215232
/* round off if there's another digit */
216233
if (isdigit((unsigned char) *s) && *s >= '5')
217-
value++;
234+
value--; /* remember we build the value in the negative */
235+
236+
if (value > 0)
237+
ereport(ERROR,
238+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
239+
errmsg("value \"%s\" is out of range for type money",
240+
str)));
218241

219242
/* adjust for less than required decimal places */
220243
for (; dec < fpoint; dec++)
221-
value *= 10;
244+
{
245+
Cash newvalue = value * 10;
246+
247+
if (newvalue / 10 != value)
248+
ereport(ERROR,
249+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
250+
errmsg("value \"%s\" is out of range for type money",
251+
str)));
252+
253+
value = newvalue;
254+
}
222255

223256
/*
224257
* should only be trailing digits followed by whitespace, right paren,
@@ -247,7 +280,19 @@ cash_in(PG_FUNCTION_ARGS)
247280
str)));
248281
}
249282

250-
result = value * sgn;
283+
/* If the value is supposed to be positive, flip the sign, but check for
284+
* the most negative number. */
285+
if (sgn > 0)
286+
{
287+
result = -value;
288+
if (result < 0)
289+
ereport(ERROR,
290+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
291+
errmsg("value \"%s\" is out of range for type money",
292+
str)));
293+
}
294+
else
295+
result = value;
251296

252297
#ifdef CASHDEBUG
253298
printf("cashin- result is " INT64_FORMAT "\n", result);

src/test/regress/expected/money.out

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,96 @@ SELECT * FROM money_data;
185185
$123.46
186186
(1 row)
187187

188+
-- input checks
189+
SELECT '1234567890'::money;
190+
money
191+
-------------------
192+
$1,234,567,890.00
193+
(1 row)
194+
195+
SELECT '12345678901234567'::money;
196+
money
197+
----------------------------
198+
$12,345,678,901,234,567.00
199+
(1 row)
200+
201+
SELECT '123456789012345678'::money;
202+
ERROR: value "123456789012345678" is out of range for type money
203+
LINE 1: SELECT '123456789012345678'::money;
204+
^
205+
SELECT '9223372036854775807'::money;
206+
ERROR: value "9223372036854775807" is out of range for type money
207+
LINE 1: SELECT '9223372036854775807'::money;
208+
^
209+
SELECT '-12345'::money;
210+
money
211+
-------------
212+
-$12,345.00
213+
(1 row)
214+
215+
SELECT '-1234567890'::money;
216+
money
217+
--------------------
218+
-$1,234,567,890.00
219+
(1 row)
220+
221+
SELECT '-12345678901234567'::money;
222+
money
223+
-----------------------------
224+
-$12,345,678,901,234,567.00
225+
(1 row)
226+
227+
SELECT '-123456789012345678'::money;
228+
ERROR: value "-123456789012345678" is out of range for type money
229+
LINE 1: SELECT '-123456789012345678'::money;
230+
^
231+
SELECT '-9223372036854775808'::money;
232+
ERROR: value "-9223372036854775808" is out of range for type money
233+
LINE 1: SELECT '-9223372036854775808'::money;
234+
^
235+
-- special characters
236+
SELECT '(1)'::money;
237+
money
238+
--------
239+
-$1.00
240+
(1 row)
241+
242+
SELECT '($123,456.78)'::money;
243+
money
244+
--------------
245+
-$123,456.78
246+
(1 row)
247+
248+
-- documented minimums and maximums
249+
SELECT '-92233720368547758.08'::money;
250+
money
251+
-----------------------------
252+
-$92,233,720,368,547,758.08
253+
(1 row)
254+
255+
SELECT '92233720368547758.07'::money;
256+
money
257+
----------------------------
258+
$92,233,720,368,547,758.07
259+
(1 row)
260+
261+
SELECT '-92233720368547758.09'::money;
262+
ERROR: value "-92233720368547758.09" is out of range for type money
263+
LINE 1: SELECT '-92233720368547758.09'::money;
264+
^
265+
SELECT '92233720368547758.08'::money;
266+
ERROR: value "92233720368547758.08" is out of range for type money
267+
LINE 1: SELECT '92233720368547758.08'::money;
268+
^
269+
-- rounding
270+
SELECT '-92233720368547758.085'::money;
271+
ERROR: value "-92233720368547758.085" is out of range for type money
272+
LINE 1: SELECT '-92233720368547758.085'::money;
273+
^
274+
SELECT '92233720368547758.075'::money;
275+
ERROR: value "92233720368547758.075" is out of range for type money
276+
LINE 1: SELECT '92233720368547758.075'::money;
277+
^
188278
-- Cast int4/int8 to money
189279
SELECT 1234567890::money;
190280
money
@@ -198,10 +288,6 @@ SELECT 12345678901234567::money;
198288
$12,345,678,901,234,567.00
199289
(1 row)
200290

201-
SELECT 123456789012345678::money;
202-
ERROR: bigint out of range
203-
SELECT 9223372036854775807::money;
204-
ERROR: bigint out of range
205291
SELECT (-12345)::money;
206292
money
207293
-------------
@@ -220,10 +306,6 @@ SELECT (-12345678901234567)::money;
220306
-$12,345,678,901,234,567.00
221307
(1 row)
222308

223-
SELECT (-123456789012345678)::money;
224-
ERROR: bigint out of range
225-
SELECT (-9223372036854775808)::money;
226-
ERROR: bigint out of range
227309
SELECT 1234567890::int4::money;
228310
money
229311
-------------------

src/test/regress/sql/money.sql

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,38 @@ DELETE FROM money_data;
5757
INSERT INTO money_data VALUES ('$123.459');
5858
SELECT * FROM money_data;
5959

60+
-- input checks
61+
SELECT '1234567890'::money;
62+
SELECT '12345678901234567'::money;
63+
SELECT '123456789012345678'::money;
64+
SELECT '9223372036854775807'::money;
65+
SELECT '-12345'::money;
66+
SELECT '-1234567890'::money;
67+
SELECT '-12345678901234567'::money;
68+
SELECT '-123456789012345678'::money;
69+
SELECT '-9223372036854775808'::money;
70+
71+
-- special characters
72+
SELECT '(1)'::money;
73+
SELECT '($123,456.78)'::money;
74+
75+
-- documented minimums and maximums
76+
SELECT '-92233720368547758.08'::money;
77+
SELECT '92233720368547758.07'::money;
78+
79+
SELECT '-92233720368547758.09'::money;
80+
SELECT '92233720368547758.08'::money;
81+
82+
-- rounding
83+
SELECT '-92233720368547758.085'::money;
84+
SELECT '92233720368547758.075'::money;
85+
6086
-- Cast int4/int8 to money
6187
SELECT 1234567890::money;
6288
SELECT 12345678901234567::money;
63-
SELECT 123456789012345678::money;
64-
SELECT 9223372036854775807::money;
6589
SELECT (-12345)::money;
6690
SELECT (-1234567890)::money;
6791
SELECT (-12345678901234567)::money;
68-
SELECT (-123456789012345678)::money;
69-
SELECT (-9223372036854775808)::money;
7092
SELECT 1234567890::int4::money;
7193
SELECT 12345678901234567::int8::money;
7294
SELECT (-1234567890)::int4::money;

0 commit comments

Comments
 (0)