git.postgresql.org Git - postgresql.git/commitdiff

git projects / postgresql.git / commitdiff
? search:
summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 53c38a0)
Fix numeric_mul() overflow due to too many digits after decimal point.
2021年7月10日 11:42:59 +0000 (12:42 +0100)
2021年7月10日 11:42:59 +0000 (12:42 +0100)
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


diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index bc71326fc8af59bbeb2a4e774d9b173439958f16..2a0f68f98b2af6f2af2bdaa39f763ff5ad5845f9 100644 (file)
--- 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);
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 4ad485130bda0965b17d29f656af069665edd378..385e963a75f13cf0e8d9e9f1dede6ab62cf2bcf1 100644 (file)
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -2145,6 +2145,12 @@ select 4769999999999999999999999999999999999999999999999999999999999999999999999
47699999999999999999999999999999999999999999999999999999999999999999999999999999999999985230000000000000000000000000000000000000000000000000000000000000000000000000000000000001
(1 row)
+select trim_scale((0.1 - 2e-16383) * (0.1 - 3e-16383));
+ trim_scale
+------------
+ 0.01
+(1 row)
+
--
-- Test some corner cases for division
--
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index 3784c5253d7ca30a6b2b8dc01965dbc81f84fe9d..7e17c28d51e39156cc01c2464b166bee4fc7ffd3 100644 (file)
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -1044,6 +1044,8 @@ select 4770999999999999999999999999999999999999999999999999999999999999999999999
select 4769999999999999999999999999999999999999999999999999999999999999999999999999999999999999 * 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999;
+select trim_scale((0.1 - 2e-16383) * (0.1 - 3e-16383));
+
--
-- Test some corner cases for division
--
This is the main PostgreSQL git repository.
RSS Atom

AltStyle によって変換されたページ (->オリジナル) /