* int.c
* Functions for the built-in integer types (except int8).
*
- * Portions Copyright (c) 1996-2002, 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.53 2003/03/11 21:01:33 tgl 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 <ctype.h>
#include <limits.h>
+#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 *
}
/*
- * 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;
+
+ result = (int2vector *) palloc0(Int2VectorSize(FUNC_MAX_ARGS));
- for (slot = 0; *intString && slot < INDEX_MAX_KEYS; slot++)
+ 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);
}
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')
;
}
PG_RETURN_CSTRING(result);
}
+/*
+ * int2vectorrecv - converts external binary format to int2vector
+ */
+Datum
+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_POINTER(result);
+}
+
+/*
+ * int2vectorsend - converts int2vector to binary format
+ */
+Datum
+int2vectorsend(PG_FUNCTION_ARGS)
+{
+ return array_send(fcinfo);
+}
+
/*
* We don't have a complete set of int2vector support routines,
* but we need int2vectoreq for catcache indexing.
Datum
int2vectoreq(PG_FUNCTION_ARGS)
{
- int16 *arg1 = (int16 *) PG_GETARG_POINTER(0);
- int16 *arg2 = (int16 *) PG_GETARG_POINTER(1);
+ int2vector *a = (int2vector *) PG_GETARG_POINTER(0);
+ int2vector *b = (int2vector *) PG_GETARG_POINTER(1);
- PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(int16)) == 0);
+ if (a->dim1 != b->dim1)
+ PG_RETURN_BOOL(false);
+ PG_RETURN_BOOL(memcmp(a->values, b->values, a->dim1 * sizeof(int16)) == 0);
}
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
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
{
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
{
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
{
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
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
if (arg2 == 0)
- elog(ERROR, "division by zero");
+ {
+ 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_INT32(arg1 / arg2);
+ /*
+ * 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
int4inc(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
+ int32 result;
+
+ 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(arg + 1);
+ 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
{
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
{
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
{
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;
+
+ if (result32 < SHRT_MIN || result32 > SHRT_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
- PG_RETURN_INT16(arg1 * arg2);
+ PG_RETURN_INT16((int16) result32);
}
Datum
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int16 result;
if (arg2 == 0)
- elog(ERROR, "division by zero");
+ {
+ 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
{
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
{
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
{
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
int32 arg2 = PG_GETARG_INT32(1);
if (arg2 == 0)
- elog(ERROR, "division by zero");
+ {
+ 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_INT32(arg1 / arg2);
+ /* No overflow is possible */
+ PG_RETURN_INT32((int32) arg1 / arg2);
}
Datum
{
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
{
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
{
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
{
int32 arg1 = PG_GETARG_INT32(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result;
if (arg2 == 0)
- elog(ERROR, "division by zero");
+ {
+ 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_INT32(arg1 / arg2);
-}
+ /*
+ * 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);
+ }
-Datum
-int4mod(PG_FUNCTION_ARGS)
-{
- int32 arg1 = PG_GETARG_INT32(0);
- int32 arg2 = PG_GETARG_INT32(1);
+ /* No overflow is possible */
- if (arg2 == 0)
- elog(ERROR, "division by zero");
+ result = arg1 / arg2;
- PG_RETURN_INT32(arg1 % arg2);
+ PG_RETURN_INT32(result);
}
Datum
-int2mod(PG_FUNCTION_ARGS)
+int4mod(PG_FUNCTION_ARGS)
{
- int16 arg1 = PG_GETARG_INT16(0);
- int16 arg2 = PG_GETARG_INT16(1);
+ int32 arg1 = PG_GETARG_INT32(0);
+ int32 arg2 = PG_GETARG_INT32(1);
if (arg2 == 0)
- elog(ERROR, "division by zero");
-
- PG_RETURN_INT16(arg1 % arg2);
-}
+ {
+ 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();
+ }
-Datum
-int24mod(PG_FUNCTION_ARGS)
-{
- int16 arg1 = PG_GETARG_INT16(0);
- int32 arg2 = PG_GETARG_INT32(1);
+ /*
+ * 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);
- if (arg2 == 0)
- elog(ERROR, "division by zero");
+ /* 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);
if (arg2 == 0)
- elog(ERROR, "division by zero");
+ {
+ 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_INT32(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. (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);
-/* int[24]fac()
- * Factorial
- */
-Datum
-int4fac(PG_FUNCTION_ARGS)
-{
- int32 arg1 = PG_GETARG_INT32(0);
- int32 result;
+ /* No overflow is possible */
- if (arg1 == 0)
- result = 1;
- else if (arg1 < 0)
- result = 0;
- else
- for (result = 1; arg1 > 0; --arg1)
- result *= arg1;
- PG_RETURN_INT32(result);
+ PG_RETURN_INT16(arg1 % arg2);
}
-Datum
-int2fac(PG_FUNCTION_ARGS)
-{
- int16 arg1 = PG_GETARG_INT16(0);
- int32 result;
-
- if (arg1 == 0)
- result = 1;
- else if (arg1 < 0)
- result = 0;
- else
- for (result = 1; arg1 > 0; --arg1)
- result *= arg1;
- PG_RETURN_INT32(result);
-}
/* int[24]abs()
* Absolute value
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
PG_RETURN_INT32((arg1 < arg2) ? arg1 : arg2);
}
-/* Binary arithmetics
+/*
+ * Bit-pushing operators
*
* int[24]and - returns arg1 & arg2
* int[24]or - returns arg1 | arg2
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);
+}