Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1d35232

Browse files
committed
Fix power_var_int() for large integer exponents.
The code for raising a NUMERIC value to an integer power wasn't verycareful about large powers. It got an outright wrong answer for anexponent of INT_MIN, due to failure to consider overflow of the Abs(exp)operation; which is fixable by using an unsigned rather than signedexponent value after that point. Also, even though the number ofiterations of the power-computation loop is pretty limited, it's easy forthe repeated squarings to result in ridiculously enormous intermediatevalues, which can take unreasonable amounts of time/memory to process,or even overflow the internal "weight" field and so produce a wrong answer.We can forestall misbehaviors of that sort by bailing out as soon as theweight value exceeds what will fit in int16, since then the final answermust overflow (if exp > 0) or underflow (if exp < 0) the packed numericformat.Per off-list report from Pavel Stehule. Back-patch to all supportedbranches.
1 parente3ec072 commit1d35232

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

‎src/backend/utils/adt/numeric.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6073,10 +6073,12 @@ power_var(NumericVar *base, NumericVar *exp, NumericVar *result)
60736073
staticvoid
60746074
power_var_int(NumericVar*base,intexp,NumericVar*result,intrscale)
60756075
{
6076+
unsignedintmask;
60766077
boolneg;
60776078
NumericVarbase_prod;
60786079
intlocal_rscale;
60796080

6081+
/* Handle some common special cases, as well as corner cases */
60806082
switch (exp)
60816083
{
60826084
case0:
@@ -6110,23 +6112,43 @@ power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale)
61106112
* pattern of exp. We do the multiplications with some extra precision.
61116113
*/
61126114
neg= (exp<0);
6113-
exp=Abs(exp);
6115+
mask=Abs(exp);
61146116

61156117
local_rscale=rscale+MUL_GUARD_DIGITS*2;
61166118

61176119
init_var(&base_prod);
61186120
set_var_from_var(base,&base_prod);
61196121

6120-
if (exp&1)
6122+
if (mask&1)
61216123
set_var_from_var(base,result);
61226124
else
61236125
set_var_from_var(&const_one,result);
61246126

6125-
while ((exp >>=1)>0)
6127+
while ((mask >>=1)>0)
61266128
{
61276129
mul_var(&base_prod,&base_prod,&base_prod,local_rscale);
6128-
if (exp&1)
6130+
if (mask&1)
61296131
mul_var(&base_prod,result,result,local_rscale);
6132+
6133+
/*
6134+
* When abs(base) > 1, the number of digits to the left of the decimal
6135+
* point in base_prod doubles at each iteration, so if exp is large we
6136+
* could easily spend large amounts of time and memory space doing the
6137+
* multiplications. But once the weight exceeds what will fit in
6138+
* int16, the final result is guaranteed to overflow (or underflow, if
6139+
* exp < 0), so we can give up before wasting too many cycles.
6140+
*/
6141+
if (base_prod.weight>SHRT_MAX||result->weight>SHRT_MAX)
6142+
{
6143+
/* overflow, unless neg, in which case result should be 0 */
6144+
if (!neg)
6145+
ereport(ERROR,
6146+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
6147+
errmsg("value overflows numeric format")));
6148+
zero_var(result);
6149+
neg= false;
6150+
break;
6151+
}
61306152
}
61316153

61326154
free_var(&base_prod);

‎src/test/regress/expected/numeric.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,3 +1390,22 @@ select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
13901390
12345678901234567890
13911391
(1 row)
13921392

1393+
--
1394+
-- Test code path for raising to integer powers
1395+
--
1396+
select 10.0 ^ -2147483648 as rounds_to_zero;
1397+
rounds_to_zero
1398+
--------------------
1399+
0.0000000000000000
1400+
(1 row)
1401+
1402+
select 10.0 ^ -2147483647 as rounds_to_zero;
1403+
rounds_to_zero
1404+
--------------------
1405+
0.0000000000000000
1406+
(1 row)
1407+
1408+
select 10.0 ^ 2147483647 as overflows;
1409+
ERROR: value overflows numeric format
1410+
select 117743296169.0 ^ 1000000000 as overflows;
1411+
ERROR: value overflows numeric format

‎src/test/regress/sql/numeric.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,3 +828,12 @@ select 12345678901234567890 % 123;
828828
select12345678901234567890/123;
829829
select div(12345678901234567890,123);
830830
select div(12345678901234567890,123)*123+12345678901234567890 %123;
831+
832+
--
833+
-- Test code path for raising to integer powers
834+
--
835+
836+
select10.0 ^-2147483648as rounds_to_zero;
837+
select10.0 ^-2147483647as rounds_to_zero;
838+
select10.0 ^2147483647as overflows;
839+
select117743296169.0 ^1000000000as overflows;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp