Skip to content

Commit cc4420f

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 0afe231 commit cc4420f

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
@@ -3260,11 +3260,13 @@ numericvar_to_int32(const NumericVar *var, int32 *result)
32603260
if (!numericvar_to_int64(var, &val))
32613261
return false;
32623262

3263+
if (unlikely(val < PG_INT32_MIN) || unlikely(val > PG_INT32_MAX))
3264+
return false;
3265+
32633266
/* Down-convert to int4 */
32643267
*result = (int32) val;
32653268

3266-
/* Test for overflow by reverse-conversion. */
3267-
return ((int64) *result == val);
3269+
return true;
32683270
}
32693271

32703272
Datum
@@ -3352,15 +3354,14 @@ numeric_int2(PG_FUNCTION_ARGS)
33523354
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
33533355
errmsg("smallint out of range")));
33543356

3355-
/* Down-convert to int2 */
3356-
result = (int16) val;
3357-
3358-
/* Test for overflow by reverse-conversion. */
3359-
if ((int64) result != val)
3357+
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
33603358
ereport(ERROR,
33613359
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
33623360
errmsg("smallint out of range")));
33633361

3362+
/* Down-convert to int2 */
3363+
result = (int16) val;
3364+
33643365
PG_RETURN_INT16(result);
33653366
}
33663367

@@ -8396,18 +8397,15 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
83968397

83978398
if (numericvar_to_int64(exp, &expval64))
83988399
{
8399-
int expval = (int) expval64;
8400-
8401-
/* Test for overflow by reverse-conversion. */
8402-
if ((int64) expval == expval64)
8400+
if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX)
84038401
{
84048402
/* Okay, select rscale */
84058403
rscale = NUMERIC_MIN_SIG_DIGITS;
84068404
rscale = Max(rscale, base->dscale);
84078405
rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
84088406
rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);
84098407

8410-
power_var_int(base, expval, result, rscale);
8408+
power_var_int(base, (int) expval64, result, rscale);
84118409
return;
84128410
}
84138411
}

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
@@ -1668,10 +1717,10 @@ select 1.000000000123 ^ (-2147483648);
16681717
0.7678656556403084
16691718
(1 row)
16701719

1671-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
1720+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
16721721
rounds_to_zero
16731722
----------------
1674-
t
1723+
0
16751724
(1 row)
16761725

16771726
-- cases that used to error out
@@ -1687,10 +1736,10 @@ select 0.5678 ^ (-85);
16871736
782333637740774446257.7719390061997396
16881737
(1 row)
16891738

1690-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
1739+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
16911740
underflows
16921741
------------
1693-
t
1742+
0
16941743
(1 row)
16951744

16961745
-- negative base to integer powers
@@ -1842,16 +1891,16 @@ select exp(1.0::numeric(71,70));
18421891
2.7182818284590452353602874713526624977572470936999595749669676277240766
18431892
(1 row)
18441893

1845-
select exp(-5000::numeric) = 0 as rounds_to_zero;
1894+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
18461895
rounds_to_zero
18471896
----------------
1848-
t
1897+
0
18491898
(1 row)
18501899

1851-
select exp(-10000::numeric) = 0 as underflows;
1900+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
18521901
underflows
18531902
------------
1854-
t
1903+
0
18551904
(1 row)
18561905

18571906
-- 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;
@@ -907,12 +921,12 @@ select 3.789 ^ 35;
907921
select 1.2 ^ 345;
908922
select 0.12 ^ (-20);
909923
select 1.000000000123 ^ (-2147483648);
910-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
924+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
911925

912926
-- cases that used to error out
913927
select 0.12 ^ (-25);
914928
select 0.5678 ^ (-85);
915-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
929+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
916930

917931
-- negative base to integer powers
918932
select (-1.0) ^ 2147483646;
@@ -959,8 +973,8 @@ select 1.234 ^ 5678;
959973
select exp(0.0);
960974
select exp(1.0);
961975
select exp(1.0::numeric(71,70));
962-
select exp(-5000::numeric) = 0 as rounds_to_zero;
963-
select exp(-10000::numeric) = 0 as underflows;
976+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
977+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
964978

965979
-- cases that used to generate inaccurate results
966980
select exp(32.999);

0 commit comments

Comments
 (0)