Skip to content

Commit da188b9

Browse files
committed
Adjust the integer overflow tests in the numeric code.
Formerly, the numeric code tested whether an integer value of a larger type would fit in a smaller type by casting it to the smaller type and then testing if the reverse conversion produced the original value. That's perfectly fine, except that it caused a test failure on buildfarm animal castoroides, most likely due to a compiler bug. Instead, do these tests by comparing against PG_INT16/32_MIN/MAX. That matches existing code in other places, such as int84(), which is more widely tested, and so is less likely to go wrong. While at it, add regression tests covering the numeric-to-int8/4/2 conversions, and adjust the recently added tests to the style of 434ddfb (on the v11 branch) to make failures easier to diagnose. Per buildfarm via Tom Lane, reviewed by Tom Lane. Discussion: https://postgr.es/m/2394813.1628179479%40sss.pgh.pa.us
1 parent d3ad656 commit da188b9

File tree

3 files changed

+85
-24
lines changed

3 files changed

+85
-24
lines changed

src/backend/utils/adt/numeric.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3455,11 +3455,13 @@ numericvar_to_int32(const NumericVar *var, int32 *result)
34553455
if (!numericvar_to_int64(var, &val))
34563456
return false;
34573457

3458+
if (unlikely(val < PG_INT32_MIN) || unlikely(val > PG_INT32_MAX))
3459+
return false;
3460+
34583461
/* Down-convert to int4 */
34593462
*result = (int32) val;
34603463

3461-
/* Test for overflow by reverse-conversion. */
3462-
return ((int64) *result == val);
3464+
return true;
34633465
}
34643466

34653467
Datum
@@ -3547,15 +3549,14 @@ numeric_int2(PG_FUNCTION_ARGS)
35473549
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
35483550
errmsg("smallint out of range")));
35493551

3550-
/* Down-convert to int2 */
3551-
result = (int16) val;
3552-
3553-
/* Test for overflow by reverse-conversion. */
3554-
if ((int64) result != val)
3552+
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
35553553
ereport(ERROR,
35563554
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
35573555
errmsg("smallint out of range")));
35583556

3557+
/* Down-convert to int2 */
3558+
result = (int16) val;
3559+
35593560
PG_RETURN_INT16(result);
35603561
}
35613562

@@ -9165,18 +9166,15 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
91659166

91669167
if (numericvar_to_int64(exp, &expval64))
91679168
{
9168-
int expval = (int) expval64;
9169-
9170-
/* Test for overflow by reverse-conversion. */
9171-
if ((int64) expval == expval64)
9169+
if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX)
91729170
{
91739171
/* Okay, select rscale */
91749172
rscale = NUMERIC_MIN_SIG_DIGITS;
91759173
rscale = Max(rscale, base->dscale);
91769174
rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
91779175
rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);
91789176

9179-
power_var_int(base, expval, result, rscale);
9177+
power_var_int(base, (int) expval64, result, rscale);
91809178
return;
91819179
}
91829180
}

src/test/regress/expected/numeric.out

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,55 @@ SELECT * FROM fract_only;
708708
(6 rows)
709709

710710
DROP TABLE fract_only;
711+
-- Check conversion to integers
712+
SELECT (-9223372036854775808.5)::int8; -- should fail
713+
ERROR: bigint out of range
714+
SELECT (-9223372036854775808.4)::int8; -- ok
715+
int8
716+
----------------------
717+
-9223372036854775808
718+
(1 row)
719+
720+
SELECT 9223372036854775807.4::int8; -- ok
721+
int8
722+
---------------------
723+
9223372036854775807
724+
(1 row)
725+
726+
SELECT 9223372036854775807.5::int8; -- should fail
727+
ERROR: bigint out of range
728+
SELECT (-2147483648.5)::int4; -- should fail
729+
ERROR: integer out of range
730+
SELECT (-2147483648.4)::int4; -- ok
731+
int4
732+
-------------
733+
-2147483648
734+
(1 row)
735+
736+
SELECT 2147483647.4::int4; -- ok
737+
int4
738+
------------
739+
2147483647
740+
(1 row)
741+
742+
SELECT 2147483647.5::int4; -- should fail
743+
ERROR: integer out of range
744+
SELECT (-32768.5)::int2; -- should fail
745+
ERROR: smallint out of range
746+
SELECT (-32768.4)::int2; -- ok
747+
int2
748+
--------
749+
-32768
750+
(1 row)
751+
752+
SELECT 32767.4::int2; -- ok
753+
int2
754+
-------
755+
32767
756+
(1 row)
757+
758+
SELECT 32767.5::int2; -- should fail
759+
ERROR: smallint out of range
711760
-- Check inf/nan conversion behavior
712761
SELECT 'NaN'::float8::numeric;
713762
numeric
@@ -1719,10 +1768,10 @@ select 1.000000000123 ^ (-2147483648);
17191768
0.7678656556403084
17201769
(1 row)
17211770

1722-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
1771+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
17231772
rounds_to_zero
17241773
----------------
1725-
t
1774+
0
17261775
(1 row)
17271776

17281777
-- cases that used to error out
@@ -1738,10 +1787,10 @@ select 0.5678 ^ (-85);
17381787
782333637740774446257.7719390061997396
17391788
(1 row)
17401789

1741-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
1790+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
17421791
underflows
17431792
------------
1744-
t
1793+
0
17451794
(1 row)
17461795

17471796
-- negative base to integer powers
@@ -1893,16 +1942,16 @@ select exp(1.0::numeric(71,70));
18931942
2.7182818284590452353602874713526624977572470936999595749669676277240766
18941943
(1 row)
18951944

1896-
select exp(-5000::numeric) = 0 as rounds_to_zero;
1945+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
18971946
rounds_to_zero
18981947
----------------
1899-
t
1948+
0
19001949
(1 row)
19011950

1902-
select exp(-10000::numeric) = 0 as underflows;
1951+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
19031952
underflows
19041953
------------
1905-
t
1954+
0
19061955
(1 row)
19071956

19081957
-- cases that used to generate inaccurate results

src/test/regress/sql/numeric.sql

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,20 @@ INSERT INTO fract_only VALUES (8, '0.00017');
655655
SELECT * FROM fract_only;
656656
DROP TABLE fract_only;
657657

658+
-- Check conversion to integers
659+
SELECT (-9223372036854775808.5)::int8; -- should fail
660+
SELECT (-9223372036854775808.4)::int8; -- ok
661+
SELECT 9223372036854775807.4::int8; -- ok
662+
SELECT 9223372036854775807.5::int8; -- should fail
663+
SELECT (-2147483648.5)::int4; -- should fail
664+
SELECT (-2147483648.4)::int4; -- ok
665+
SELECT 2147483647.4::int4; -- ok
666+
SELECT 2147483647.5::int4; -- should fail
667+
SELECT (-32768.5)::int2; -- should fail
668+
SELECT (-32768.4)::int2; -- ok
669+
SELECT 32767.4::int2; -- ok
670+
SELECT 32767.5::int2; -- should fail
671+
658672
-- Check inf/nan conversion behavior
659673
SELECT 'NaN'::float8::numeric;
660674
SELECT 'Infinity'::float8::numeric;
@@ -920,12 +934,12 @@ select 3.789 ^ 35;
920934
select 1.2 ^ 345;
921935
select 0.12 ^ (-20);
922936
select 1.000000000123 ^ (-2147483648);
923-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
937+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
924938

925939
-- cases that used to error out
926940
select 0.12 ^ (-25);
927941
select 0.5678 ^ (-85);
928-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
942+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
929943

930944
-- negative base to integer powers
931945
select (-1.0) ^ 2147483646;
@@ -972,8 +986,8 @@ select 1.234 ^ 5678;
972986
select exp(0.0);
973987
select exp(1.0);
974988
select exp(1.0::numeric(71,70));
975-
select exp(-5000::numeric) = 0 as rounds_to_zero;
976-
select exp(-10000::numeric) = 0 as underflows;
989+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
990+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
977991

978992
-- cases that used to generate inaccurate results
979993
select exp(32.999);

0 commit comments

Comments
 (0)