Commit 101c7ee3 authored by Andres Freund's avatar Andres Freund

Use new overflow aware integer operations.

A previous commit added inline functions that provide fast(er) and
correct overflow checks for signed integer math. Use them in a
significant portion of backend code.  There's more to touch in both
backend and frontend code, but these were the easily identifiable
cases.

The old overflow checks are noticeable in integer heavy workloads.

A secondary benefit is that getting rid of overflow checks that rely
on signed integer overflow wrapping around, will allow us to get rid
of -fwrapv in the future. Which in turn slows down other code.

Author: Andres Freund
Discussion: https://postgr.es/m/20171024103954.ztmatprlglz3rwke@alap3.anarazel.de
parent 4d6ad312
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "btree_gist.h" #include "btree_gist.h"
#include "btree_utils_num.h" #include "btree_utils_num.h"
#include "common/int.h"
#include "utils/cash.h" #include "utils/cash.h"
typedef struct typedef struct
...@@ -99,15 +100,14 @@ cash_dist(PG_FUNCTION_ARGS) ...@@ -99,15 +100,14 @@ cash_dist(PG_FUNCTION_ARGS)
Cash r; Cash r;
Cash ra; Cash ra;
r = a - b; if (pg_sub_s64_overflow(a, b, &r) ||
ra = Abs(r); r == INT64_MIN)
/* Overflow check. */
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("money out of range"))); errmsg("money out of range")));
ra = Abs(r);
PG_RETURN_CASH(ra); PG_RETURN_CASH(ra);
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "btree_gist.h" #include "btree_gist.h"
#include "btree_utils_num.h" #include "btree_utils_num.h"
#include "common/int.h"
typedef struct int16key typedef struct int16key
{ {
...@@ -98,15 +99,14 @@ int2_dist(PG_FUNCTION_ARGS) ...@@ -98,15 +99,14 @@ int2_dist(PG_FUNCTION_ARGS)
int16 r; int16 r;
int16 ra; int16 ra;
r = a - b; if (pg_sub_s16_overflow(a, b, &r) ||
ra = Abs(r); r == INT16_MIN)
/* Overflow check. */
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range"))); errmsg("smallint out of range")));
ra = Abs(r);
PG_RETURN_INT16(ra); PG_RETURN_INT16(ra);
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "btree_gist.h" #include "btree_gist.h"
#include "btree_utils_num.h" #include "btree_utils_num.h"
#include "common/int.h"
typedef struct int32key typedef struct int32key
{ {
...@@ -99,15 +100,14 @@ int4_dist(PG_FUNCTION_ARGS) ...@@ -99,15 +100,14 @@ int4_dist(PG_FUNCTION_ARGS)
int32 r; int32 r;
int32 ra; int32 ra;
r = a - b; if (pg_sub_s32_overflow(a, b, &r) ||
ra = Abs(r); r == INT32_MIN)
/* Overflow check. */
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
ra = Abs(r);
PG_RETURN_INT32(ra); PG_RETURN_INT32(ra);
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "btree_gist.h" #include "btree_gist.h"
#include "btree_utils_num.h" #include "btree_utils_num.h"
#include "common/int.h"
typedef struct int64key typedef struct int64key
{ {
...@@ -99,15 +100,14 @@ int8_dist(PG_FUNCTION_ARGS) ...@@ -99,15 +100,14 @@ int8_dist(PG_FUNCTION_ARGS)
int64 r; int64 r;
int64 ra; int64 ra;
r = a - b; if (pg_sub_s64_overflow(a, b, &r) ||
ra = Abs(r); r == INT64_MIN)
/* Overflow check. */
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range"))); errmsg("bigint out of range")));
ra = Abs(r);
PG_RETURN_INT64(ra); PG_RETURN_INT64(ra);
} }
......
...@@ -89,8 +89,6 @@ typedef struct ...@@ -89,8 +89,6 @@ typedef struct
#define GET_FLOAT_DISTANCE(t, arg1, arg2) Abs( ((float8) *((const t *) (arg1))) - ((float8) *((const t *) (arg2))) ) #define GET_FLOAT_DISTANCE(t, arg1, arg2) Abs( ((float8) *((const t *) (arg1))) - ((float8) *((const t *) (arg2))) )
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/* /*
* check to see if a float4/8 val has underflowed or overflowed * check to see if a float4/8 val has underflowed or overflowed
* borrowed from src/backend/utils/adt/float.c * borrowed from src/backend/utils/adt/float.c
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -118,15 +119,11 @@ array_append(PG_FUNCTION_ARGS) ...@@ -118,15 +119,11 @@ array_append(PG_FUNCTION_ARGS)
if (eah->ndims == 1) if (eah->ndims == 1)
{ {
/* append newelem */ /* append newelem */
int ub;
lb = eah->lbound; lb = eah->lbound;
dimv = eah->dims; dimv = eah->dims;
ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
/* overflow? */ /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
if (indx < ub) if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
...@@ -176,11 +173,9 @@ array_prepend(PG_FUNCTION_ARGS) ...@@ -176,11 +173,9 @@ array_prepend(PG_FUNCTION_ARGS)
{ {
/* prepend newelem */ /* prepend newelem */
lb = eah->lbound; lb = eah->lbound;
indx = lb[0] - 1;
lb0 = lb[0]; lb0 = lb[0];
/* overflow? */ if (pg_sub_s32_overflow(lb0, 1, &indx))
if (indx > lb[0])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
#include "common/int.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/cash.h" #include "utils/cash.h"
...@@ -199,20 +200,21 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -199,20 +200,21 @@ cash_in(PG_FUNCTION_ARGS)
for (; *s; s++) for (; *s; s++)
{ {
/* we look for digits as long as we have found less */ /*
/* than the required number of decimal places */ * We look for digits as long as we have found less than the required
* number of decimal places.
*/
if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint)) if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
{ {
Cash newvalue = (value * 10) - (*s - '0'); int8 digit = *s - '0';
if (newvalue / 10 != value) if (pg_mul_s64_overflow(value, 10, &value) ||
pg_sub_s64_overflow(value, digit, &value))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s", errmsg("value \"%s\" is out of range for type %s",
str, "money"))); str, "money")));
value = newvalue;
if (seen_dot) if (seen_dot)
dec++; dec++;
} }
...@@ -230,26 +232,23 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -230,26 +232,23 @@ cash_in(PG_FUNCTION_ARGS)
/* round off if there's another digit */ /* round off if there's another digit */
if (isdigit((unsigned char) *s) && *s >= '5') if (isdigit((unsigned char) *s) && *s >= '5')
value--; /* remember we build the value in the negative */ {
/* remember we build the value in the negative */
if (value > 0) if (pg_sub_s64_overflow(value, 1, &value))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s", errmsg("value \"%s\" is out of range for type %s",
str, "money"))); str, "money")));
}
/* adjust for less than required decimal places */ /* adjust for less than required decimal places */
for (; dec < fpoint; dec++) for (; dec < fpoint; dec++)
{ {
Cash newvalue = value * 10; if (pg_mul_s64_overflow(value, 10, &value))
if (newvalue / 10 != value)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s", errmsg("value \"%s\" is out of range for type %s",
str, "money"))); str, "money")));
value = newvalue;
} }
/* /*
...@@ -285,12 +284,12 @@ cash_in(PG_FUNCTION_ARGS) ...@@ -285,12 +284,12 @@ cash_in(PG_FUNCTION_ARGS)
*/ */
if (sgn > 0) if (sgn > 0)
{ {
result = -value; if (value == PG_INT64_MIN)
if (result < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s", errmsg("value \"%s\" is out of range for type %s",
str, "money"))); str, "money")));
result = -value;
} }
else else
result = value; result = value;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <limits.h> #include <limits.h>
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -3548,9 +3549,7 @@ width_bucket_float8(PG_FUNCTION_ARGS) ...@@ -3548,9 +3549,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
result = 0; result = 0;
else if (operand >= bound2) else if (operand >= bound2)
{ {
result = count + 1; if (pg_add_s32_overflow(count, 1, &result))
/* check for overflow */
if (result < count)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
...@@ -3564,9 +3563,7 @@ width_bucket_float8(PG_FUNCTION_ARGS) ...@@ -3564,9 +3563,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
result = 0; result = 0;
else if (operand <= bound2) else if (operand <= bound2)
{ {
result = count + 1; if (pg_add_s32_overflow(count, 1, &result))
/* check for overflow */
if (result < count)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
......
This diff is collapsed.
This diff is collapsed.
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "access/hash.h" #include "access/hash.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h"
#include "funcapi.h" #include "funcapi.h"
#include "lib/hyperloglog.h" #include "lib/hyperloglog.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
...@@ -6169,8 +6170,7 @@ numericvar_to_int64(const NumericVar *var, int64 *result) ...@@ -6169,8 +6170,7 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
int ndigits; int ndigits;
int weight; int weight;
int i; int i;
int64 val, int64 val;
oldval;
bool neg; bool neg;
NumericVar rounded; NumericVar rounded;
...@@ -6196,27 +6196,25 @@ numericvar_to_int64(const NumericVar *var, int64 *result) ...@@ -6196,27 +6196,25 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
weight = rounded.weight; weight = rounded.weight;
Assert(weight >= 0 && ndigits <= weight + 1); Assert(weight >= 0 && ndigits <= weight + 1);
/* Construct the result */ /*
* Construct the result. To avoid issues with converting a value
* corresponding to INT64_MIN (which can't be represented as a positive 64
* bit two's complement integer), accumulate value as a negative number.
*/
digits = rounded.digits; digits = rounded.digits;
neg = (rounded.sign == NUMERIC_NEG); neg = (rounded.sign == NUMERIC_NEG);
val = digits[0]; val = -digits[0];
for (i = 1; i <= weight; i++) for (i = 1; i <= weight; i++)
{ {
oldval = val; if (unlikely(pg_mul_s64_overflow(val, NBASE, &val)))
val *= NBASE; {
if (i < ndigits) free_var(&rounded);
val += digits[i]; return false;
}
/* if (i < ndigits)
* The overflow check is a bit tricky because we want to accept
* INT64_MIN, which will overflow the positive accumulator. We can
* detect this case easily though because INT64_MIN is the only
* nonzero value for which -val == val (on a two's complement machine,
* anyway).
*/
if ((val / NBASE) != oldval) /* possible overflow? */
{ {
if (!neg || (-val) != val || val == 0 || oldval < 0) if (unlikely(pg_sub_s64_overflow(val, digits[i], &val)))
{ {
free_var(&rounded); free_var(&rounded);
return false; return false;
...@@ -6226,7 +6224,14 @@ numericvar_to_int64(const NumericVar *var, int64 *result) ...@@ -6226,7 +6224,14 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
free_var(&rounded); free_var(&rounded);
*result = neg ? -val : val; if (!neg)
{
if (unlikely(val == INT64_MIN))
return false;
val = -val;
}
*result = val;
return true; return true;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "common/int.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/formatting.h" #include "utils/formatting.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
...@@ -1045,19 +1046,12 @@ repeat(PG_FUNCTION_ARGS) ...@@ -1045,19 +1046,12 @@ repeat(PG_FUNCTION_ARGS)
count = 0; count = 0;
slen = VARSIZE_ANY_EXHDR(string); slen = VARSIZE_ANY_EXHDR(string);
tlen = VARHDRSZ + (count * slen);
/* Check for integer overflow */ if (unlikely(pg_mul_s32_overflow(count, slen, &tlen)) ||
if (slen != 0 && count != 0) unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)))
{ ereport(ERROR,
int check = count * slen; (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
int check2 = check + VARHDRSZ; errmsg("requested length too large")));
if ((check / slen) != count || check2 <= check)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("requested length too large")));
}
result = (text *) palloc(tlen); result = (text *) palloc(tlen);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "common/int.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "utils/array.h" #include "utils/array.h"
...@@ -1166,8 +1167,7 @@ bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl) ...@@ -1166,8 +1167,7 @@ bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SUBSTRING_ERROR), (errcode(ERRCODE_SUBSTRING_ERROR),
errmsg("negative substring length not allowed"))); errmsg("negative substring length not allowed")));
sp_pl_sl = sp + sl; if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
if (sp_pl_sl <= sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "access/tuptoaster.h" #include "access/tuptoaster.h"
#include "catalog/pg_collation.h" #include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h"
#include "common/md5.h" #include "common/md5.h"
#include "lib/hyperloglog.h" #include "lib/hyperloglog.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
...@@ -1047,8 +1048,7 @@ text_overlay(text *t1, text *t2, int sp, int sl) ...@@ -1047,8 +1048,7 @@ text_overlay(text *t1, text *t2, int sp, int sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SUBSTRING_ERROR), (errcode(ERRCODE_SUBSTRING_ERROR),
errmsg("negative substring length not allowed"))); errmsg("negative substring length not allowed")));
sp_pl_sl = sp + sl; if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
if (sp_pl_sl <= sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
...@@ -2950,8 +2950,7 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) ...@@ -2950,8 +2950,7 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SUBSTRING_ERROR), (errcode(ERRCODE_SUBSTRING_ERROR),
errmsg("negative substring length not allowed"))); errmsg("negative substring length not allowed")));
sp_pl_sl = sp + sl; if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
if (sp_pl_sl <= sl)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
...@@ -5279,13 +5278,13 @@ text_format_parse_digits(const char **ptr, const char *end_ptr, int *value) ...@@ -5279,13 +5278,13 @@ text_format_parse_digits(const char **ptr, const char *end_ptr, int *value)
while (*cp >= '0' && *cp <= '9') while (*cp >= '0' && *cp <= '9')
{ {
int newval = val * 10 + (*cp - '0'); int8 digit = (*cp - '0');
if (newval / 10 != val) /* overflow? */ if (unlikely(pg_mul_s32_overflow(val, 10, &val)) ||
unlikely(pg_add_s32_overflow(val, digit, &val)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("number is out of range"))); errmsg("number is out of range")));
val = newval;
ADVANCE_PARSE_POINTER(cp, end_ptr); ADVANCE_PARSE_POINTER(cp, end_ptr);
found = true; found = true;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment