Skip to content

Commit 0325565

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 c3a135b commit 0325565

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
@@ -4228,11 +4228,13 @@ numericvar_to_int32(const NumericVar *var, int32 *result)
42284228
if (!numericvar_to_int64(var, &val))
42294229
return false;
42304230

4231+
if (unlikely(val < PG_INT32_MIN) || unlikely(val > PG_INT32_MAX))
4232+
return false;
4233+
42314234
/* Down-convert to int4 */
42324235
*result = (int32) val;
42334236

4234-
/* Test for overflow by reverse-conversion. */
4235-
return ((int64) *result == val);
4237+
return true;
42364238
}
42374239

42384240
Datum
@@ -4312,15 +4314,14 @@ numeric_int2(PG_FUNCTION_ARGS)
43124314
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
43134315
errmsg("smallint out of range")));
43144316

4315-
/* Down-convert to int2 */
4316-
result = (int16) val;
4317-
4318-
/* Test for overflow by reverse-conversion. */
4319-
if ((int64) result != val)
4317+
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
43204318
ereport(ERROR,
43214319
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
43224320
errmsg("smallint out of range")));
43234321

4322+
/* Down-convert to int2 */
4323+
result = (int16) val;
4324+
43244325
PG_RETURN_INT16(result);
43254326
}
43264327

@@ -10147,18 +10148,15 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
1014710148

1014810149
if (numericvar_to_int64(exp, &expval64))
1014910150
{
10150-
int expval = (int) expval64;
10151-
10152-
/* Test for overflow by reverse-conversion. */
10153-
if ((int64) expval == expval64)
10151+
if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX)
1015410152
{
1015510153
/* Okay, select rscale */
1015610154
rscale = NUMERIC_MIN_SIG_DIGITS;
1015710155
rscale = Max(rscale, base->dscale);
1015810156
rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
1015910157
rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);
1016010158

10161-
power_var_int(base, expval, result, rscale);
10159+
power_var_int(base, (int) expval64, result, rscale);
1016210160
return;
1016310161
}
1016410162
}

src/test/regress/expected/numeric.out

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,55 @@ SELECT * FROM fract_only;
11541154
(7 rows)
11551155

11561156
DROP TABLE fract_only;
1157+
-- Check conversion to integers
1158+
SELECT (-9223372036854775808.5)::int8; -- should fail
1159+
ERROR: bigint out of range
1160+
SELECT (-9223372036854775808.4)::int8; -- ok
1161+
int8
1162+
----------------------
1163+
-9223372036854775808
1164+
(1 row)
1165+
1166+
SELECT 9223372036854775807.4::int8; -- ok
1167+
int8
1168+
---------------------
1169+
9223372036854775807
1170+
(1 row)
1171+
1172+
SELECT 9223372036854775807.5::int8; -- should fail
1173+
ERROR: bigint out of range
1174+
SELECT (-2147483648.5)::int4; -- should fail
1175+
ERROR: integer out of range
1176+
SELECT (-2147483648.4)::int4; -- ok
1177+
int4
1178+
-------------
1179+
-2147483648
1180+
(1 row)
1181+
1182+
SELECT 2147483647.4::int4; -- ok
1183+
int4
1184+
------------
1185+
2147483647
1186+
(1 row)
1187+
1188+
SELECT 2147483647.5::int4; -- should fail
1189+
ERROR: integer out of range
1190+
SELECT (-32768.5)::int2; -- should fail
1191+
ERROR: smallint out of range
1192+
SELECT (-32768.4)::int2; -- ok
1193+
int2
1194+
--------
1195+
-32768
1196+
(1 row)
1197+
1198+
SELECT 32767.4::int2; -- ok
1199+
int2
1200+
-------
1201+
32767
1202+
(1 row)
1203+
1204+
SELECT 32767.5::int2; -- should fail
1205+
ERROR: smallint out of range
11571206
-- Check inf/nan conversion behavior
11581207
SELECT 'NaN'::float8::numeric;
11591208
numeric
@@ -2365,10 +2414,10 @@ select 1.000000000123 ^ (-2147483648);
23652414
0.7678656556403084
23662415
(1 row)
23672416

2368-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
2417+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
23692418
rounds_to_zero
23702419
----------------
2371-
t
2420+
0
23722421
(1 row)
23732422

23742423
-- cases that used to error out
@@ -2384,10 +2433,10 @@ select 0.5678 ^ (-85);
23842433
782333637740774446257.7719390061997396
23852434
(1 row)
23862435

2387-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
2436+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
23882437
underflows
23892438
------------
2390-
t
2439+
0
23912440
(1 row)
23922441

23932442
-- negative base to integer powers
@@ -2557,16 +2606,16 @@ select exp('-inf'::numeric);
25572606
0
25582607
(1 row)
25592608

2560-
select exp(-5000::numeric) = 0 as rounds_to_zero;
2609+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
25612610
rounds_to_zero
25622611
----------------
2563-
t
2612+
0
25642613
(1 row)
25652614

2566-
select exp(-10000::numeric) = 0 as underflows;
2615+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
25672616
underflows
25682617
------------
2569-
t
2618+
0
25702619
(1 row)
25712620

25722621
-- 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
@@ -773,6 +773,20 @@ INSERT INTO fract_only VALUES (11, '-Inf'); -- should fail
773773
SELECT * FROM fract_only;
774774
DROP TABLE fract_only;
775775

776+
-- Check conversion to integers
777+
SELECT (-9223372036854775808.5)::int8; -- should fail
778+
SELECT (-9223372036854775808.4)::int8; -- ok
779+
SELECT 9223372036854775807.4::int8; -- ok
780+
SELECT 9223372036854775807.5::int8; -- should fail
781+
SELECT (-2147483648.5)::int4; -- should fail
782+
SELECT (-2147483648.4)::int4; -- ok
783+
SELECT 2147483647.4::int4; -- ok
784+
SELECT 2147483647.5::int4; -- should fail
785+
SELECT (-32768.5)::int2; -- should fail
786+
SELECT (-32768.4)::int2; -- ok
787+
SELECT 32767.4::int2; -- ok
788+
SELECT 32767.5::int2; -- should fail
789+
776790
-- Check inf/nan conversion behavior
777791
SELECT 'NaN'::float8::numeric;
778792
SELECT 'Infinity'::float8::numeric;
@@ -1099,12 +1113,12 @@ select 3.789 ^ 35;
10991113
select 1.2 ^ 345;
11001114
select 0.12 ^ (-20);
11011115
select 1.000000000123 ^ (-2147483648);
1102-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
1116+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
11031117

11041118
-- cases that used to error out
11051119
select 0.12 ^ (-25);
11061120
select 0.5678 ^ (-85);
1107-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
1121+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
11081122

11091123
-- negative base to integer powers
11101124
select (-1.0) ^ 2147483646;
@@ -1154,8 +1168,8 @@ select exp(1.0::numeric(71,70));
11541168
select exp('nan'::numeric);
11551169
select exp('inf'::numeric);
11561170
select exp('-inf'::numeric);
1157-
select exp(-5000::numeric) = 0 as rounds_to_zero;
1158-
select exp(-10000::numeric) = 0 as underflows;
1171+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
1172+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
11591173

11601174
-- cases that used to generate inaccurate results
11611175
select exp(32.999);

0 commit comments

Comments
 (0)