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.c30
1 files changed, 26 insertions, 4 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 19d0bdcbb98..2d6a4cb7de2 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -6073,10 +6073,12 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result)
static void
power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale)
{
+ unsigned int mask;
bool neg;
NumericVar base_prod;
int local_rscale;
+ /* Handle some common special cases, as well as corner cases */
switch (exp)
{
case 0:
@@ -6110,23 +6112,43 @@ power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale)
* pattern of exp. We do the multiplications with some extra precision.
*/
neg = (exp < 0);
- exp = Abs(exp);
+ mask = Abs(exp);
local_rscale = rscale + MUL_GUARD_DIGITS * 2;
init_var(&base_prod);
set_var_from_var(base, &base_prod);
- if (exp & 1)
+ if (mask & 1)
set_var_from_var(base, result);
else
set_var_from_var(&const_one, result);
- while ((exp >>= 1) > 0)
+ while ((mask >>= 1) > 0)
{
mul_var(&base_prod, &base_prod, &base_prod, local_rscale);
- if (exp & 1)
+ if (mask & 1)
mul_var(&base_prod, result, result, local_rscale);
+
+ /*
+ * When abs(base) > 1, the number of digits to the left of the decimal
+ * point in base_prod doubles at each iteration, so if exp is large we
+ * could easily spend large amounts of time and memory space doing the
+ * multiplications. But once the weight exceeds what will fit in
+ * 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)
+ {
+ /* overflow, unless neg, in which case result should be 0 */
+ if (!neg)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value overflows numeric format")));
+ zero_var(result);
+ neg = false;
+ break;
+ }
}
free_var(&base_prod);