]> granicus.if.org Git - postgresql/commitdiff
Use new overflow aware integer operations.
authorAndres Freund <andres@anarazel.de>
Wed, 13 Dec 2017 00:32:31 +0000 (16:32 -0800)
committerAndres Freund <andres@anarazel.de>
Wed, 13 Dec 2017 00:55:37 +0000 (16:55 -0800)
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

14 files changed:
contrib/btree_gist/btree_cash.c
contrib/btree_gist/btree_int2.c
contrib/btree_gist/btree_int4.c
contrib/btree_gist/btree_int8.c
contrib/btree_gist/btree_utils_num.h
src/backend/utils/adt/array_userfuncs.c
src/backend/utils/adt/cash.c
src/backend/utils/adt/float.c
src/backend/utils/adt/int.c
src/backend/utils/adt/int8.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/oracle_compat.c
src/backend/utils/adt/varbit.c
src/backend/utils/adt/varlena.c

index 81131af4dc88caaa01e0442d0d178f20f00d34df..18f45f2750be542568f2827192f1a0411661248e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "btree_gist.h"
 #include "btree_utils_num.h"
+#include "common/int.h"
 #include "utils/cash.h"
 
 typedef struct
@@ -99,15 +100,14 @@ cash_dist(PG_FUNCTION_ARGS)
        Cash            r;
        Cash            ra;
 
-       r = a - b;
-       ra = Abs(r);
-
-       /* Overflow check. */
-       if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+       if (pg_sub_s64_overflow(a, b, &r) ||
+               r == INT64_MIN)
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("money out of range")));
 
+       ra = Abs(r);
+
        PG_RETURN_CASH(ra);
 }
 
index f343b8615f67d629faf7d3205547ddb464282010..c2af4cd566d5ab26f73dc0ea00a7618ea65f6c16 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "btree_gist.h"
 #include "btree_utils_num.h"
+#include "common/int.h"
 
 typedef struct int16key
 {
@@ -98,15 +99,14 @@ int2_dist(PG_FUNCTION_ARGS)
        int16           r;
        int16           ra;
 
-       r = a - b;
-       ra = Abs(r);
-
-       /* Overflow check. */
-       if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+       if (pg_sub_s16_overflow(a, b, &r) ||
+               r == INT16_MIN)
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
 
+       ra = Abs(r);
+
        PG_RETURN_INT16(ra);
 }
 
index 35bb4424379caa806235d110708c03f529095f27..f2b6dec6607a5c498348c822c3195655989fefd2 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "btree_gist.h"
 #include "btree_utils_num.h"
+#include "common/int.h"
 
 typedef struct int32key
 {
@@ -99,15 +100,14 @@ int4_dist(PG_FUNCTION_ARGS)
        int32           r;
        int32           ra;
 
-       r = a - b;
-       ra = Abs(r);
-
-       /* Overflow check. */
-       if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+       if (pg_sub_s32_overflow(a, b, &r) ||
+               r == INT32_MIN)
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
 
+       ra = Abs(r);
+
        PG_RETURN_INT32(ra);
 }
 
index 91f2d032d10917a011c12ad94d8e4f9b2b5e434a..16db0028b7a69777fdc2c704d347a4c462b0886c 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "btree_gist.h"
 #include "btree_utils_num.h"
+#include "common/int.h"
 
 typedef struct int64key
 {
@@ -99,15 +100,14 @@ int8_dist(PG_FUNCTION_ARGS)
        int64           r;
        int64           ra;
 
-       r = a - b;
-       ra = Abs(r);
-
-       /* Overflow check. */
-       if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+       if (pg_sub_s64_overflow(a, b, &r) ||
+               r == INT64_MIN)
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
 
+       ra = Abs(r);
+
        PG_RETURN_INT64(ra);
 }
 
index 17561fa9e4ef888a8e459a0c0330e0044a7e8c5f..d7945f856c89bfc168fd94ccfeaa145b8b077392 100644 (file)
@@ -89,8 +89,6 @@ typedef struct
 
 #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
  * borrowed from src/backend/utils/adt/float.c
index 87d79f3f98b8b849cd39418345b68e6b890fcb2f..bb70cba1713d6e53beb982ef2fca9a1a01568965 100644 (file)
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "catalog/pg_type.h"
+#include "common/int.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -118,15 +119,11 @@ array_append(PG_FUNCTION_ARGS)
        if (eah->ndims == 1)
        {
                /* append newelem */
-               int                     ub;
-
                lb = eah->lbound;
                dimv = eah->dims;
-               ub = dimv[0] + lb[0] - 1;
-               indx = ub + 1;
 
-               /* overflow? */
-               if (indx < ub)
+               /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
+               if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("integer out of range")));
@@ -176,11 +173,9 @@ array_prepend(PG_FUNCTION_ARGS)
        {
                /* prepend newelem */
                lb = eah->lbound;
-               indx = lb[0] - 1;
                lb0 = lb[0];
 
-               /* overflow? */
-               if (indx > lb[0])
+               if (pg_sub_s32_overflow(lb0, 1, &indx))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("integer out of range")));
index 7bbc634bd2b1e22cd522827f5f3963a3a1e30832..c787dd341913222acb853c0824aea879ef876df6 100644 (file)
@@ -22,6 +22,7 @@
 #include <ctype.h>
 #include <math.h>
 
+#include "common/int.h"
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
 #include "utils/cash.h"
@@ -199,20 +200,21 @@ cash_in(PG_FUNCTION_ARGS)
 
        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))
                {
-                       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,
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                 errmsg("value \"%s\" is out of range for type %s",
                                                                str, "money")));
 
-                       value = newvalue;
-
                        if (seen_dot)
                                dec++;
                }
@@ -230,26 +232,23 @@ cash_in(PG_FUNCTION_ARGS)
 
        /* round off if there's another digit */
        if (isdigit((unsigned char) *s) && *s >= '5')
-               value--;                                /* remember we build the value in the negative */
-
-       if (value > 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                errmsg("value \"%s\" is out of range for type %s",
-                                               str, "money")));
+       {
+               /* remember we build the value in the negative */
+               if (pg_sub_s64_overflow(value, 1, &value))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("value \"%s\" is out of range for type %s",
+                                                       str, "money")));
+       }
 
        /* adjust for less than required decimal places */
        for (; dec < fpoint; dec++)
        {
-               Cash            newvalue = value * 10;
-
-               if (newvalue / 10 != value)
+               if (pg_mul_s64_overflow(value, 10, &value))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("value \"%s\" is out of range for type %s",
                                                        str, "money")));
-
-               value = newvalue;
        }
 
        /*
@@ -285,12 +284,12 @@ cash_in(PG_FUNCTION_ARGS)
         */
        if (sgn > 0)
        {
-               result = -value;
-               if (result < 0)
+               if (value == PG_INT64_MIN)
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("value \"%s\" is out of range for type %s",
                                                        str, "money")));
+               result = -value;
        }
        else
                result = value;
index 18b3b949acbbaeafa3f02981d4d064bc2f4fa37c..be65aab1c909be89b7ddbda341bfd5bdd8bbc357 100644 (file)
@@ -20,6 +20,7 @@
 #include <limits.h>
 
 #include "catalog/pg_type.h"
+#include "common/int.h"
 #include "libpq/pqformat.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -3548,9 +3549,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
                        result = 0;
                else if (operand >= bound2)
                {
-                       result = count + 1;
-                       /* check for overflow */
-                       if (result < count)
+                       if (pg_add_s32_overflow(count, 1, &result))
                                ereport(ERROR,
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                 errmsg("integer out of range")));
@@ -3564,9 +3563,7 @@ width_bucket_float8(PG_FUNCTION_ARGS)
                        result = 0;
                else if (operand <= bound2)
                {
-                       result = count + 1;
-                       /* check for overflow */
-                       if (result < count)
+                       if (pg_add_s32_overflow(count, 1, &result))
                                ereport(ERROR,
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                 errmsg("integer out of range")));
index 4cd8960b3fca3223e9ee4fad6bd7576591a7ba4d..36ba86ca734bede4415d10fd0c6a00664e5aaa0f 100644 (file)
 #include <limits.h>
 
 #include "catalog/pg_type.h"
+#include "common/int.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 
-
-#define SAMESIGN(a,b)  (((a) < 0) == ((b) < 0))
-
 #define Int2VectorSize(n)      (offsetof(int2vector, values) + (n) * sizeof(int16))
 
 typedef struct
@@ -328,7 +326,7 @@ i4toi2(PG_FUNCTION_ARGS)
 {
        int32           arg1 = PG_GETARG_INT32(0);
 
-       if (arg1 < SHRT_MIN || arg1 > SHRT_MAX)
+       if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
@@ -598,15 +596,12 @@ Datum
 int4um(PG_FUNCTION_ARGS)
 {
        int32           arg = PG_GETARG_INT32(0);
-       int32           result;
 
-       result = -arg;
-       /* overflow check (needed for INT_MIN) */
-       if (arg != 0 && SAMESIGN(result, arg))
+       if (unlikely(arg == PG_INT32_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
-       PG_RETURN_INT32(result);
+       PG_RETURN_INT32(-arg);
 }
 
 Datum
@@ -624,14 +619,7 @@ int4pl(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s32_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -645,14 +633,7 @@ int4mi(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s32_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -666,24 +647,7 @@ int4mul(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg2 gives arg1
-        * again.  There are two cases where this fails: arg2 = 0 (which cannot
-        * overflow) and arg1 = INT_MIN, arg2 = -1 (where the division itself will
-        * overflow and thus incorrectly match).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int16
-        * range; if so, no overflow is possible.
-        */
-       if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX &&
-                 arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
-               arg2 != 0 &&
-               ((arg2 == -1 && arg1 < 0 && result < 0) ||
-                result / arg2 != arg1))
+       if (unlikely(pg_mul_s32_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -714,12 +678,11 @@ int4div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for INT_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == PG_INT32_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("integer out of range")));
+               result = -arg1;
                PG_RETURN_INT32(result);
        }
 
@@ -736,9 +699,7 @@ int4inc(PG_FUNCTION_ARGS)
        int32           arg = PG_GETARG_INT32(0);
        int32           result;
 
-       result = arg + 1;
-       /* Overflow check */
-       if (arg > 0 && result < 0)
+       if (unlikely(pg_add_s32_overflow(arg, 1, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -750,15 +711,12 @@ Datum
 int2um(PG_FUNCTION_ARGS)
 {
        int16           arg = PG_GETARG_INT16(0);
-       int16           result;
 
-       result = -arg;
-       /* overflow check (needed for SHRT_MIN) */
-       if (arg != 0 && SAMESIGN(result, arg))
+       if (unlikely(arg == PG_INT16_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
-       PG_RETURN_INT16(result);
+       PG_RETURN_INT16(-arg);
 }
 
 Datum
@@ -776,14 +734,7 @@ int2pl(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int16           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s16_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
@@ -797,14 +748,7 @@ int2mi(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int16           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s16_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
@@ -816,20 +760,14 @@ int2mul(PG_FUNCTION_ARGS)
 {
        int16           arg1 = PG_GETARG_INT16(0);
        int16           arg2 = PG_GETARG_INT16(1);
-       int32           result32;
-
-       /*
-        * The most practical way to detect overflow is to do the arithmetic in
-        * int32 (so that the result can't overflow) and then do a range check.
-        */
-       result32 = (int32) arg1 * (int32) arg2;
+       int16           result;
 
-       if (result32 < SHRT_MIN || result32 > SHRT_MAX)
+       if (unlikely(pg_mul_s16_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
 
-       PG_RETURN_INT16((int16) result32);
+       PG_RETURN_INT16(result);
 }
 
 Datum
@@ -856,12 +794,11 @@ int2div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for SHRT_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == INT16_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("smallint out of range")));
+               result = -arg1;
                PG_RETURN_INT16(result);
        }
 
@@ -879,14 +816,7 @@ int24pl(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s32_overflow((int32) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -900,14 +830,7 @@ int24mi(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s32_overflow((int32) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -921,20 +844,7 @@ int24mul(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int32           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg2 gives arg1
-        * again.  There is one case where this fails: arg2 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int16
-        * range; if so, no overflow is possible.
-        */
-       if (!(arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
-               result / arg2 != arg1)
+       if (unlikely(pg_mul_s32_overflow((int32) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -947,7 +857,7 @@ int24div(PG_FUNCTION_ARGS)
        int16           arg1 = PG_GETARG_INT16(0);
        int32           arg2 = PG_GETARG_INT32(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -967,14 +877,7 @@ int42pl(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int32           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s32_overflow(arg1, (int32) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -988,14 +891,7 @@ int42mi(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int32           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s32_overflow(arg1, (int32) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -1009,20 +905,7 @@ int42mul(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int32           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg1 gives arg2
-        * again.  There is one case where this fails: arg1 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int16
-        * range; if so, no overflow is possible.
-        */
-       if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX) &&
-               result / arg1 != arg2)
+       if (unlikely(pg_mul_s32_overflow(arg1, (int32) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -1036,7 +919,7 @@ int42div(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int32           result;
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1053,12 +936,11 @@ int42div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for INT_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == PG_INT32_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("integer out of range")));
+               result = -arg1;
                PG_RETURN_INT32(result);
        }
 
@@ -1075,7 +957,7 @@ int4mod(PG_FUNCTION_ARGS)
        int32           arg1 = PG_GETARG_INT32(0);
        int32           arg2 = PG_GETARG_INT32(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1103,7 +985,7 @@ int2mod(PG_FUNCTION_ARGS)
        int16           arg1 = PG_GETARG_INT16(0);
        int16           arg2 = PG_GETARG_INT16(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1136,12 +1018,11 @@ int4abs(PG_FUNCTION_ARGS)
        int32           arg1 = PG_GETARG_INT32(0);
        int32           result;
 
-       result = (arg1 < 0) ? -arg1 : arg1;
-       /* overflow check (needed for INT_MIN) */
-       if (result < 0)
+       if (unlikely(arg1 == INT32_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
+       result = (arg1 < 0) ? -arg1 : arg1;
        PG_RETURN_INT32(result);
 }
 
@@ -1151,12 +1032,11 @@ int2abs(PG_FUNCTION_ARGS)
        int16           arg1 = PG_GETARG_INT16(0);
        int16           result;
 
-       result = (arg1 < 0) ? -arg1 : arg1;
-       /* overflow check (needed for SHRT_MIN) */
-       if (result < 0)
+       if (unlikely(arg1 == INT16_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
+       result = (arg1 < 0) ? -arg1 : arg1;
        PG_RETURN_INT16(result);
 }
 
@@ -1381,11 +1261,11 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
        if ((fctx->step > 0 && fctx->current <= fctx->finish) ||
                (fctx->step < 0 && fctx->current >= fctx->finish))
        {
-               /* increment current in preparation for next iteration */
-               fctx->current += fctx->step;
-
-               /* if next-value computation overflows, this is the final result */
-               if (SAMESIGN(result, fctx->step) && !SAMESIGN(result, fctx->current))
+               /*
+                * Increment current in preparation for next iteration. If next-value
+                * computation overflows, this is the final result.
+                */
+               if (pg_add_s32_overflow(fctx->current, fctx->step, &fctx->current))
                        fctx->step = 0;
 
                /* do when there is more left to send */
index afa434cfee21a246174b40b15ac417f902263874..bc8dad5c5d892aecb5c3acc38fd3d326c7bac5f8 100644 (file)
@@ -17,6 +17,7 @@
 #include <limits.h>
 #include <math.h>
 
+#include "common/int.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "utils/int8.h"
@@ -25,8 +26,6 @@
 
 #define MAXINT8LEN             25
 
-#define SAMESIGN(a,b)  (((a) < 0) == ((b) < 0))
-
 typedef struct
 {
        int64           current;
@@ -56,11 +55,14 @@ scanint8(const char *str, bool errorOK, int64 *result)
 {
        const char *ptr = str;
        int64           tmp = 0;
-       int                     sign = 1;
+       bool            neg = false;
 
        /*
         * Do our own scan, rather than relying on sscanf which might be broken
         * for long long.
+        *
+        * As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
+        * value as a negative number.
         */
 
        /* skip leading spaces */
@@ -71,72 +73,60 @@ scanint8(const char *str, bool errorOK, int64 *result)
        if (*ptr == '-')
        {
                ptr++;
-
-               /*
-                * Do an explicit check for INT64_MIN.  Ugly though this is, it's
-                * cleaner than trying to get the loop below to handle it portably.
-                */
-               if (strncmp(ptr, "9223372036854775808", 19) == 0)
-               {
-                       tmp = PG_INT64_MIN;
-                       ptr += 19;
-                       goto gotdigits;
-               }
-               sign = -1;
+               neg = true;
        }
        else if (*ptr == '+')
                ptr++;
 
        /* require at least one digit */
-       if (!isdigit((unsigned char) *ptr))
+       if (unlikely(!isdigit((unsigned char) *ptr)))
        {
-               if (errorOK)
-                       return false;
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("invalid input syntax for integer: \"%s\"",
-                                                       str)));
+               goto invalid_syntax;
        }
 
        /* process digits */
        while (*ptr && isdigit((unsigned char) *ptr))
        {
-               int64           newtmp = tmp * 10 + (*ptr++ - '0');
-
-               if ((newtmp / 10) != tmp)       /* overflow? */
-               {
-                       if (errorOK)
-                               return false;
-                       else
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                                errmsg("value \"%s\" is out of range for type %s",
-                                                               str, "bigint")));
-               }
-               tmp = newtmp;
-       }
+               int8            digit = (*ptr++ - '0');
 
-gotdigits:
+               if (unlikely(pg_mul_s64_overflow(tmp, 10, &tmp)) ||
+                       unlikely(pg_sub_s64_overflow(tmp, digit, &tmp)))
+                       goto out_of_range;
+       }
 
        /* allow trailing whitespace, but not other trailing chars */
        while (*ptr != '\0' && isspace((unsigned char) *ptr))
                ptr++;
 
-       if (*ptr != '\0')
+       if (unlikely(*ptr != '\0'))
+               goto invalid_syntax;
+
+       if (!neg)
        {
-               if (errorOK)
-                       return false;
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("invalid input syntax for integer: \"%s\"",
-                                                       str)));
+               if (unlikely(tmp == INT64_MIN))
+                       goto out_of_range;
+               tmp = -tmp;
        }
-
-       *result = (sign < 0) ? -tmp : tmp;
+       *result = tmp;
 
        return true;
+
+out_of_range:
+       if (errorOK)
+               return false;
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                errmsg("value \"%s\" is out of range for type %s",
+                                               str, "bigint")));
+invalid_syntax:
+       if (errorOK)
+               return false;
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for integer: \"%s\"",
+                                               str)));
 }
 
 /* int8in()
@@ -492,12 +482,11 @@ int8um(PG_FUNCTION_ARGS)
        int64           arg = PG_GETARG_INT64(0);
        int64           result;
 
-       result = -arg;
-       /* overflow check (needed for INT64_MIN) */
-       if (arg != 0 && SAMESIGN(result, arg))
+       if (unlikely(arg == PG_INT64_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
+       result = -arg;
        PG_RETURN_INT64(result);
 }
 
@@ -516,14 +505,7 @@ int8pl(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s64_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -537,14 +519,7 @@ int8mi(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s64_overflow(arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -558,28 +533,10 @@ int8mul(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg2 gives arg1
-        * again.  There are two cases where this fails: arg2 = 0 (which cannot
-        * overflow) and arg1 = INT64_MIN, arg2 = -1 (where the division itself
-        * will overflow and thus incorrectly match).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int32
-        * range; if so, no overflow is possible.
-        */
-       if (arg1 != (int64) ((int32) arg1) || arg2 != (int64) ((int32) arg2))
-       {
-               if (arg2 != 0 &&
-                       ((arg2 == -1 && arg1 < 0 && result < 0) ||
-                        result / arg2 != arg1))
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                        errmsg("bigint out of range")));
-       }
+       if (unlikely(pg_mul_s64_overflow(arg1, arg2, &result)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                errmsg("bigint out of range")));
        PG_RETURN_INT64(result);
 }
 
@@ -607,12 +564,11 @@ int8div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for INT64_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == INT64_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
+               result = -arg1;
                PG_RETURN_INT64(result);
        }
 
@@ -632,12 +588,11 @@ int8abs(PG_FUNCTION_ARGS)
        int64           arg1 = PG_GETARG_INT64(0);
        int64           result;
 
-       result = (arg1 < 0) ? -arg1 : arg1;
-       /* overflow check (needed for INT64_MIN) */
-       if (result < 0)
+       if (unlikely(arg1 == INT64_MIN))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
+       result = (arg1 < 0) ? -arg1 : arg1;
        PG_RETURN_INT64(result);
 }
 
@@ -650,7 +605,7 @@ int8mod(PG_FUNCTION_ARGS)
        int64           arg1 = PG_GETARG_INT64(0);
        int64           arg2 = PG_GETARG_INT64(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -687,16 +642,12 @@ int8inc(PG_FUNCTION_ARGS)
        if (AggCheckCallContext(fcinfo, NULL))
        {
                int64      *arg = (int64 *) PG_GETARG_POINTER(0);
-               int64           result;
 
-               result = *arg + 1;
-               /* Overflow check */
-               if (result < 0 && *arg > 0)
+               if (unlikely(pg_add_s64_overflow(*arg, 1, arg)))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
 
-               *arg = result;
                PG_RETURN_POINTER(arg);
        }
        else
@@ -706,9 +657,7 @@ int8inc(PG_FUNCTION_ARGS)
                int64           arg = PG_GETARG_INT64(0);
                int64           result;
 
-               result = arg + 1;
-               /* Overflow check */
-               if (result < 0 && arg > 0)
+               if (unlikely(pg_add_s64_overflow(arg, 1, &result)))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
@@ -731,16 +680,11 @@ int8dec(PG_FUNCTION_ARGS)
        if (AggCheckCallContext(fcinfo, NULL))
        {
                int64      *arg = (int64 *) PG_GETARG_POINTER(0);
-               int64           result;
 
-               result = *arg - 1;
-               /* Overflow check */
-               if (result > 0 && *arg < 0)
+               if (unlikely(pg_sub_s64_overflow(*arg, 1, arg)))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
-
-               *arg = result;
                PG_RETURN_POINTER(arg);
        }
        else
@@ -750,9 +694,7 @@ int8dec(PG_FUNCTION_ARGS)
                int64           arg = PG_GETARG_INT64(0);
                int64           result;
 
-               result = arg - 1;
-               /* Overflow check */
-               if (result > 0 && arg < 0)
+               if (unlikely(pg_sub_s64_overflow(arg, 1, &result)))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
@@ -821,14 +763,7 @@ int84pl(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int64           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -842,14 +777,7 @@ int84mi(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int64           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -863,20 +791,7 @@ int84mul(PG_FUNCTION_ARGS)
        int32           arg2 = PG_GETARG_INT32(1);
        int64           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg1 gives arg2
-        * again.  There is one case where this fails: arg1 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int32
-        * range; if so, no overflow is possible.
-        */
-       if (arg1 != (int64) ((int32) arg1) &&
-               result / arg1 != arg2)
+       if (unlikely(pg_mul_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -907,12 +822,11 @@ int84div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for INT64_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == INT64_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
+               result = -arg1;
                PG_RETURN_INT64(result);
        }
 
@@ -930,14 +844,7 @@ int48pl(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -951,14 +858,7 @@ int48mi(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -972,20 +872,7 @@ int48mul(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg2 gives arg1
-        * again.  There is one case where this fails: arg2 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int32
-        * range; if so, no overflow is possible.
-        */
-       if (arg2 != (int64) ((int32) arg2) &&
-               result / arg2 != arg1)
+       if (unlikely(pg_mul_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -998,7 +885,7 @@ int48div(PG_FUNCTION_ARGS)
        int32           arg1 = PG_GETARG_INT32(0);
        int64           arg2 = PG_GETARG_INT64(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1018,14 +905,7 @@ int82pl(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int64           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1039,14 +919,7 @@ int82mi(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int64           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1060,20 +933,7 @@ int82mul(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int64           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg1 gives arg2
-        * again.  There is one case where this fails: arg1 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int32
-        * range; if so, no overflow is possible.
-        */
-       if (arg1 != (int64) ((int32) arg1) &&
-               result / arg1 != arg2)
+       if (unlikely(pg_mul_s64_overflow(arg1, (int64) arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1087,7 +947,7 @@ int82div(PG_FUNCTION_ARGS)
        int16           arg2 = PG_GETARG_INT16(1);
        int64           result;
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1104,12 +964,11 @@ int82div(PG_FUNCTION_ARGS)
         */
        if (arg2 == -1)
        {
-               result = -arg1;
-               /* overflow check (needed for INT64_MIN) */
-               if (arg1 != 0 && SAMESIGN(result, arg1))
+               if (unlikely(arg1 == INT64_MIN))
                        ereport(ERROR,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("bigint out of range")));
+               result = -arg1;
                PG_RETURN_INT64(result);
        }
 
@@ -1127,14 +986,7 @@ int28pl(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 + arg2;
-
-       /*
-        * Overflow check.  If the inputs are of different signs then their sum
-        * cannot overflow.  If the inputs are of the same sign, their sum had
-        * better be that sign too.
-        */
-       if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_add_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1148,14 +1000,7 @@ int28mi(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 - arg2;
-
-       /*
-        * Overflow check.  If the inputs are of the same sign then their
-        * difference cannot overflow.  If they are of different signs then the
-        * result should be of the same sign as the first input.
-        */
-       if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+       if (unlikely(pg_sub_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1169,20 +1014,7 @@ int28mul(PG_FUNCTION_ARGS)
        int64           arg2 = PG_GETARG_INT64(1);
        int64           result;
 
-       result = arg1 * arg2;
-
-       /*
-        * Overflow check.  We basically check to see if result / arg2 gives arg1
-        * again.  There is one case where this fails: arg2 = 0 (which cannot
-        * overflow).
-        *
-        * Since the division is likely much more expensive than the actual
-        * multiplication, we'd like to skip it where possible.  The best bang for
-        * the buck seems to be to check whether both inputs are in the int32
-        * range; if so, no overflow is possible.
-        */
-       if (arg2 != (int64) ((int32) arg2) &&
-               result / arg2 != arg1)
+       if (unlikely(pg_mul_s64_overflow((int64) arg1, arg2, &result)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
@@ -1195,7 +1027,7 @@ int28div(PG_FUNCTION_ARGS)
        int16           arg1 = PG_GETARG_INT16(0);
        int64           arg2 = PG_GETARG_INT64(1);
 
-       if (arg2 == 0)
+       if (unlikely(arg2 == 0))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -1287,17 +1119,13 @@ Datum
 int84(PG_FUNCTION_ARGS)
 {
        int64           arg = PG_GETARG_INT64(0);
-       int32           result;
-
-       result = (int32) arg;
 
-       /* Test for overflow by reverse-conversion. */
-       if ((int64) result != arg)
+       if (unlikely(arg < PG_INT32_MIN) || unlikely(arg > PG_INT32_MAX))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
 
-       PG_RETURN_INT32(result);
+       PG_RETURN_INT32((int32) arg);
 }
 
 Datum
@@ -1312,17 +1140,13 @@ Datum
 int82(PG_FUNCTION_ARGS)
 {
        int64           arg = PG_GETARG_INT64(0);
-       int16           result;
 
-       result = (int16) arg;
-
-       /* Test for overflow by reverse-conversion. */
-       if ((int64) result != arg)
+       if (unlikely(arg < PG_INT16_MIN) || unlikely(arg > PG_INT16_MAX))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("smallint out of range")));
 
-       PG_RETURN_INT16(result);
+       PG_RETURN_INT16((int16) arg);
 }
 
 Datum
@@ -1348,18 +1172,15 @@ dtoi8(PG_FUNCTION_ARGS)
        /* Round arg to nearest integer (but it's still in float form) */
        arg = rint(arg);
 
-       /*
-        * Does it fit in an int64?  Avoid assuming that we have handy constants
-        * defined for the range boundaries, instead test for overflow by
-        * reverse-conversion.
-        */
-       result = (int64) arg;
-
-       if ((float8) result != arg)
+       if (unlikely(arg < (double) PG_INT64_MIN) ||
+               unlikely(arg > (double) PG_INT64_MAX) ||
+               unlikely(isnan(arg)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
 
+       result = (int64) arg;
+
        PG_RETURN_INT64(result);
 }
 
@@ -1381,42 +1202,32 @@ Datum
 ftoi8(PG_FUNCTION_ARGS)
 {
        float4          arg = PG_GETARG_FLOAT4(0);
-       int64           result;
        float8          darg;
 
        /* Round arg to nearest integer (but it's still in float form) */
        darg = rint(arg);
 
-       /*
-        * Does it fit in an int64?  Avoid assuming that we have handy constants
-        * defined for the range boundaries, instead test for overflow by
-        * reverse-conversion.
-        */
-       result = (int64) darg;
-
-       if ((float8) result != darg)
+       if (unlikely(arg < (float4) PG_INT64_MIN) ||
+               unlikely(arg > (float4) PG_INT64_MAX) ||
+               unlikely(isnan(arg)))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("bigint out of range")));
 
-       PG_RETURN_INT64(result);
+       PG_RETURN_INT64((int64) darg);
 }
 
 Datum
 i8tooid(PG_FUNCTION_ARGS)
 {
        int64           arg = PG_GETARG_INT64(0);
-       Oid                     result;
-
-       result = (Oid) arg;
 
-       /* Test for overflow by reverse-conversion. */
-       if ((int64) result != arg)
+       if (unlikely(arg < 0) || unlikely(arg > PG_UINT32_MAX))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("OID out of range")));
 
-       PG_RETURN_OID(result);
+       PG_RETURN_OID((Oid) arg);
 }
 
 Datum
@@ -1494,11 +1305,11 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
        if ((fctx->step > 0 && fctx->current <= fctx->finish) ||
                (fctx->step < 0 && fctx->current >= fctx->finish))
        {
-               /* increment current in preparation for next iteration */
-               fctx->current += fctx->step;
-
-               /* if next-value computation overflows, this is the final result */
-               if (SAMESIGN(result, fctx->step) && !SAMESIGN(result, fctx->current))
+               /*
+                * Increment current in preparation for next iteration. If next-value
+                * computation overflows, this is the final result.
+                */
+               if (pg_add_s64_overflow(fctx->current, fctx->step, &fctx->current))
                        fctx->step = 0;
 
                /* do when there is more left to send */
index 82e6f4483bd772b1c16bfc010429896bfd017275..e9a6ca3535d862893ac86a50adf3e429992533c4 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "access/hash.h"
 #include "catalog/pg_type.h"
+#include "common/int.h"
 #include "funcapi.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
@@ -6169,8 +6170,7 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
        int                     ndigits;
        int                     weight;
        int                     i;
-       int64           val,
-                               oldval;
+       int64           val;
        bool            neg;
        NumericVar      rounded;
 
@@ -6196,27 +6196,25 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
        weight = rounded.weight;
        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;
        neg = (rounded.sign == NUMERIC_NEG);
-       val = digits[0];
+       val = -digits[0];
        for (i = 1; i <= weight; i++)
        {
-               oldval = val;
-               val *= NBASE;
-               if (i < ndigits)
-                       val += digits[i];
+               if (unlikely(pg_mul_s64_overflow(val, NBASE, &val)))
+               {
+                       free_var(&rounded);
+                       return false;
+               }
 
-               /*
-                * 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 (i < ndigits)
                {
-                       if (!neg || (-val) != val || val == 0 || oldval < 0)
+                       if (unlikely(pg_sub_s64_overflow(val, digits[i], &val)))
                        {
                                free_var(&rounded);
                                return false;
@@ -6226,7 +6224,14 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
 
        free_var(&rounded);
 
-       *result = neg ? -val : val;
+       if (!neg)
+       {
+               if (unlikely(val == INT64_MIN))
+                       return false;
+               val = -val;
+       }
+       *result = val;
+
        return true;
 }
 
index b82016500b0cc7d5d0a9c5e3ac0ffd38f966ce34..a5aa6a95aa7d11429ce7084432d917d6a0d751d2 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include "postgres.h"
 
+#include "common/int.h"
 #include "utils/builtins.h"
 #include "utils/formatting.h"
 #include "mb/pg_wchar.h"
@@ -1045,19 +1046,12 @@ repeat(PG_FUNCTION_ARGS)
                count = 0;
 
        slen = VARSIZE_ANY_EXHDR(string);
-       tlen = VARHDRSZ + (count * slen);
 
-       /* Check for integer overflow */
-       if (slen != 0 && count != 0)
-       {
-               int                     check = count * slen;
-               int                     check2 = check + VARHDRSZ;
-
-               if ((check / slen) != count || check2 <= check)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                                        errmsg("requested length too large")));
-       }
+       if (unlikely(pg_mul_s32_overflow(count, slen, &tlen)) ||
+               unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                errmsg("requested length too large")));
 
        result = (text *) palloc(tlen);
 
index 478fab9bfce060b713e9226dfd8f7ff7674fc595..6caa7e9b5e5f5fc4d2bf7b688ede573af81f9198 100644 (file)
@@ -17,6 +17,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "common/int.h"
 #include "libpq/pqformat.h"
 #include "nodes/nodeFuncs.h"
 #include "utils/array.h"
@@ -1166,8 +1167,7 @@ bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl)
                ereport(ERROR,
                                (errcode(ERRCODE_SUBSTRING_ERROR),
                                 errmsg("negative substring length not allowed")));
-       sp_pl_sl = sp + sl;
-       if (sp_pl_sl <= sl)
+       if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
index 39b68dbc65122eec3a717ae6ccaf9ecc7f9604dc..a84e845ad24b9c94cd5b05eb75e71e496cb95306 100644 (file)
@@ -21,6 +21,7 @@
 #include "access/tuptoaster.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "common/int.h"
 #include "common/md5.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
@@ -1047,8 +1048,7 @@ text_overlay(text *t1, text *t2, int sp, int sl)
                ereport(ERROR,
                                (errcode(ERRCODE_SUBSTRING_ERROR),
                                 errmsg("negative substring length not allowed")));
-       sp_pl_sl = sp + sl;
-       if (sp_pl_sl <= sl)
+       if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                 errmsg("integer out of range")));
@@ -2950,8 +2950,7 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl)
                ereport(ERROR,
                                (errcode(ERRCODE_SUBSTRING_ERROR),
                                 errmsg("negative substring length not allowed")));
-       sp_pl_sl = sp + sl;
-       if (sp_pl_sl <= sl)
+       if (pg_add_s32_overflow(sp, sl, &sp_pl_sl))
                ereport(ERROR,
                                (errcode(ERRCODE_NUMERIC_VALUE_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)
 
        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,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("number is out of range")));
-               val = newval;
                ADVANCE_PARSE_POINTER(cp, end_ptr);
                found = true;
        }