diff options
author | Nathan Bossart | 2024-07-19 16:52:32 +0000 |
---|---|---|
committer | Nathan Bossart | 2024-07-19 16:52:32 +0000 |
commit | 22b0ccd65d275d227a7d911aede12d34e1b5dfc9 (patch) | |
tree | 614ed9fcfcb4b8337ab9d85c3352fb69faf46a75 /src/backend | |
parent | aa607980aee08416211f003ab41aa750f5559712 (diff) |
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
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/utils/adt/cash.c | 174 |
1 files changed, 94 insertions, 80 deletions
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index 32fbad2f57d..b20c358486d 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -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() |