X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fadt%2Fint.c;h=669355e4540a891ab8cfdc79326f1fe6bbe0f163;hb=7e04792a1cbd1763edf72474f6b1fbad2cd0ad31;hp=189cd384162996947e2cbc0033a6d2b732de7a10;hpb=623bf843d201438db1590906b4c39faf9d2e1c2a;p=postgresql diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 189cd38416..669355e454 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -3,43 +3,52 @@ * int.c * Functions for the built-in integer types (except int8). * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.45 2001/01/24 19:43:14 momjian Exp $ + * src/backend/utils/adt/int.c * *------------------------------------------------------------------------- */ /* * OLD COMMENTS * I/O routines: - * int2in, int2out, int2vectorin, int2vectorout, int4in, int4out - * Conversion routines: - * itoi, int2_text, int4_text + * int2in, int2out, int2recv, int2send + * int4in, int4out, int4recv, int4send + * int2vectorin, int2vectorout, int2vectorrecv, int2vectorsend * Boolean operators: * inteq, intne, intlt, intle, intgt, intge * Arithmetic operators: * intpl, intmi, int4mul, intdiv * * Arithmetic operators: - * intmod, int4fac + * intmod */ - #include "postgres.h" #include #include +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/builtins.h" -#ifndef SHRT_MAX -#define SHRT_MAX (0x7FFF) -#endif -#ifndef SHRT_MIN -#define SHRT_MIN (-0x8000) -#endif + +#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) + +#define Int2VectorSize(n) (offsetof(int2vector, values) + (n) * sizeof(int16)) + +typedef struct +{ + int32 current; + int32 finish; + int32 step; +} generate_series_fctx; + /***************************************************************************** * USER I/O ROUTINES * @@ -63,39 +72,101 @@ Datum int2out(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); - char *result = (char *) palloc(7); /* sign, 5 digits, '\0' */ + char *result = (char *) palloc(7); /* sign, 5 digits, '\0' */ pg_itoa(arg1, result); PG_RETURN_CSTRING(result); } /* - * int2vectorin - converts "num num ..." to internal form + * int2recv - converts external binary format to int2 + */ +Datum +int2recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + + PG_RETURN_INT16((int16) pq_getmsgint(buf, sizeof(int16))); +} + +/* + * int2send - converts int2 to binary format + */ +Datum +int2send(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendint(&buf, arg1, sizeof(int16)); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/* + * construct int2vector given a raw array of int2s * - * Note: Fills any missing slots with zeroes. + * If int2s is NULL then caller must fill values[] afterward + */ +int2vector * +buildint2vector(const int16 *int2s, int n) +{ + int2vector *result; + + result = (int2vector *) palloc0(Int2VectorSize(n)); + + if (n > 0 && int2s) + memcpy(result->values, int2s, n * sizeof(int16)); + + /* + * Attach standard array header. For historical reasons, we set the index + * lower bound to 0 not 1. + */ + SET_VARSIZE(result, Int2VectorSize(n)); + result->ndim = 1; + result->dataoffset = 0; /* never any nulls */ + result->elemtype = INT2OID; + result->dim1 = n; + result->lbound1 = 0; + + return result; +} + +/* + * int2vectorin - converts "num num ..." to internal form */ Datum int2vectorin(PG_FUNCTION_ARGS) { char *intString = PG_GETARG_CSTRING(0); - int16 *result = (int16 *) palloc(sizeof(int16[INDEX_MAX_KEYS])); - int slot; + int2vector *result; + int n; - for (slot = 0; *intString && slot < INDEX_MAX_KEYS; slot++) + result = (int2vector *) palloc0(Int2VectorSize(FUNC_MAX_ARGS)); + + for (n = 0; *intString && n < FUNC_MAX_ARGS; n++) { - if (sscanf(intString, "%hd", &result[slot]) != 1) - break; while (*intString && isspace((unsigned char) *intString)) intString++; + if (*intString == '\0') + break; + result->values[n] = pg_atoi(intString, sizeof(int16), ' '); while (*intString && !isspace((unsigned char) *intString)) intString++; } while (*intString && isspace((unsigned char) *intString)) intString++; if (*intString) - elog(ERROR, "int2vector value has too many values"); - while (slot < INDEX_MAX_KEYS) - result[slot++] = 0; + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("int2vector has too many elements"))); + + SET_VARSIZE(result, Int2VectorSize(n)); + result->ndim = 1; + result->dataoffset = 0; /* never any nulls */ + result->elemtype = INT2OID; + result->dim1 = n; + result->lbound1 = 0; PG_RETURN_POINTER(result); } @@ -106,24 +177,19 @@ int2vectorin(PG_FUNCTION_ARGS) Datum int2vectorout(PG_FUNCTION_ARGS) { - int16 *int2Array = (int16 *) PG_GETARG_POINTER(0); + int2vector *int2Array = (int2vector *) PG_GETARG_POINTER(0); int num, - maxnum; + nnums = int2Array->dim1; char *rp; char *result; - /* find last non-zero value in vector */ - for (maxnum = INDEX_MAX_KEYS - 1; maxnum >= 0; maxnum--) - if (int2Array[maxnum] != 0) - break; - /* assumes sign, 5 digits, ' ' */ - rp = result = (char *) palloc((maxnum + 1) * 7 + 1); - for (num = 0; num <= maxnum; num++) + rp = result = (char *) palloc(nnums * 7 + 1); + for (num = 0; num < nnums; num++) { if (num != 0) *rp++ = ' '; - pg_itoa(int2Array[num], rp); + pg_itoa(int2Array->values[num], rp); while (*++rp != '\0') ; } @@ -132,68 +198,75 @@ int2vectorout(PG_FUNCTION_ARGS) } /* - * We don't have a complete set of int2vector support routines, - * but we need int2vectoreq for catcache indexing. + * int2vectorrecv - converts external binary format to int2vector */ Datum -int2vectoreq(PG_FUNCTION_ARGS) -{ - int16 *arg1 = (int16 *) PG_GETARG_POINTER(0); - int16 *arg2 = (int16 *) PG_GETARG_POINTER(1); +int2vectorrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + FunctionCallInfoData locfcinfo; + int2vector *result; + + /* + * Normally one would call array_recv() using DirectFunctionCall3, but + * that does not work since array_recv wants to cache some data using + * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo + * parameter. + */ + InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3, + InvalidOid, NULL, NULL); + + locfcinfo.arg[0] = PointerGetDatum(buf); + locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID); + locfcinfo.arg[2] = Int32GetDatum(-1); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + locfcinfo.argnull[2] = false; + + result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo)); + + Assert(!locfcinfo.isnull); + + /* sanity checks: int2vector must be 1-D, 0-based, no nulls */ + if (ARR_NDIM(result) != 1 || + ARR_HASNULL(result) || + ARR_ELEMTYPE(result) != INT2OID || + ARR_LBOUND(result)[0] != 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid int2vector data"))); + + /* check length for consistency with int2vectorin() */ + if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("oidvector has too many elements"))); - PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(int16)) == 0); + PG_RETURN_POINTER(result); } /* - * Type int44 has no real-world use, but the regression tests use it. - * It's a four-element vector of int4's. - */ - -/* - * int44in - converts "num num ..." to internal form - * - * Note: Fills any missing positions with zeroes. + * int2vectorsend - converts int2vector to binary format */ Datum -int44in(PG_FUNCTION_ARGS) +int2vectorsend(PG_FUNCTION_ARGS) { - char *input_string = PG_GETARG_CSTRING(0); - int32 *result = (int32 *) palloc(4 * sizeof(int32)); - int i; - - i = sscanf(input_string, - "%d, %d, %d, %d", - &result[0], - &result[1], - &result[2], - &result[3]); - while (i < 4) - result[i++] = 0; - - PG_RETURN_POINTER(result); + return array_send(fcinfo); } /* - * int44out - converts internal form to "num num ..." + * We don't have a complete set of int2vector support routines, + * but we need int2vectoreq for catcache indexing. */ Datum -int44out(PG_FUNCTION_ARGS) +int2vectoreq(PG_FUNCTION_ARGS) { - int32 *an_array = (int32 *) PG_GETARG_POINTER(0); - char *result = (char *) palloc(16 * 4); /* Allow 14 digits + sign */ - int i; - char *walk; + int2vector *a = (int2vector *) PG_GETARG_POINTER(0); + int2vector *b = (int2vector *) PG_GETARG_POINTER(1); - walk = result; - for (i = 0; i < 4; i++) - { - pg_ltoa(an_array[i], walk); - while (*++walk != '\0') - ; - *walk++ = ' '; - } - *--walk = '\0'; - PG_RETURN_CSTRING(result); + if (a->dim1 != b->dim1) + PG_RETURN_BOOL(false); + PG_RETURN_BOOL(memcmp(a->values, b->values, a->dim1 * sizeof(int16)) == 0); } @@ -219,103 +292,85 @@ Datum int4out(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); - char *result = (char *) palloc(12); /* sign, 10 digits, '\0' */ + char *result = (char *) palloc(12); /* sign, 10 digits, '\0' */ pg_ltoa(arg1, result); PG_RETURN_CSTRING(result); } - /* - * =================== - * CONVERSION ROUTINES - * =================== + * int4recv - converts external binary format to int4 */ - Datum -i2toi4(PG_FUNCTION_ARGS) +int4recv(PG_FUNCTION_ARGS) { - int16 arg1 = PG_GETARG_INT16(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); - PG_RETURN_INT32((int32) arg1); + PG_RETURN_INT32((int32) pq_getmsgint(buf, sizeof(int32))); } +/* + * int4send - converts int4 to binary format + */ Datum -i4toi2(PG_FUNCTION_ARGS) +int4send(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); + StringInfoData buf; - if (arg1 < SHRT_MIN) - elog(ERROR, "i4toi2: '%d' causes int2 underflow", arg1); - if (arg1 > SHRT_MAX) - elog(ERROR, "i4toi2: '%d' causes int2 overflow", arg1); - - PG_RETURN_INT16((int16) arg1); + pq_begintypsend(&buf); + pq_sendint(&buf, arg1, sizeof(int32)); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } + +/* + * =================== + * CONVERSION ROUTINES + * =================== + */ + Datum -int2_text(PG_FUNCTION_ARGS) +i2toi4(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); - text *result = (text *) palloc(7+VARHDRSZ); /* sign,5 digits, '\0' */ - pg_itoa(arg1, VARDATA(result)); - VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ; - PG_RETURN_TEXT_P(result); + PG_RETURN_INT32((int32) arg1); } Datum -text_int2(PG_FUNCTION_ARGS) +i4toi2(PG_FUNCTION_ARGS) { - text *string = PG_GETARG_TEXT_P(0); - Datum result; - int len; - char *str; - - len = VARSIZE(string) - VARHDRSZ; - - str = palloc(len + 1); - memcpy(str, VARDATA(string), len); - *(str + len) = '\0'; + int32 arg1 = PG_GETARG_INT32(0); - result = DirectFunctionCall1(int2in, CStringGetDatum(str)); - pfree(str); + if (arg1 < SHRT_MIN || arg1 > SHRT_MAX) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); - return result; + PG_RETURN_INT16((int16) arg1); } +/* Cast int4 -> bool */ Datum -int4_text(PG_FUNCTION_ARGS) +int4_bool(PG_FUNCTION_ARGS) { - int32 arg1 = PG_GETARG_INT32(0); - text *result = (text *) palloc(12+VARHDRSZ); /* sign,10 digits,'\0' */ - - pg_ltoa(arg1, VARDATA(result)); - VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ; - PG_RETURN_TEXT_P(result); + if (PG_GETARG_INT32(0) == 0) + PG_RETURN_BOOL(false); + else + PG_RETURN_BOOL(true); } +/* Cast bool -> int4 */ Datum -text_int4(PG_FUNCTION_ARGS) +bool_int4(PG_FUNCTION_ARGS) { - text *string = PG_GETARG_TEXT_P(0); - Datum result; - int len; - char *str; - - len = VARSIZE(string) - VARHDRSZ; - - str = palloc(len + 1); - memcpy(str, VARDATA(string), len); - *(str + len) = '\0'; - - result = DirectFunctionCall1(int4in, CStringGetDatum(str)); - pfree(str); - - return result; + if (PG_GETARG_BOOL(0) == false) + PG_RETURN_INT32(0); + else + PG_RETURN_INT32(1); } - /* * ============================ * COMPARISON OPERATOR ROUTINES @@ -556,10 +611,25 @@ int42ge(PG_FUNCTION_ARGS) 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)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); +} + +Datum +int4up(PG_FUNCTION_ARGS) { int32 arg = PG_GETARG_INT32(0); - PG_RETURN_INT32(-arg); + PG_RETURN_INT32(arg); } Datum @@ -567,8 +637,20 @@ 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 @@ -576,8 +658,20 @@ 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 @@ -585,8 +679,30 @@ 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 && + ((arg2 == -1 && arg1 < 0 && result < 0) || + result / arg2 != arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); } Datum @@ -594,24 +710,78 @@ 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"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * INT_MIN / -1 is problematic, since the result can't be represented on a + * two's-complement machine. Some machines produce INT_MIN, some produce + * zero, some throw an exception. We can dodge the problem by recognizing + * that division by -1 is the same as negation. + */ + if (arg2 == -1) + { + result = -arg1; + /* overflow check (needed for INT_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; - PG_RETURN_INT32(arg1 / arg2); + 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 +int2up(PG_FUNCTION_ARGS) +{ + int16 arg = PG_GETARG_INT16(0); + + PG_RETURN_INT16(arg); } Datum @@ -619,8 +789,20 @@ int2pl(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); + int16 result; + + result = arg1 + arg2; - PG_RETURN_INT16(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 @@ -628,8 +810,20 @@ 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 @@ -637,8 +831,20 @@ 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; - PG_RETURN_INT16(arg1 * 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 @@ -646,8 +852,39 @@ 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"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * SHRT_MIN / -1 is problematic, since the result can't be represented on + * a two's-complement machine. Some machines produce SHRT_MIN, some + * produce zero, some throw an exception. We can dodge the problem by + * recognizing that division by -1 is the same as negation. + */ + if (arg2 == -1) + { + result = -arg1; + /* overflow check (needed for SHRT_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + PG_RETURN_INT16(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; - PG_RETURN_INT16(arg1 / arg2); + PG_RETURN_INT16(result); } Datum @@ -655,8 +892,20 @@ 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 @@ -664,8 +913,20 @@ 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 @@ -673,8 +934,26 @@ 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 @@ -683,7 +962,17 @@ int24div(PG_FUNCTION_ARGS) int16 arg1 = PG_GETARG_INT16(0); int32 arg2 = PG_GETARG_INT32(1); - PG_RETURN_INT32(arg1 / arg2); + 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_INT32((int32) arg1 / arg2); } Datum @@ -691,8 +980,20 @@ 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 @@ -700,8 +1001,20 @@ 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 @@ -709,8 +1022,26 @@ 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 @@ -718,8 +1049,39 @@ int42div(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); int16 arg2 = PG_GETARG_INT16(1); + int32 result; - PG_RETURN_INT32(arg1 / arg2); + 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(); + } + + /* + * INT_MIN / -1 is problematic, since the result can't be represented on a + * two's-complement machine. Some machines produce INT_MIN, some produce + * zero, some throw an exception. We can dodge the problem by recognizing + * that division by -1 is the same as negation. + */ + if (arg2 == -1) + { + result = -arg1; + /* overflow check (needed for INT_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + PG_RETURN_INT32(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; + + PG_RETURN_INT32(result); } Datum @@ -728,67 +1090,58 @@ int4mod(PG_FUNCTION_ARGS) int32 arg1 = PG_GETARG_INT32(0); int32 arg2 = PG_GETARG_INT32(1); - PG_RETURN_INT32(arg1 % arg2); -} - -Datum -int2mod(PG_FUNCTION_ARGS) -{ - int16 arg1 = PG_GETARG_INT16(0); - int16 arg2 = PG_GETARG_INT16(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(); + } - PG_RETURN_INT16(arg1 % arg2); -} + /* + * Some machines throw a floating-point exception for INT_MIN % -1, which + * is a bit silly since the correct answer is perfectly well-defined, + * namely zero. + */ + if (arg2 == -1) + PG_RETURN_INT32(0); -Datum -int24mod(PG_FUNCTION_ARGS) -{ - int16 arg1 = PG_GETARG_INT16(0); - int32 arg2 = PG_GETARG_INT32(1); + /* No overflow is possible */ PG_RETURN_INT32(arg1 % arg2); } Datum -int42mod(PG_FUNCTION_ARGS) +int2mod(PG_FUNCTION_ARGS) { - int32 arg1 = PG_GETARG_INT32(0); + int16 arg1 = PG_GETARG_INT16(0); int16 arg2 = PG_GETARG_INT16(1); - PG_RETURN_INT32(arg1 % arg2); -} - -/* int[24]fac() - * Factorial - */ -Datum -int4fac(PG_FUNCTION_ARGS) -{ - int32 arg1 = PG_GETARG_INT32(0); - int32 result; + 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(); + } - if (arg1 < 1) - result = 0; - else - for (result = 1; arg1 > 0; --arg1) - result *= arg1; - PG_RETURN_INT32(result); -} + /* + * Some machines throw a floating-point exception for INT_MIN % -1, which + * is a bit silly since the correct answer is perfectly well-defined, + * namely zero. (It's not clear this ever happens when dealing with + * int16, but we might as well have the test for safety.) + */ + if (arg2 == -1) + PG_RETURN_INT16(0); -Datum -int2fac(PG_FUNCTION_ARGS) -{ - int16 arg1 = PG_GETARG_INT16(0); - int32 result; + /* No overflow is possible */ - if (arg1 < 1) - result = 0; - else - for (result = 1; arg1 > 0; --arg1) - result *= arg1; - PG_RETURN_INT32(result); + PG_RETURN_INT16(arg1 % arg2); } + /* int[24]abs() * Absolute value */ @@ -796,16 +1149,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 @@ -844,7 +1211,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 @@ -961,3 +1329,84 @@ int2shr(PG_FUNCTION_ARGS) PG_RETURN_INT16(arg1 >> arg2); } +/* + * non-persistent numeric series generator + */ +Datum +generate_series_int4(PG_FUNCTION_ARGS) +{ + return generate_series_step_int4(fcinfo); +} + +Datum +generate_series_step_int4(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + generate_series_fctx *fctx; + int32 result; + MemoryContext oldcontext; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + int32 start = PG_GETARG_INT32(0); + int32 finish = PG_GETARG_INT32(1); + int32 step = 1; + + /* see if we were given an explicit step size */ + if (PG_NARGS() == 3) + step = PG_GETARG_INT32(2); + if (step == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + 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 + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (generate_series_fctx *) palloc(sizeof(generate_series_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value + */ + fctx->current = start; + fctx->finish = finish; + fctx->step = step; + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* + * get the saved state and use current as the result for this iteration + */ + fctx = funcctx->user_fctx; + result = fctx->current; + + 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)) + fctx->step = 0; + + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, Int32GetDatum(result)); + } + else + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); +}