1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-16 06:01:02 +03:00

Add overflow checks to money type.

None of the arithmetic functions for the the money type handle
overflow.  This commit introduces several helper functions with
overflow checking and makes use of them in the money type's
arithmetic functions.

Fixes bug .

Reported-by: Alexander Lakhin
Author: Joseph Koshakow
Discussion: https://postgr.es/m/18240-c5da758d7dc1ecf0%40postgresql.org
Discussion: https://postgr.es/m/CAAvxfHdBPOyEGS7s%2Bxf4iaW0-cgiq25jpYdWBqQqvLtLe_t6tw%40mail.gmail.com
Backpatch-through: 12
This commit is contained in:
Nathan Bossart
2024-07-19 11:52:32 -05:00
parent 80c34692e8
commit 3764ee47f7
3 changed files with 124 additions and 80 deletions
src
backend
utils
test
regress

@ -26,6 +26,7 @@
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/cash.h"
#include "utils/float.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
@ -86,6 +87,82 @@ num_word(Cash value)
return buf;
} /* num_word() */
static inline Cash
cash_pl_cash(Cash c1, Cash c2)
{
Cash res;
if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range")));
return res;
}
static inline Cash
cash_mi_cash(Cash c1, Cash c2)
{
Cash res;
if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range")));
return res;
}
static inline Cash
cash_mul_float8(Cash c, float8 f)
{
float8 res = rint(float8_mul((float8) c, f));
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range")));
return (Cash) res;
}
static inline Cash
cash_div_float8(Cash c, float8 f)
{
float8 res = rint(float8_div((float8) c, f));
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range")));
return (Cash) res;
}
static inline Cash
cash_mul_int64(Cash c, int64 i)
{
Cash res;
if (unlikely(pg_mul_s64_overflow(c, i, &res)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range")));
return res;
}
static inline Cash
cash_div_int64(Cash c, int64 i)
{
if (unlikely(i == 0))
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
return c / i;
}
/* cash_in()
* Convert a string to a cash data type.
* Format is [$]###[,]###[.##]
@ -612,11 +689,8 @@ cash_pl(PG_FUNCTION_ARGS)
{
Cash c1 = PG_GETARG_CASH(0);
Cash c2 = PG_GETARG_CASH(1);
Cash result;
result = c1 + c2;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_pl_cash(c1, c2));
}
@ -628,11 +702,8 @@ cash_mi(PG_FUNCTION_ARGS)
{
Cash c1 = PG_GETARG_CASH(0);
Cash c2 = PG_GETARG_CASH(1);
Cash result;
result = c1 - c2;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mi_cash(c1, c2));
}
@ -664,10 +735,8 @@ cash_mul_flt8(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
float8 f = PG_GETARG_FLOAT8(1);
Cash result;
result = rint(c * f);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_float8(c, f));
}
@ -679,10 +748,8 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
{
float8 f = PG_GETARG_FLOAT8(0);
Cash c = PG_GETARG_CASH(1);
Cash result;
result = rint(f * c);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_float8(c, f));
}
@ -694,15 +761,8 @@ cash_div_flt8(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
float8 f = PG_GETARG_FLOAT8(1);
Cash result;
if (f == 0.0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = rint(c / f);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_div_float8(c, f));
}
@ -714,10 +774,8 @@ cash_mul_flt4(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
float4 f = PG_GETARG_FLOAT4(1);
Cash result;
result = rint(c * (float8) f);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
}
@ -729,10 +787,8 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
{
float4 f = PG_GETARG_FLOAT4(0);
Cash c = PG_GETARG_CASH(1);
Cash result;
result = rint((float8) f * c);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
}
@ -745,15 +801,8 @@ cash_div_flt4(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
float4 f = PG_GETARG_FLOAT4(1);
Cash result;
if (f == 0.0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = rint(c / (float8) f);
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_div_float8(c, (float8) f));
}
@ -765,10 +814,8 @@ cash_mul_int8(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int64 i = PG_GETARG_INT64(1);
Cash result;
result = c * i;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, i));
}
@ -780,10 +827,8 @@ int8_mul_cash(PG_FUNCTION_ARGS)
{
int64 i = PG_GETARG_INT64(0);
Cash c = PG_GETARG_CASH(1);
Cash result;
result = i * c;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, i));
}
/* cash_div_int8()
@ -794,16 +839,8 @@ cash_div_int8(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int64 i = PG_GETARG_INT64(1);
Cash result;
if (i == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = c / i;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_div_int64(c, i));
}
@ -815,10 +852,8 @@ cash_mul_int4(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int32 i = PG_GETARG_INT32(1);
Cash result;
result = c * i;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
}
@ -830,10 +865,8 @@ int4_mul_cash(PG_FUNCTION_ARGS)
{
int32 i = PG_GETARG_INT32(0);
Cash c = PG_GETARG_CASH(1);
Cash result;
result = i * c;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
}
@ -846,16 +879,8 @@ cash_div_int4(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int32 i = PG_GETARG_INT32(1);
Cash result;
if (i == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = c / i;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_div_int64(c, (int64) i));
}
@ -867,10 +892,8 @@ cash_mul_int2(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int16 s = PG_GETARG_INT16(1);
Cash result;
result = c * s;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
}
/* int2_mul_cash()
@ -881,10 +904,8 @@ int2_mul_cash(PG_FUNCTION_ARGS)
{
int16 s = PG_GETARG_INT16(0);
Cash c = PG_GETARG_CASH(1);
Cash result;
result = s * c;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
}
/* cash_div_int2()
@ -896,15 +917,8 @@ cash_div_int2(PG_FUNCTION_ARGS)
{
Cash c = PG_GETARG_CASH(0);
int16 s = PG_GETARG_INT16(1);
Cash result;
if (s == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = c / s;
PG_RETURN_CASH(result);
PG_RETURN_CASH(cash_div_int64(c, (int64) s));
}
/* cashlarger()

@ -528,3 +528,22 @@ SELECT '-92233720368547758.08'::money::numeric;
-92233720368547758.08
(1 row)
-- overflow checks
SELECT '92233720368547758.07'::money + '0.01'::money;
ERROR: money out of range
SELECT '-92233720368547758.08'::money - '0.01'::money;
ERROR: money out of range
SELECT '92233720368547758.07'::money * 2::float8;
ERROR: money out of range
SELECT '-1'::money / 1.175494e-38::float4;
ERROR: money out of range
SELECT '92233720368547758.07'::money * 2::int4;
ERROR: money out of range
SELECT '1'::money / 0::int2;
ERROR: division by zero
SELECT '42'::money * 'inf'::float8;
ERROR: money out of range
SELECT '42'::money * '-inf'::float8;
ERROR: money out of range
SELECT '42'::money * 'nan'::float4;
ERROR: money out of range

@ -135,3 +135,14 @@ SELECT '12345678901234567'::money::numeric;
SELECT '-12345678901234567'::money::numeric;
SELECT '92233720368547758.07'::money::numeric;
SELECT '-92233720368547758.08'::money::numeric;
-- overflow checks
SELECT '92233720368547758.07'::money + '0.01'::money;
SELECT '-92233720368547758.08'::money - '0.01'::money;
SELECT '92233720368547758.07'::money * 2::float8;
SELECT '-1'::money / 1.175494e-38::float4;
SELECT '92233720368547758.07'::money * 2::int4;
SELECT '1'::money / 0::int2;
SELECT '42'::money * 'inf'::float8;
SELECT '42'::money * '-inf'::float8;
SELECT '42'::money * 'nan'::float4;