diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-10-04 14:42:48 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-10-04 14:42:48 +0000 |
commit | 4171bb869f234281a13bb862d3b1e577bf336242 (patch) | |
tree | e8193f7be04ddd942f13811ef9bbe0494d24920d /src/backend | |
parent | 24201b4bc60e46e8de031fb5911af32bdb412d43 (diff) | |
download | postgresql-4171bb869f234281a13bb862d3b1e577bf336242.tar.gz postgresql-4171bb869f234281a13bb862d3b1e577bf336242.zip |
Detect overflow in integer arithmetic operators (integer, smallint, and
bigint variants). Clean up some inconsistencies in error message wording.
Fix scanint8 to allow trailing whitespace in INT64_MIN case. Update
int8-exp-three-digits.out, which seems to have been ignored by the last
couple of people to modify the int8 regression test, and remove
int8-exp-three-digits-win32.out which is thereby exposed as redundant.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/utils/adt/float.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/int.c | 283 | ||||
-rw-r--r-- | src/backend/utils/adt/int8.c | 380 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 12 | ||||
-rw-r--r-- | src/backend/utils/adt/numutils.c | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/varbit.c | 4 |
6 files changed, 545 insertions, 144 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index ccee67319ce..9bb521183ab 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.110 2004/09/02 17:12:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.111 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1147,7 +1147,7 @@ dtoi2(PG_FUNCTION_ARGS) if ((num < SHRT_MIN) || (num > SHRT_MAX)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); result = (int16) rint(num); PG_RETURN_INT16(result); @@ -1213,7 +1213,7 @@ ftoi2(PG_FUNCTION_ARGS) if ((num < SHRT_MIN) || (num > SHRT_MAX)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); result = (int16) rint(num); PG_RETURN_INT16(result); diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 1183fa9aa0c..7dde75014e8 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.62 2004/08/29 05:06:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.63 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,6 @@ * Arithmetic operators: * intmod */ - #include "postgres.h" #include <ctype.h> @@ -38,6 +37,7 @@ #include "libpq/pqformat.h" #include "utils/builtins.h" + #ifndef SHRT_MAX #define SHRT_MAX (0x7FFF) #endif @@ -45,6 +45,8 @@ #define SHRT_MIN (-0x8000) #endif +#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) + typedef struct { int32 current; @@ -52,6 +54,7 @@ typedef struct int32 step; } generate_series_fctx; + /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ @@ -291,7 +294,7 @@ i4toi2(PG_FUNCTION_ARGS) if (arg1 < SHRT_MIN || arg1 > SHRT_MAX) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); PG_RETURN_INT16((int16) arg1); } @@ -601,8 +604,15 @@ Datum int4um(PG_FUNCTION_ARGS) { int32 arg = PG_GETARG_INT32(0); + int32 result; - PG_RETURN_INT32(-arg); + result = -arg; + /* overflow check (needed for INT_MIN) */ + if (arg != 0 && SAMESIGN(result, arg)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -618,8 +628,19 @@ int4pl(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 + arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -627,8 +648,19 @@ int4mi(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 - arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -636,8 +668,28 @@ int4mul(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 * arg2); + 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 && + (result/arg2 != arg1 || (arg2 == -1 && arg1 < 0 && result < 0))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -645,29 +697,55 @@ int4div(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - PG_RETURN_INT32(arg1 / arg2); + result = arg1 / arg2; + /* + * Overflow check. The only possible overflow case is for + * arg1 = INT_MIN, arg2 = -1, where the correct result is -INT_MIN, + * which can't be represented on a two's-complement machine. + */ + if (arg2 == -1 && arg1 < 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum int4inc(PG_FUNCTION_ARGS) { int32 arg = PG_GETARG_INT32(0); + int32 result; - PG_RETURN_INT32(arg + 1); + result = arg + 1; + /* Overflow check */ + if (arg > 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT32(result); } Datum int2um(PG_FUNCTION_ARGS) { int16 arg = PG_GETARG_INT16(0); + int16 result; - PG_RETURN_INT16(-arg); + result = -arg; + /* overflow check (needed for SHRT_MIN) */ + if (arg != 0 && SAMESIGN(result, arg)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); } Datum @@ -683,8 +761,19 @@ int2pl(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + int16 result; - PG_RETURN_INT16(arg1 + arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); } Datum @@ -692,8 +781,19 @@ int2mi(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + int16 result; - PG_RETURN_INT16(arg1 - arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); } Datum @@ -701,8 +801,20 @@ int2mul(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result32; - PG_RETURN_INT16(arg1 * arg2); + /* + * 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; + if (result32 < SHRT_MIN || result32 > SHRT_MAX) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + + PG_RETURN_INT16((int16) result32); } Datum @@ -710,13 +822,24 @@ int2div(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + int16 result; if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - PG_RETURN_INT16(arg1 / arg2); + result = arg1 / arg2; + /* + * Overflow check. The only possible overflow case is for + * arg1 = SHRT_MIN, arg2 = -1, where the correct result is -SHRT_MIN, + * which can't be represented on a two's-complement machine. + */ + if (arg2 == -1 && arg1 < 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); } Datum @@ -724,8 +847,19 @@ int24pl(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 + arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -733,8 +867,19 @@ int24mi(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 - arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -742,8 +887,25 @@ int24mul(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); + int32 result; - PG_RETURN_INT32(arg1 * arg2); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -756,8 +918,8 @@ int24div(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - - PG_RETURN_INT32(arg1 / arg2); + /* No overflow is possible */ + PG_RETURN_INT32((int32) arg1 / arg2); } Datum @@ -765,8 +927,19 @@ int42pl(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result; - PG_RETURN_INT32(arg1 + arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -774,8 +947,19 @@ int42mi(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result; - PG_RETURN_INT32(arg1 - arg2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -783,8 +967,25 @@ int42mul(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result; - PG_RETURN_INT32(arg1 * arg2); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -792,13 +993,24 @@ int42div(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result; if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - PG_RETURN_INT32(arg1 / arg2); + result = arg1 / arg2; + /* + * Overflow check. The only possible overflow case is for + * arg1 = INT_MIN, arg2 = -1, where the correct result is -INT_MIN, + * which can't be represented on a two's-complement machine. + */ + if (arg2 == -1 && arg1 < 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -811,6 +1023,7 @@ int4mod(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* No overflow is possible */ PG_RETURN_INT32(arg1 % arg2); } @@ -825,6 +1038,7 @@ int2mod(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* No overflow is possible */ PG_RETURN_INT16(arg1 % arg2); } @@ -839,6 +1053,7 @@ int24mod(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* No overflow is possible */ PG_RETURN_INT32(arg1 % arg2); } @@ -853,6 +1068,7 @@ int42mod(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* No overflow is possible */ PG_RETURN_INT32(arg1 % arg2); } @@ -865,16 +1081,30 @@ Datum int4abs(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); + int32 result; - PG_RETURN_INT32((arg1 < 0) ? -arg1 : arg1); + result = (arg1 < 0) ? -arg1 : arg1; + /* overflow check (needed for INT_MIN) */ + if (result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum int2abs(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); + int16 result; - PG_RETURN_INT16((arg1 < 0) ? -arg1 : arg1); + result = (arg1 < 0) ? -arg1 : arg1; + /* overflow check (needed for SHRT_MIN) */ + if (result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); } Datum @@ -913,7 +1143,8 @@ int4smaller(PG_FUNCTION_ARGS) PG_RETURN_INT32((arg1 < arg2) ? arg1 : arg2); } -/* Binary arithmetics +/* + * Bit-pushing operators * * int[24]and - returns arg1 & arg2 * int[24]or - returns arg1 | arg2 diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index e58c94268ad..96964c82b9e 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/int8.c,v 1.55 2004/08/29 05:06:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/int8.c,v 1.56 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,8 @@ #define MAXINT8LEN 25 +#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) + typedef struct { int64 current; @@ -31,6 +33,7 @@ typedef struct int64 step; } generate_series_fctx; + /*********************************************************************** ** ** Routines for 64-bit integers. @@ -67,7 +70,6 @@ scanint8(const char *str, bool errorOK, int64 *result) if (*ptr == '-') { ptr++; - sign = -1; /* * Do an explicit check for INT64_MIN. Ugly though this is, it's @@ -75,12 +77,15 @@ scanint8(const char *str, bool errorOK, int64 *result) * portably. */ #ifndef INT64_IS_BUSTED - if (strcmp(ptr, "9223372036854775808") == 0) + if (strncmp(ptr, "9223372036854775808", 19) == 0) { - *result = -INT64CONST(0x7fffffffffffffff) - 1; - return true; + tmp = -INT64CONST(0x7fffffffffffffff) - 1; + ptr += 19; + goto gotdigits; } #endif + + sign = -1; } else if (*ptr == '+') ptr++; @@ -93,7 +98,8 @@ scanint8(const char *str, bool errorOK, int64 *result) else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("invalid input syntax for type bigint: \"%s\"", str))); + errmsg("invalid input syntax for integer: \"%s\"", + str))); } /* process digits */ @@ -108,11 +114,14 @@ scanint8(const char *str, bool errorOK, int64 *result) else ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("value \"%s\" is out of range for type bigint", + str))); } tmp = newtmp; } +gotdigits: + /* allow trailing whitespace, but not other trailing chars */ while (*ptr != '\0' && isspace((unsigned char) *ptr)) ptr++; @@ -124,7 +133,8 @@ scanint8(const char *str, bool errorOK, int64 *result) else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("invalid input syntax for type bigint: \"%s\"", str))); + errmsg("invalid input syntax for integer: \"%s\"", + str))); } *result = (sign < 0) ? -tmp : tmp; @@ -485,58 +495,118 @@ int28ge(PG_FUNCTION_ARGS) Datum int8um(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); + int64 result; - PG_RETURN_INT64(-val); + result = -arg; + /* overflow check (needed for INT64_MIN) */ + if (arg != 0 && SAMESIGN(result, arg)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int8up(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); - PG_RETURN_INT64(val); + PG_RETURN_INT64(arg); } Datum int8pl(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 + val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int8mi(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 - val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int8mul(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 * val2); + 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)) && + arg2 != 0 && + (result/arg2 != arg1 || (arg2 == -1 && arg1 < 0 && result < 0))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int8div(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - if (val2 == 0) + if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - PG_RETURN_INT64(val1 / val2); + result = arg1 / arg2; + /* + * Overflow check. The only possible overflow case is for + * arg1 = INT64_MIN, arg2 = -1, where the correct result is -INT64_MIN, + * which can't be represented on a two's-complement machine. + */ + if (arg2 == -1 && arg1 < 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } /* int8abs() @@ -546,8 +616,15 @@ Datum int8abs(PG_FUNCTION_ARGS) { int64 arg1 = PG_GETARG_INT64(0); + int64 result; - PG_RETURN_INT64((arg1 < 0) ? -arg1 : arg1); + result = (arg1 < 0) ? -arg1 : arg1; + /* overflow check (needed for INT64_MIN) */ + if (result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } /* int8mod() @@ -556,20 +633,16 @@ int8abs(PG_FUNCTION_ARGS) Datum int8mod(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); - int64 result; + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); - if (val2 == 0) + if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* No overflow is possible */ - result = val1 / val2; - result *= val2; - result = val1 - result; - - PG_RETURN_INT64(result); + PG_RETURN_INT64(arg1 % arg2); } @@ -577,18 +650,26 @@ Datum int8inc(PG_FUNCTION_ARGS) { int64 arg = PG_GETARG_INT64(0); + int64 result; - PG_RETURN_INT64(arg + 1); + result = arg + 1; + /* Overflow check */ + if (arg > 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + + PG_RETURN_INT64(result); } Datum int8larger(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); int64 result; - result = ((val1 > val2) ? val1 : val2); + result = ((arg1 > arg2) ? arg1 : arg2); PG_RETURN_INT64(result); } @@ -596,11 +677,11 @@ int8larger(PG_FUNCTION_ARGS) Datum int8smaller(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int64 val2 = PG_GETARG_INT64(1); + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); int64 result; - result = ((val1 < val2) ? val1 : val2); + result = ((arg1 < arg2) ? arg1 : arg2); PG_RETURN_INT64(result); } @@ -608,83 +689,172 @@ int8smaller(PG_FUNCTION_ARGS) Datum int84pl(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int32 val2 = PG_GETARG_INT32(1); + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; - PG_RETURN_INT64(val1 + val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int84mi(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int32 val2 = PG_GETARG_INT32(1); + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; - PG_RETURN_INT64(val1 - val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int84mul(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int32 val2 = PG_GETARG_INT32(1); + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; - PG_RETURN_INT64(val1 * val2); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int84div(PG_FUNCTION_ARGS) { - int64 val1 = PG_GETARG_INT64(0); - int32 val2 = PG_GETARG_INT32(1); + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; - if (val2 == 0) + if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - PG_RETURN_INT64(val1 / val2); + result = arg1 / arg2; + /* + * Overflow check. The only possible overflow case is for + * arg1 = INT64_MIN, arg2 = -1, where the correct result is -INT64_MIN, + * which can't be represented on a two's-complement machine. + */ + if (arg2 == -1 && arg1 < 0 && result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int48pl(PG_FUNCTION_ARGS) { - int32 val1 = PG_GETARG_INT32(0); - int64 val2 = PG_GETARG_INT64(1); + int32 arg1 = PG_GETARG_INT32(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 + val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int48mi(PG_FUNCTION_ARGS) { - int32 val1 = PG_GETARG_INT32(0); - int64 val2 = PG_GETARG_INT64(1); + int32 arg1 = PG_GETARG_INT32(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 - val2); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int48mul(PG_FUNCTION_ARGS) { - int32 val1 = PG_GETARG_INT32(0); - int64 val2 = PG_GETARG_INT64(1); + int32 arg1 = PG_GETARG_INT32(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; - PG_RETURN_INT64(val1 * val2); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); } Datum int48div(PG_FUNCTION_ARGS) { - int32 val1 = PG_GETARG_INT32(0); - int64 val2 = PG_GETARG_INT64(1); + int32 arg1 = PG_GETARG_INT32(0); + int64 arg2 = PG_GETARG_INT64(1); - if (val2 == 0) + if (arg2 == 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); - - PG_RETURN_INT64(val1 / val2); + /* No overflow is possible */ + PG_RETURN_INT64((int64) arg1 / arg2); } /* Binary arithmetics @@ -757,21 +927,21 @@ int8shr(PG_FUNCTION_ARGS) Datum int48(PG_FUNCTION_ARGS) { - int32 val = PG_GETARG_INT32(0); + int32 arg = PG_GETARG_INT32(0); - PG_RETURN_INT64((int64) val); + PG_RETURN_INT64((int64) arg); } Datum int84(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); int32 result; - result = (int32) val; + result = (int32) arg; /* Test for overflow by reverse-conversion. */ - if ((int64) result != val) + if ((int64) result != arg) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); @@ -782,24 +952,24 @@ int84(PG_FUNCTION_ARGS) Datum int28(PG_FUNCTION_ARGS) { - int16 val = PG_GETARG_INT16(0); + int16 arg = PG_GETARG_INT16(0); - PG_RETURN_INT64((int64) val); + PG_RETURN_INT64((int64) arg); } Datum int82(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); int16 result; - result = (int16) val; + result = (int16) arg; /* Test for overflow by reverse-conversion. */ - if ((int64) result != val) + if ((int64) result != arg) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); PG_RETURN_INT16(result); } @@ -807,10 +977,10 @@ int82(PG_FUNCTION_ARGS) Datum i8tod(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); float8 result; - result = val; + result = arg; PG_RETURN_FLOAT8(result); } @@ -821,23 +991,23 @@ i8tod(PG_FUNCTION_ARGS) Datum dtoi8(PG_FUNCTION_ARGS) { - float8 val = PG_GETARG_FLOAT8(0); + float8 arg = PG_GETARG_FLOAT8(0); int64 result; - /* Round val to nearest integer (but it's still in float form) */ - val = rint(val); + /* 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) val; + result = (int64) arg; - if ((float8) result != val) + if ((float8) result != arg) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("bigint out of range"))); PG_RETURN_INT64(result); } @@ -845,10 +1015,10 @@ dtoi8(PG_FUNCTION_ARGS) Datum i8tof(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); float4 result; - result = val; + result = arg; PG_RETURN_FLOAT4(result); } @@ -859,24 +1029,24 @@ i8tof(PG_FUNCTION_ARGS) Datum ftoi8(PG_FUNCTION_ARGS) { - float4 val = PG_GETARG_FLOAT4(0); + float4 arg = PG_GETARG_FLOAT4(0); int64 result; - float8 dval; + float8 darg; - /* Round val to nearest integer (but it's still in float form) */ - dval = rint(val); + /* 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) dval; + result = (int64) darg; - if ((float8) result != dval) + if ((float8) result != darg) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("bigint out of range"))); PG_RETURN_INT64(result); } @@ -884,13 +1054,13 @@ ftoi8(PG_FUNCTION_ARGS) Datum i8tooid(PG_FUNCTION_ARGS) { - int64 val = PG_GETARG_INT64(0); + int64 arg = PG_GETARG_INT64(0); Oid result; - result = (Oid) val; + result = (Oid) arg; /* Test for overflow by reverse-conversion. */ - if ((int64) result != val) + if ((int64) result != arg) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("OID out of range"))); @@ -901,9 +1071,9 @@ i8tooid(PG_FUNCTION_ARGS) Datum oidtoi8(PG_FUNCTION_ARGS) { - Oid val = PG_GETARG_OID(0); + Oid arg = PG_GETARG_OID(0); - PG_RETURN_INT64((int64) val); + PG_RETURN_INT64((int64) arg); } Datum @@ -929,13 +1099,13 @@ text_int8(PG_FUNCTION_ARGS) Datum int8_text(PG_FUNCTION_ARGS) { - /* val is int64, but easier to leave it as Datum */ - Datum val = PG_GETARG_DATUM(0); + /* arg is int64, but easier to leave it as Datum */ + Datum arg = PG_GETARG_DATUM(0); char *s; int len; text *result; - s = DatumGetCString(DirectFunctionCall1(int8out, val)); + s = DatumGetCString(DirectFunctionCall1(int8out, arg)); len = strlen(s); result = (text *) palloc(VARHDRSZ + len); diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 9c8abfb365a..f99fb897153 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -14,7 +14,7 @@ * Copyright (c) 1998-2004, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.79 2004/08/30 02:54:39 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.80 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1826,7 +1826,7 @@ numeric_int8(PG_FUNCTION_ARGS) if (NUMERIC_IS_NAN(num)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot convert NaN to integer"))); + errmsg("cannot convert NaN to bigint"))); /* Convert to variable format and thence to int8 */ init_var(&x); @@ -1835,7 +1835,7 @@ numeric_int8(PG_FUNCTION_ARGS) if (!numericvar_to_int8(&x, &result)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("bigint out of range"))); free_var(&x); @@ -1874,7 +1874,7 @@ numeric_int2(PG_FUNCTION_ARGS) if (NUMERIC_IS_NAN(num)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot convert NaN to integer"))); + errmsg("cannot convert NaN to smallint"))); /* Convert to variable format and thence to int8 */ init_var(&x); @@ -1883,7 +1883,7 @@ numeric_int2(PG_FUNCTION_ARGS) if (!numericvar_to_int8(&x, &val)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); free_var(&x); @@ -1894,7 +1894,7 @@ numeric_int2(PG_FUNCTION_ARGS) if ((int64) result != val) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("smallint out of range"))); PG_RETURN_INT16(result); } diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 100f38d593f..a4d18417c22 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numutils.c,v 1.65 2004/08/29 05:06:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numutils.c,v 1.66 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -114,7 +114,7 @@ pg_atoi(char *s, int size, int c) if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("value \"%s\" is out of range for type shortint", s))); + errmsg("value \"%s\" is out of range for type smallint", s))); break; case sizeof(int8): if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX) diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index b9f6a296b23..e65614a4aa4 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.42 2004/08/29 05:06:49 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.43 2004/10/04 14:42:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1310,7 +1310,7 @@ bittoint8(PG_FUNCTION_ARGS) if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("integer out of range"))); + errmsg("bigint out of range"))); result = 0; for (r = VARBITS(arg); r < VARBITEND(arg); r++) |