]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/int.c
Update copyright for 2014
[postgresql] / src / backend / utils / adt / int.c
index 73687d55ec93d07b6da94d8120017b31cb354779..669355e4540a891ab8cfdc79326f1fe6bbe0f163 100644 (file)
@@ -3,43 +3,52 @@
  * 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                                                                                                               *
@@ -70,32 +79,94 @@ int2out(PG_FUNCTION_ARGS)
 }
 
 /*
- *             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);
 }
@@ -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')
                        ;
        }
@@ -131,6 +197,63 @@ int2vectorout(PG_FUNCTION_ARGS)
        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.
@@ -138,10 +261,12 @@ int2vectorout(PG_FUNCTION_ARGS)
 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);
 }
 
 
@@ -173,97 +298,79 @@ int4out(PG_FUNCTION_ARGS)
        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
@@ -506,8 +613,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
@@ -523,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
@@ -532,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
@@ -541,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
@@ -550,27 +710,70 @@ int4div(PG_FUNCTION_ARGS)
 {
        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
@@ -586,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
@@ -595,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
@@ -604,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;
+
+       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
@@ -613,11 +852,39 @@ int2div(PG_FUNCTION_ARGS)
 {
        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
@@ -625,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
@@ -634,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
@@ -643,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
@@ -654,9 +963,16 @@ int24div(PG_FUNCTION_ARGS)
        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
@@ -664,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
@@ -673,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
@@ -682,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
@@ -691,95 +1049,98 @@ int42div(PG_FUNCTION_ARGS)
 {
        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
@@ -788,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
@@ -836,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
@@ -952,3 +1328,85 @@ 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);
+}