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 #18240. 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:
parent
aa607980ae
commit
22b0ccd65d
@ -26,6 +26,7 @@
|
|||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/cash.h"
|
#include "utils/cash.h"
|
||||||
|
#include "utils/float.h"
|
||||||
#include "utils/numeric.h"
|
#include "utils/numeric.h"
|
||||||
#include "utils/pg_locale.h"
|
#include "utils/pg_locale.h"
|
||||||
|
|
||||||
@ -86,6 +87,82 @@ num_word(Cash value)
|
|||||||
return buf;
|
return buf;
|
||||||
} /* num_word() */
|
} /* 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()
|
/* cash_in()
|
||||||
* Convert a string to a cash data type.
|
* Convert a string to a cash data type.
|
||||||
* Format is [$]###[,]###[.##]
|
* Format is [$]###[,]###[.##]
|
||||||
@ -612,11 +689,8 @@ cash_pl(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c1 = PG_GETARG_CASH(0);
|
Cash c1 = PG_GETARG_CASH(0);
|
||||||
Cash c2 = PG_GETARG_CASH(1);
|
Cash c2 = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = c1 + c2;
|
PG_RETURN_CASH(cash_pl_cash(c1, c2));
|
||||||
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -628,11 +702,8 @@ cash_mi(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c1 = PG_GETARG_CASH(0);
|
Cash c1 = PG_GETARG_CASH(0);
|
||||||
Cash c2 = PG_GETARG_CASH(1);
|
Cash c2 = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = c1 - c2;
|
PG_RETURN_CASH(cash_mi_cash(c1, c2));
|
||||||
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -664,10 +735,8 @@ cash_mul_flt8(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
float8 f = PG_GETARG_FLOAT8(1);
|
float8 f = PG_GETARG_FLOAT8(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = rint(c * f);
|
PG_RETURN_CASH(cash_mul_float8(c, f));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -679,10 +748,8 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
float8 f = PG_GETARG_FLOAT8(0);
|
float8 f = PG_GETARG_FLOAT8(0);
|
||||||
Cash c = PG_GETARG_CASH(1);
|
Cash c = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = rint(f * c);
|
PG_RETURN_CASH(cash_mul_float8(c, f));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -694,15 +761,8 @@ cash_div_flt8(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
float8 f = PG_GETARG_FLOAT8(1);
|
float8 f = PG_GETARG_FLOAT8(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
if (f == 0.0)
|
PG_RETURN_CASH(cash_div_float8(c, f));
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
||||||
errmsg("division by zero")));
|
|
||||||
|
|
||||||
result = rint(c / f);
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -714,10 +774,8 @@ cash_mul_flt4(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
float4 f = PG_GETARG_FLOAT4(1);
|
float4 f = PG_GETARG_FLOAT4(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = rint(c * (float8) f);
|
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -729,10 +787,8 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
float4 f = PG_GETARG_FLOAT4(0);
|
float4 f = PG_GETARG_FLOAT4(0);
|
||||||
Cash c = PG_GETARG_CASH(1);
|
Cash c = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = rint((float8) f * c);
|
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -745,15 +801,8 @@ cash_div_flt4(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
float4 f = PG_GETARG_FLOAT4(1);
|
float4 f = PG_GETARG_FLOAT4(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
if (f == 0.0)
|
PG_RETURN_CASH(cash_div_float8(c, (float8) f));
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
||||||
errmsg("division by zero")));
|
|
||||||
|
|
||||||
result = rint(c / (float8) f);
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -765,10 +814,8 @@ cash_mul_int8(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int64 i = PG_GETARG_INT64(1);
|
int64 i = PG_GETARG_INT64(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = c * i;
|
PG_RETURN_CASH(cash_mul_int64(c, i));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -780,10 +827,8 @@ int8_mul_cash(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
int64 i = PG_GETARG_INT64(0);
|
int64 i = PG_GETARG_INT64(0);
|
||||||
Cash c = PG_GETARG_CASH(1);
|
Cash c = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = i * c;
|
PG_RETURN_CASH(cash_mul_int64(c, i));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cash_div_int8()
|
/* cash_div_int8()
|
||||||
@ -794,16 +839,8 @@ cash_div_int8(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int64 i = PG_GETARG_INT64(1);
|
int64 i = PG_GETARG_INT64(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
if (i == 0)
|
PG_RETURN_CASH(cash_div_int64(c, i));
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
||||||
errmsg("division by zero")));
|
|
||||||
|
|
||||||
result = c / i;
|
|
||||||
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -815,10 +852,8 @@ cash_mul_int4(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int32 i = PG_GETARG_INT32(1);
|
int32 i = PG_GETARG_INT32(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = c * i;
|
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -830,10 +865,8 @@ int4_mul_cash(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
int32 i = PG_GETARG_INT32(0);
|
int32 i = PG_GETARG_INT32(0);
|
||||||
Cash c = PG_GETARG_CASH(1);
|
Cash c = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = i * c;
|
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -846,16 +879,8 @@ cash_div_int4(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int32 i = PG_GETARG_INT32(1);
|
int32 i = PG_GETARG_INT32(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
if (i == 0)
|
PG_RETURN_CASH(cash_div_int64(c, (int64) i));
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
||||||
errmsg("division by zero")));
|
|
||||||
|
|
||||||
result = c / i;
|
|
||||||
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -867,10 +892,8 @@ cash_mul_int2(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int16 s = PG_GETARG_INT16(1);
|
int16 s = PG_GETARG_INT16(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = c * s;
|
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* int2_mul_cash()
|
/* int2_mul_cash()
|
||||||
@ -881,10 +904,8 @@ int2_mul_cash(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
int16 s = PG_GETARG_INT16(0);
|
int16 s = PG_GETARG_INT16(0);
|
||||||
Cash c = PG_GETARG_CASH(1);
|
Cash c = PG_GETARG_CASH(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
result = s * c;
|
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cash_div_int2()
|
/* cash_div_int2()
|
||||||
@ -896,15 +917,8 @@ cash_div_int2(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Cash c = PG_GETARG_CASH(0);
|
Cash c = PG_GETARG_CASH(0);
|
||||||
int16 s = PG_GETARG_INT16(1);
|
int16 s = PG_GETARG_INT16(1);
|
||||||
Cash result;
|
|
||||||
|
|
||||||
if (s == 0)
|
PG_RETURN_CASH(cash_div_int64(c, (int64) s));
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
||||||
errmsg("division by zero")));
|
|
||||||
|
|
||||||
result = c / s;
|
|
||||||
PG_RETURN_CASH(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cashlarger()
|
/* cashlarger()
|
||||||
|
@ -528,3 +528,22 @@ SELECT '-92233720368547758.08'::money::numeric;
|
|||||||
-92233720368547758.08
|
-92233720368547758.08
|
||||||
(1 row)
|
(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 '-12345678901234567'::money::numeric;
|
||||||
SELECT '92233720368547758.07'::money::numeric;
|
SELECT '92233720368547758.07'::money::numeric;
|
||||||
SELECT '-92233720368547758.08'::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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user