X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fadt%2Fint8.c;h=6707b79e5485cc65b86ad7f29e57cebd34937b3a;hb=901be0fad4034c9cf8a3588fd6cf2ece82e4b8ce;hp=e58c94268ad3fc4d36c0d492c53014194a085f80;hpb=b6b71b85bc45b49005b5aec87cba2c33fc8baf49;p=postgresql diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index e58c94268a..6707b79e54 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -3,11 +3,11 @@ * int8.c * Internal 64-bit integer operations * - * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * 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.77 2010/01/07 04:53:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,11 +19,14 @@ #include "funcapi.h" #include "libpq/pqformat.h" +#include "nodes/nodes.h" #include "utils/int8.h" #define MAXINT8LEN 25 +#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) + typedef struct { int64 current; @@ -31,6 +34,7 @@ typedef struct int64 step; } generate_series_fctx; + /*********************************************************************** ** ** Routines for 64-bit integers. @@ -55,8 +59,8 @@ scanint8(const char *str, bool errorOK, int64 *result) int sign = 1; /* - * Do our own scan, rather than relying on sscanf which might be - * broken for long long. + * Do our own scan, rather than relying on sscanf which might be broken + * for long long. */ /* skip leading spaces */ @@ -67,20 +71,18 @@ 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 - * cleaner than trying to get the loop below to handle it - * portably. + * cleaner than trying to get the loop below to handle it 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 +95,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 +111,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 +130,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 +492,124 @@ 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; + + result = arg1 - arg2; - PG_RETURN_INT64(val1 - val2); + /* + * 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)) + { + if (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. Most machines + * produce INT64_MIN but it seems some produce zero. + */ + 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 +619,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,39 +636,95 @@ 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); } Datum int8inc(PG_FUNCTION_ARGS) { - int64 arg = PG_GETARG_INT64(0); + /* + * When int8 is pass-by-reference, we provide this special case to avoid + * palloc overhead for COUNT(): when called from nodeAgg, we know that the + * argument is modifiable local storage, so just update it in-place. (If + * int8 is pass-by-value, then of course this is useless as well as + * incorrect, so just ifdef it out.) + */ +#ifndef USE_FLOAT8_BYVAL /* controls int8 too */ + if (fcinfo->context && + (IsA(fcinfo->context, AggState) || + IsA(fcinfo->context, WindowAggState))) + { + int64 *arg = (int64 *) PG_GETARG_POINTER(0); + int64 result; + + result = *arg + 1; + /* Overflow check */ + if (result < 0 && *arg > 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + + *arg = result; + PG_RETURN_POINTER(arg); + } + else +#endif + { + /* Not called by nodeAgg, so just do it the dumb way */ + int64 arg = PG_GETARG_INT64(0); + int64 result; - PG_RETURN_INT64(arg + 1); + result = arg + 1; + /* Overflow check */ + if (result < 0 && arg > 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + + PG_RETURN_INT64(result); + } +} + +/* + * These functions are exactly like int8inc but are used for aggregates that + * count only non-null values. Since the functions are declared strict, + * the null checks happen before we ever get here, and all we need do is + * increment the state value. We could actually make these pg_proc entries + * point right at int8inc, but then the opr_sanity regression test would + * complain about mismatched entries for a built-in function. + */ + +Datum +int8inc_any(PG_FUNCTION_ARGS) +{ + return int8inc(fcinfo); } +Datum +int8inc_float8_float8(PG_FUNCTION_ARGS) +{ + return int8inc(fcinfo); +} + + 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 +732,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 +744,369 @@ 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; + + result = arg1 - arg2; - PG_RETURN_INT64(val1 - val2); + /* + * 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. Most machines + * produce INT64_MIN but it seems some produce zero. + */ + 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; + + result = arg1 + arg2; - PG_RETURN_INT64(val1 + val2); + /* + * 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; + + result = arg1 - arg2; - PG_RETURN_INT64(val1 - val2); + /* + * 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"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* No overflow is possible */ + PG_RETURN_INT64((int64) arg1 / arg2); +} + +Datum +int82pl(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int16 arg2 = PG_GETARG_INT16(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 +int82mi(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); +} + +Datum +int82mul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); +} + +Datum +int82div(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int16 arg2 = PG_GETARG_INT16(1); + int64 result; + + if (arg2 == 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + + 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. Most machines + * produce INT64_MIN but it seems some produce zero. + */ + 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 +int28pl(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); +} + +Datum +int28mi(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); +} + +Datum +int28mul(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + 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) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(result); +} + +Datum +int28div(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + int64 arg2 = PG_GETARG_INT64(1); + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* No overflow is possible */ + PG_RETURN_INT64((int64) arg1 / arg2); } /* Binary arithmetics @@ -757,21 +1179,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 +1204,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 +1229,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 +1243,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. + * 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 +1267,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 +1281,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. + * 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 +1306,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,51 +1323,9 @@ i8tooid(PG_FUNCTION_ARGS) Datum oidtoi8(PG_FUNCTION_ARGS) { - Oid val = PG_GETARG_OID(0); - - PG_RETURN_INT64((int64) val); -} - -Datum -text_int8(PG_FUNCTION_ARGS) -{ - text *str = PG_GETARG_TEXT_P(0); - int len; - char *s; - Datum result; - - len = (VARSIZE(str) - VARHDRSZ); - s = palloc(len + 1); - memcpy(s, VARDATA(str), len); - *(s + len) = '\0'; - - result = DirectFunctionCall1(int8in, CStringGetDatum(s)); - - pfree(s); - - return result; -} - -Datum -int8_text(PG_FUNCTION_ARGS) -{ - /* val is int64, but easier to leave it as Datum */ - Datum val = PG_GETARG_DATUM(0); - char *s; - int len; - text *result; - - s = DatumGetCString(DirectFunctionCall1(int8out, val)); - len = strlen(s); - - result = (text *) palloc(VARHDRSZ + len); - - VARATT_SIZEP(result) = len + VARHDRSZ; - memcpy(VARDATA(result), s, len); - - pfree(s); + Oid arg = PG_GETARG_OID(0); - PG_RETURN_TEXT_P(result); + PG_RETURN_INT64((int64) arg); } /* @@ -978,14 +1358,13 @@ generate_series_step_int8(PG_FUNCTION_ARGS) if (step == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("step size may not equal zero"))); + errmsg("step size cannot equal zero"))); /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* - * switch to memory context appropriate for multiple function - * calls + * switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1008,8 +1387,7 @@ generate_series_step_int8(PG_FUNCTION_ARGS) funcctx = SRF_PERCALL_SETUP(); /* - * get the saved state and use current as the result for this - * iteration + * get the saved state and use current as the result for this iteration */ fctx = funcctx->user_fctx; result = fctx->current;