Skip to content

Commit 2642df9

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 ba4eb86 commit 2642df9

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
@@ -4289,11 +4289,13 @@ numericvar_to_int32(const NumericVar *var, int32 *result)
42894289
if (!numericvar_to_int64(var, &val))
42904290
return false;
42914291

4292+
if (unlikely(val < PG_INT32_MIN) || unlikely(val > PG_INT32_MAX))
4293+
return false;
4294+
42924295
/* Down-convert to int4 */
42934296
*result = (int32) val;
42944297

4295-
/* Test for overflow by reverse-conversion. */
4296-
return ((int64) *result == val);
4298+
return true;
42974299
}
42984300

42994301
Datum
@@ -4373,15 +4375,14 @@ numeric_int2(PG_FUNCTION_ARGS)
43734375
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
43744376
errmsg("smallint out of range")));
43754377

4376-
/* Down-convert to int2 */
4377-
result = (int16) val;
4378-
4379-
/* Test for overflow by reverse-conversion. */
4380-
if ((int64) result != val)
4378+
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
43814379
ereport(ERROR,
43824380
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
43834381
errmsg("smallint out of range")));
43844382

4383+
/* Down-convert to int2 */
4384+
result = (int16) val;
4385+
43854386
PG_RETURN_INT16(result);
43864387
}
43874388

@@ -10186,18 +10187,15 @@ power_var(const NumericVar *base, const NumericVar *exp, NumericVar *result)
1018610187

1018710188
if (numericvar_to_int64(exp, &expval64))
1018810189
{
10189-
int expval = (int) expval64;
10190-
10191-
/* Test for overflow by reverse-conversion. */
10192-
if ((int64) expval == expval64)
10190+
if (expval64 >= PG_INT32_MIN && expval64 <= PG_INT32_MAX)
1019310191
{
1019410192
/* Okay, select rscale */
1019510193
rscale = NUMERIC_MIN_SIG_DIGITS;
1019610194
rscale = Max(rscale, base->dscale);
1019710195
rscale = Max(rscale, NUMERIC_MIN_DISPLAY_SCALE);
1019810196
rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);
1019910197

10200-
power_var_int(base, expval, result, rscale);
10198+
power_var_int(base, (int) expval64, result, rscale);
1020110199
return;
1020210200
}
1020310201
}

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
@@ -2428,10 +2477,10 @@ select 1.000000000123 ^ (-2147483648);
24282477
0.7678656556403084
24292478
(1 row)
24302479

2431-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
2480+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
24322481
rounds_to_zero
24332482
----------------
2434-
t
2483+
0
24352484
(1 row)
24362485

24372486
-- cases that used to error out
@@ -2447,10 +2496,10 @@ select 0.5678 ^ (-85);
24472496
782333637740774446257.7719390061997396
24482497
(1 row)
24492498

2450-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
2499+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
24512500
underflows
24522501
------------
2453-
t
2502+
0
24542503
(1 row)
24552504

24562505
-- negative base to integer powers
@@ -2620,16 +2669,16 @@ select exp('-inf'::numeric);
26202669
0
26212670
(1 row)
26222671

2623-
select exp(-5000::numeric) = 0 as rounds_to_zero;
2672+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
26242673
rounds_to_zero
26252674
----------------
2626-
t
2675+
0
26272676
(1 row)
26282677

2629-
select exp(-10000::numeric) = 0 as underflows;
2678+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
26302679
underflows
26312680
------------
2632-
t
2681+
0
26332682
(1 row)
26342683

26352684
-- 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;
@@ -1133,12 +1147,12 @@ select 3.789 ^ 35;
11331147
select 1.2 ^ 345;
11341148
select 0.12 ^ (-20);
11351149
select 1.000000000123 ^ (-2147483648);
1136-
select 0.9999999999 ^ 23300000000000 = 0 as rounds_to_zero;
1150+
select coalesce(nullif(0.9999999999 ^ 23300000000000, 0), 0) as rounds_to_zero;
11371151

11381152
-- cases that used to error out
11391153
select 0.12 ^ (-25);
11401154
select 0.5678 ^ (-85);
1141-
select 0.9999999999 ^ 70000000000000 = 0 as underflows;
1155+
select coalesce(nullif(0.9999999999 ^ 70000000000000, 0), 0) as underflows;
11421156

11431157
-- negative base to integer powers
11441158
select (-1.0) ^ 2147483646;
@@ -1188,8 +1202,8 @@ select exp(1.0::numeric(71,70));
11881202
select exp('nan'::numeric);
11891203
select exp('inf'::numeric);
11901204
select exp('-inf'::numeric);
1191-
select exp(-5000::numeric) = 0 as rounds_to_zero;
1192-
select exp(-10000::numeric) = 0 as underflows;
1205+
select coalesce(nullif(exp(-5000::numeric), 0), 0) as rounds_to_zero;
1206+
select coalesce(nullif(exp(-10000::numeric), 0), 0) as underflows;
11931207

11941208
-- cases that used to generate inaccurate results
11951209
select exp(32.999);

0 commit comments

Comments
 (0)