diff options
author | Dean Rasheed <dean.a.rasheed@gmail.com> | 2021-07-10 12:42:59 +0100 |
---|---|---|
committer | Dean Rasheed <dean.a.rasheed@gmail.com> | 2021-07-10 12:42:59 +0100 |
commit | e7fc488ad67caaad33f6d5177081884495cb81cb (patch) | |
tree | d152ba50a9e497483e443335a7caa2dbbd79ece8 /src/backend/utils/adt/numeric.c | |
parent | 53c38a086a8001d63401671755638bc95c7fa1c7 (diff) | |
download | postgresql-e7fc488ad67caaad33f6d5177081884495cb81cb.tar.gz postgresql-e7fc488ad67caaad33f6d5177081884495cb81cb.zip |
Fix numeric_mul() overflow due to too many digits after decimal point.
This fixes an overflow error when using the numeric * operator if the
result has more than 16383 digits after the decimal point by rounding
the result. Overflow errors should only occur if the result has too
many digits *before* the decimal point.
Discussion: https://postgr.es/m/CAEZATCUmeFWCrq2dNzZpRj5+6LfN85jYiDoqm+ucSXhb9U2TbA@mail.gmail.com
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r-- | src/backend/utils/adt/numeric.c | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index bc71326fc8a..2a0f68f98b2 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -233,6 +233,7 @@ struct NumericData */ #define NUMERIC_DSCALE_MASK 0x3FFF +#define NUMERIC_DSCALE_MAX NUMERIC_DSCALE_MASK #define NUMERIC_SIGN(n) \ (NUMERIC_IS_SHORT(n) ? \ @@ -2958,7 +2959,11 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error) * Unlike add_var() and sub_var(), mul_var() will round its result. In the * case of numeric_mul(), which is invoked for the * operator on numerics, * we request exact representation for the product (rscale = sum(dscale of - * arg1, dscale of arg2)). + * arg1, dscale of arg2)). If the exact result has more digits after the + * decimal point than can be stored in a numeric, we round it. Rounding + * after computing the exact result ensures that the final result is + * correctly rounded (rounding in mul_var() using a truncated product + * would not guarantee this). */ init_var_from_num(num1, &arg1); init_var_from_num(num2, &arg2); @@ -2966,6 +2971,9 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error) init_var(&result); mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale); + if (result.dscale > NUMERIC_DSCALE_MAX) + round_var(&result, NUMERIC_DSCALE_MAX); + res = make_result_opt_error(&result, have_error); free_var(&result); |