The input function of the money type has no overflow checks: => select '12345678901234567890'::money; money ----------------------------- -$13,639,628,150,831,692.72 (1 row)
The tests in the regression test file money.sql are bogus because they only test the overflow checks of the bigint type before the cast. Here is a patch that adds appropriate checks and tests. We could probably remove the bogus tests. (Is checking for < 0 a valid overflow check? We save the sign until the very end, so it ought to work. The code in int8.c works differently there.) -- Peter Eisentraut http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 6fcce1d6c3685cfb5bacdb89981d4f6a3911dded Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <pete...@gmx.net> Date: Fri, 5 Aug 2016 11:50:53 -0400 Subject: [PATCH] Add overflow checks to money type input function --- src/backend/utils/adt/cash.c | 18 ++++++++++++++ src/test/regress/expected/money.out | 47 +++++++++++++++++++++++++++++++++++++ src/test/regress/sql/money.sql | 11 +++++++++ 3 files changed, 76 insertions(+) diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index b336185..0c06f71 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -197,6 +197,12 @@ cash_in(PG_FUNCTION_ARGS) { value = (value * 10) + (*s - '0'); + if (value < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type money", + str))); + if (seen_dot) dec++; } @@ -216,10 +222,22 @@ cash_in(PG_FUNCTION_ARGS) if (isdigit((unsigned char) *s) && *s >= '5') value++; + 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; + if (value < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type money", + str))); + /* * should only be trailing digits followed by whitespace, right paren, * trailing sign, and/or trailing currency symbol diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out index 538235c..206f5c4 100644 --- a/src/test/regress/expected/money.out +++ b/src/test/regress/expected/money.out @@ -185,6 +185,53 @@ 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; + ^ -- Cast int4/int8 to money SELECT 1234567890::money; money diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql index 09b9476..d07a616 100644 --- a/src/test/regress/sql/money.sql +++ b/src/test/regress/sql/money.sql @@ -57,6 +57,17 @@ CREATE TABLE money_data (m money); 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; + -- Cast int4/int8 to money SELECT 1234567890::money; SELECT 12345678901234567::money; -- 2.9.2
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers