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

Commitf7aec8c

Browse files
committed
Fix scale clamping in numeric round() and trunc().
The numeric round() and trunc() functions clamp the scale argument tothe range between +/- NUMERIC_MAX_RESULT_SCALE (2000), which is muchsmaller than the actual allowed range of type numeric. As a result,they return incorrect results when asked to round/truncate more than2000 digits before or after the decimal point.Fix by using the correct upper and lower scale limits based on theactual allowed (and documented) range of type numeric.While at it, use the new NUMERIC_WEIGHT_MAX constant instead ofSHRT_MAX in all other overflow checks, and fix a comment thinko inpower_var() introduced bye54a758 -- the minimum value ofln_dweight is -NUMERIC_DSCALE_MAX (-16383), not -SHRT_MAX, though thisdoesn't affect the point being made in the comment, that the resultinglocal_rscale value may exceed NUMERIC_MAX_DISPLAY_SCALE (1000).Back-patch to all supported branches.Dean Rasheed, reviewed by Joel Jacobson.Discussion:https://postgr.es/m/CAEZATCXB%2BrDTuMjhK5ZxcouufigSc-X4tGJCBTMpZ3n%3DxxQuhg%40mail.gmail.com
1 parent507f234 commitf7aec8c

File tree

3 files changed

+157
-13
lines changed

3 files changed

+157
-13
lines changed

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

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ struct NumericData
250250
| ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
251251
: ((n)->choice.n_long.n_weight))
252252

253+
/*
254+
* Maximum weight of a stored Numeric value (based on the use of int16 for the
255+
* weight in NumericLong). Note that intermediate values held in NumericVar
256+
* and NumericSumAccum variables may have much larger weights.
257+
*/
258+
#defineNUMERIC_WEIGHT_MAXPG_INT16_MAX
259+
253260
/* ----------
254261
* NumericVar is the format we use for arithmetic. The digit-array part
255262
* is the same as the NumericData storage format, but the header is more
@@ -1544,10 +1551,15 @@ numeric_round(PG_FUNCTION_ARGS)
15441551
PG_RETURN_NUMERIC(duplicate_numeric(num));
15451552

15461553
/*
1547-
* Limit the scale value to avoid possible overflow in calculations
1554+
* Limit the scale value to avoid possible overflow in calculations.
1555+
*
1556+
* These limits are based on the maximum number of digits a Numeric value
1557+
* can have before and after the decimal point, but we must allow for one
1558+
* extra digit before the decimal point, in case the most significant
1559+
* digit rounds up; we must check if that causes Numeric overflow.
15481560
*/
1549-
scale=Max(scale,-NUMERIC_MAX_RESULT_SCALE);
1550-
scale=Min(scale,NUMERIC_MAX_RESULT_SCALE);
1561+
scale=Max(scale,-(NUMERIC_WEIGHT_MAX+1)*DEC_DIGITS-1);
1562+
scale=Min(scale,NUMERIC_DSCALE_MAX);
15511563

15521564
/*
15531565
* Unpack the argument and round it at the proper digit position
@@ -1593,10 +1605,13 @@ numeric_trunc(PG_FUNCTION_ARGS)
15931605
PG_RETURN_NUMERIC(duplicate_numeric(num));
15941606

15951607
/*
1596-
* Limit the scale value to avoid possible overflow in calculations
1608+
* Limit the scale value to avoid possible overflow in calculations.
1609+
*
1610+
* These limits are based on the maximum number of digits a Numeric value
1611+
* can have before and after the decimal point.
15971612
*/
1598-
scale=Max(scale,-NUMERIC_MAX_RESULT_SCALE);
1599-
scale=Min(scale,NUMERIC_MAX_RESULT_SCALE);
1613+
scale=Max(scale,-(NUMERIC_WEIGHT_MAX+1)*DEC_DIGITS);
1614+
scale=Min(scale,NUMERIC_DSCALE_MAX);
16001615

16011616
/*
16021617
* Unpack the argument and truncate it at the proper digit position
@@ -7206,7 +7221,7 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
72067221
add_var(dest,&tmp_var,dest);
72077222

72087223
/* Result will overflow if weight overflows int16 */
7209-
if (dest->weight>SHRT_MAX)
7224+
if (dest->weight>NUMERIC_WEIGHT_MAX)
72107225
gotoout_of_range;
72117226

72127227
/* Begin a new group */
@@ -7243,7 +7258,7 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
72437258
add_var(dest,&tmp_var,dest);
72447259

72457260
/* Result will overflow if weight overflows int16 */
7246-
if (dest->weight>SHRT_MAX)
7261+
if (dest->weight>NUMERIC_WEIGHT_MAX)
72477262
gotoout_of_range;
72487263

72497264
/* Begin a new group */
@@ -7280,7 +7295,7 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
72807295
add_var(dest,&tmp_var,dest);
72817296

72827297
/* Result will overflow if weight overflows int16 */
7283-
if (dest->weight>SHRT_MAX)
7298+
if (dest->weight>NUMERIC_WEIGHT_MAX)
72847299
gotoout_of_range;
72857300

72867301
/* Begin a new group */
@@ -7316,7 +7331,7 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
73167331
int64_to_numericvar(tmp,&tmp_var);
73177332
add_var(dest,&tmp_var,dest);
73187333

7319-
if (dest->weight>SHRT_MAX)
7334+
if (dest->weight>NUMERIC_WEIGHT_MAX)
73207335
gotoout_of_range;
73217336

73227337
dest->sign=sign;
@@ -10955,7 +10970,8 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
1095510970
/*
1095610971
* Set the scale for the low-precision calculation, computing ln(base) to
1095710972
* around 8 significant digits. Note that ln_dweight may be as small as
10958-
* -SHRT_MAX, so the scale may exceed NUMERIC_MAX_DISPLAY_SCALE here.
10973+
* -NUMERIC_DSCALE_MAX, so the scale may exceed NUMERIC_MAX_DISPLAY_SCALE
10974+
* here.
1095910975
*/
1096010976
local_rscale=8-ln_dweight;
1096110977
local_rscale=Max(local_rscale,NUMERIC_MIN_DISPLAY_SCALE);
@@ -11063,7 +11079,7 @@ power_var_int(const NumericVar *base, int exp, int exp_dscale,
1106311079
f=0;/* result is 0 or 1 (weight 0), or error */
1106411080

1106511081
/* overflow/underflow tests with fuzz factors */
11066-
if (f> (SHRT_MAX+1)*DEC_DIGITS)
11082+
if (f> (NUMERIC_WEIGHT_MAX+1)*DEC_DIGITS)
1106711083
ereport(ERROR,
1106811084
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1106911085
errmsg("value overflows numeric format")));
@@ -11194,7 +11210,8 @@ power_var_int(const NumericVar *base, int exp, int exp_dscale,
1119411210
* int16, the final result is guaranteed to overflow (or underflow, if
1119511211
* exp < 0), so we can give up before wasting too many cycles.
1119611212
*/
11197-
if (base_prod.weight>SHRT_MAX||result->weight>SHRT_MAX)
11213+
if (base_prod.weight>NUMERIC_WEIGHT_MAX||
11214+
result->weight>NUMERIC_WEIGHT_MAX)
1119811215
{
1119911216
/* overflow, unless neg, in which case result should be 0 */
1120011217
if (!neg)

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

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,108 @@ FROM generate_series(-5,5) AS t(i);
13461346
5 | -300000 | -200000 | -100000 | 100000 | 200000 | 300000
13471347
(11 rows)
13481348

1349+
-- Check limits of rounding before the decimal point
1350+
SELECT round(4.4e131071, -131071) = 4e131071;
1351+
?column?
1352+
----------
1353+
t
1354+
(1 row)
1355+
1356+
SELECT round(4.5e131071, -131071) = 5e131071;
1357+
?column?
1358+
----------
1359+
t
1360+
(1 row)
1361+
1362+
SELECT round(4.5e131071, -131072); -- loses all digits
1363+
round
1364+
-------
1365+
0
1366+
(1 row)
1367+
1368+
SELECT round(5.5e131071, -131072); -- rounds up and overflows
1369+
ERROR: value overflows numeric format
1370+
SELECT round(5.5e131071, -131073); -- loses all digits
1371+
round
1372+
-------
1373+
0
1374+
(1 row)
1375+
1376+
SELECT round(5.5e131071, -1000000); -- loses all digits
1377+
round
1378+
-------
1379+
0
1380+
(1 row)
1381+
1382+
-- Check limits of rounding after the decimal point
1383+
SELECT round(5e-16383, 1000000) = 5e-16383;
1384+
?column?
1385+
----------
1386+
t
1387+
(1 row)
1388+
1389+
SELECT round(5e-16383, 16383) = 5e-16383;
1390+
?column?
1391+
----------
1392+
t
1393+
(1 row)
1394+
1395+
SELECT round(5e-16383, 16382) = 1e-16382;
1396+
?column?
1397+
----------
1398+
t
1399+
(1 row)
1400+
1401+
SELECT round(5e-16383, 16381) = 0;
1402+
?column?
1403+
----------
1404+
t
1405+
(1 row)
1406+
1407+
-- Check limits of trunc() before the decimal point
1408+
SELECT trunc(9.9e131071, -131071) = 9e131071;
1409+
?column?
1410+
----------
1411+
t
1412+
(1 row)
1413+
1414+
SELECT trunc(9.9e131071, -131072); -- loses all digits
1415+
trunc
1416+
-------
1417+
0
1418+
(1 row)
1419+
1420+
SELECT trunc(9.9e131071, -131073); -- loses all digits
1421+
trunc
1422+
-------
1423+
0
1424+
(1 row)
1425+
1426+
SELECT trunc(9.9e131071, -1000000); -- loses all digits
1427+
trunc
1428+
-------
1429+
0
1430+
(1 row)
1431+
1432+
-- Check limits of trunc() after the decimal point
1433+
SELECT trunc(5e-16383, 1000000) = 5e-16383;
1434+
?column?
1435+
----------
1436+
t
1437+
(1 row)
1438+
1439+
SELECT trunc(5e-16383, 16383) = 5e-16383;
1440+
?column?
1441+
----------
1442+
t
1443+
(1 row)
1444+
1445+
SELECT trunc(5e-16383, 16382) = 0;
1446+
?column?
1447+
----------
1448+
t
1449+
(1 row)
1450+
13491451
-- Testing for width_bucket(). For convenience, we test both the
13501452
-- numeric and float8 versions of the function in this file.
13511453
-- errors

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,31 @@ SELECT i as pow,
833833
round((2.5*10 ^ i)::numeric,-i)
834834
FROM generate_series(-5,5)AS t(i);
835835

836+
-- Check limits of rounding before the decimal point
837+
SELECT round(4.4e131071,-131071)= 4e131071;
838+
SELECT round(4.5e131071,-131071)= 5e131071;
839+
SELECT round(4.5e131071,-131072);-- loses all digits
840+
SELECT round(5.5e131071,-131072);-- rounds up and overflows
841+
SELECT round(5.5e131071,-131073);-- loses all digits
842+
SELECT round(5.5e131071,-1000000);-- loses all digits
843+
844+
-- Check limits of rounding after the decimal point
845+
SELECT round(5e-16383,1000000)= 5e-16383;
846+
SELECT round(5e-16383,16383)= 5e-16383;
847+
SELECT round(5e-16383,16382)= 1e-16382;
848+
SELECT round(5e-16383,16381)=0;
849+
850+
-- Check limits of trunc() before the decimal point
851+
SELECT trunc(9.9e131071,-131071)= 9e131071;
852+
SELECT trunc(9.9e131071,-131072);-- loses all digits
853+
SELECT trunc(9.9e131071,-131073);-- loses all digits
854+
SELECT trunc(9.9e131071,-1000000);-- loses all digits
855+
856+
-- Check limits of trunc() after the decimal point
857+
SELECT trunc(5e-16383,1000000)= 5e-16383;
858+
SELECT trunc(5e-16383,16383)= 5e-16383;
859+
SELECT trunc(5e-16383,16382)=0;
860+
836861
-- Testing for width_bucket(). For convenience, we test both the
837862
-- numeric and float8 versions of the function in this file.
838863

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp