aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c35
1 files changed, 26 insertions, 9 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 041d0dbc1d6..e222308ed60 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -221,6 +221,13 @@ struct NumericData
| ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
: ((n)->choice.n_long.n_weight))
+/*
+ * Maximum weight of a stored Numeric value (based on the use of int16 for the
+ * weight in NumericLong). Note that intermediate values held in NumericVar
+ * and NumericSumAccum variables may have much larger weights.
+ */
+#define NUMERIC_WEIGHT_MAX PG_INT16_MAX
+
/* ----------
* NumericVar is the format we use for arithmetic. The digit-array part
* is the same as the NumericData storage format, but the header is more
@@ -1230,10 +1237,15 @@ numeric_round(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(make_result(&const_nan));
/*
- * Limit the scale value to avoid possible overflow in calculations
+ * Limit the scale value to avoid possible overflow in calculations.
+ *
+ * These limits are based on the maximum number of digits a Numeric value
+ * can have before and after the decimal point, but we must allow for one
+ * extra digit before the decimal point, in case the most significant
+ * digit rounds up; we must check if that causes Numeric overflow.
*/
- scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE);
- scale = Min(scale, NUMERIC_MAX_RESULT_SCALE);
+ scale = Max(scale, -(NUMERIC_WEIGHT_MAX + 1) * DEC_DIGITS - 1);
+ scale = Min(scale, NUMERIC_DSCALE_MAX);
/*
* Unpack the argument and round it at the proper digit position
@@ -1279,10 +1291,13 @@ numeric_trunc(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(make_result(&const_nan));
/*
- * Limit the scale value to avoid possible overflow in calculations
+ * Limit the scale value to avoid possible overflow in calculations.
+ *
+ * These limits are based on the maximum number of digits a Numeric value
+ * can have before and after the decimal point.
*/
- scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE);
- scale = Min(scale, NUMERIC_MAX_RESULT_SCALE);
+ scale = Max(scale, -(NUMERIC_WEIGHT_MAX + 1) * DEC_DIGITS);
+ scale = Min(scale, NUMERIC_DSCALE_MAX);
/*
* Unpack the argument and truncate it at the proper digit position
@@ -9256,7 +9271,8 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
/*
* Set the scale for the low-precision calculation, computing ln(base) to
* around 8 significant digits. Note that ln_dweight may be as small as
- * -SHRT_MAX, so the scale may exceed NUMERIC_MAX_DISPLAY_SCALE here.
+ * -NUMERIC_DSCALE_MAX, so the scale may exceed NUMERIC_MAX_DISPLAY_SCALE
+ * here.
*/
local_rscale = 8 - ln_dweight;
local_rscale = Max(local_rscale, NUMERIC_MIN_DISPLAY_SCALE);
@@ -9396,7 +9412,7 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
* Apply crude overflow/underflow tests so we can exit early if the result
* certainly will overflow/underflow.
*/
- if (f > 3 * SHRT_MAX * DEC_DIGITS)
+ if (f > 3 * NUMERIC_WEIGHT_MAX * DEC_DIGITS)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value overflows numeric format")));
@@ -9466,7 +9482,8 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
* int16, the final result is guaranteed to overflow (or underflow, if
* exp < 0), so we can give up before wasting too many cycles.
*/
- if (base_prod.weight > SHRT_MAX || result->weight > SHRT_MAX)
+ if (base_prod.weight > NUMERIC_WEIGHT_MAX ||
+ result->weight > NUMERIC_WEIGHT_MAX)
{
/* overflow, unless neg, in which case result should be 0 */
if (!neg)