aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/int.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-10-04 14:42:48 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-10-04 14:42:48 +0000
commit4171bb869f234281a13bb862d3b1e577bf336242 (patch)
treee8193f7be04ddd942f13811ef9bbe0494d24920d /src/backend/utils/adt/int.c
parent24201b4bc60e46e8de031fb5911af32bdb412d43 (diff)
downloadpostgresql-4171bb869f234281a13bb862d3b1e577bf336242.tar.gz
postgresql-4171bb869f234281a13bb862d3b1e577bf336242.zip
Detect overflow in integer arithmetic operators (integer, smallint, and
bigint variants). Clean up some inconsistencies in error message wording. Fix scanint8 to allow trailing whitespace in INT64_MIN case. Update int8-exp-three-digits.out, which seems to have been ignored by the last couple of people to modify the int8 regression test, and remove int8-exp-three-digits-win32.out which is thereby exposed as redundant.
Diffstat (limited to 'src/backend/utils/adt/int.c')
-rw-r--r--src/backend/utils/adt/int.c283
1 files changed, 257 insertions, 26 deletions
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 1183fa9aa0c..7dde75014e8 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.62 2004/08/29 05:06:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.63 2004/10/04 14:42:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,7 +28,6 @@
* Arithmetic operators:
* intmod
*/
-
#include "postgres.h"
#include <ctype.h>
@@ -38,6 +37,7 @@
#include "libpq/pqformat.h"
#include "utils/builtins.h"
+
#ifndef SHRT_MAX
#define SHRT_MAX (0x7FFF)
#endif
@@ -45,6 +45,8 @@
#define SHRT_MIN (-0x8000)
#endif
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
+
typedef struct
{
int32 current;
@@ -52,6 +54,7 @@ typedef struct
int32 step;
} generate_series_fctx;
+
/*****************************************************************************
* USER I/O ROUTINES *
*****************************************************************************/
@@ -291,7 +294,7 @@ i4toi2(PG_FUNCTION_ARGS)
if (arg1 < SHRT_MIN || arg1 > SHRT_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
+ errmsg("smallint out of range")));
PG_RETURN_INT16((int16) arg1);
}
@@ -601,8 +604,15 @@ Datum
int4um(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
+ int32 result;
- PG_RETURN_INT32(-arg);
+ result = -arg;
+ /* overflow check (needed for INT_MIN) */
+ if (arg != 0 && SAMESIGN(result, arg))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -618,8 +628,19 @@ int4pl(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 + arg2);
+ result = arg1 + arg2;
+ /*
+ * Overflow check. If the inputs are of different signs then their sum
+ * cannot overflow. If the inputs are of the same sign, their sum
+ * had better be that sign too.
+ */
+ if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -627,8 +648,19 @@ int4mi(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 - arg2);
+ result = arg1 - arg2;
+ /*
+ * Overflow check. If the inputs are of the same sign then their
+ * difference cannot overflow. If they are of different signs then
+ * the result should be of the same sign as the first input.
+ */
+ if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -636,8 +668,28 @@ int4mul(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 * arg2);
+ result = arg1 * arg2;
+ /*
+ * Overflow check. We basically check to see if result / arg2 gives
+ * arg1 again. There are two cases where this fails: arg2 = 0 (which
+ * cannot overflow) and arg1 = INT_MIN, arg2 = -1 (where the division
+ * itself will overflow and thus incorrectly match).
+ *
+ * Since the division is likely much more expensive than the actual
+ * multiplication, we'd like to skip it where possible. The best
+ * bang for the buck seems to be to check whether both inputs are in
+ * the int16 range; if so, no overflow is possible.
+ */
+ if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX &&
+ arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
+ arg2 != 0 &&
+ (result/arg2 != arg1 || (arg2 == -1 && arg1 < 0 && result < 0)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -645,29 +697,55 @@ int4div(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
if (arg2 == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- PG_RETURN_INT32(arg1 / arg2);
+ result = arg1 / arg2;
+ /*
+ * Overflow check. The only possible overflow case is for
+ * arg1 = INT_MIN, arg2 = -1, where the correct result is -INT_MIN,
+ * which can't be represented on a two's-complement machine.
+ */
+ if (arg2 == -1 && arg1 < 0 && result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
int4inc(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
+ int32 result;
- PG_RETURN_INT32(arg + 1);
+ result = arg + 1;
+ /* Overflow check */
+ if (arg > 0 && result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ PG_RETURN_INT32(result);
}
Datum
int2um(PG_FUNCTION_ARGS)
{
int16 arg = PG_GETARG_INT16(0);
+ int16 result;
- PG_RETURN_INT16(-arg);
+ result = -arg;
+ /* overflow check (needed for SHRT_MIN) */
+ if (arg != 0 && SAMESIGN(result, arg))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+ PG_RETURN_INT16(result);
}
Datum
@@ -683,8 +761,19 @@ int2pl(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int16 result;
- PG_RETURN_INT16(arg1 + arg2);
+ result = arg1 + arg2;
+ /*
+ * Overflow check. If the inputs are of different signs then their sum
+ * cannot overflow. If the inputs are of the same sign, their sum
+ * had better be that sign too.
+ */
+ if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+ PG_RETURN_INT16(result);
}
Datum
@@ -692,8 +781,19 @@ int2mi(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int16 result;
- PG_RETURN_INT16(arg1 - arg2);
+ result = arg1 - arg2;
+ /*
+ * Overflow check. If the inputs are of the same sign then their
+ * difference cannot overflow. If they are of different signs then
+ * the result should be of the same sign as the first input.
+ */
+ if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+ PG_RETURN_INT16(result);
}
Datum
@@ -701,8 +801,20 @@ int2mul(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result32;
- PG_RETURN_INT16(arg1 * arg2);
+ /*
+ * The most practical way to detect overflow is to do the arithmetic
+ * in int32 (so that the result can't overflow) and then do a range
+ * check.
+ */
+ result32 = (int32) arg1 * (int32) arg2;
+ if (result32 < SHRT_MIN || result32 > SHRT_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ PG_RETURN_INT16((int16) result32);
}
Datum
@@ -710,13 +822,24 @@ int2div(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int16 result;
if (arg2 == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- PG_RETURN_INT16(arg1 / arg2);
+ result = arg1 / arg2;
+ /*
+ * Overflow check. The only possible overflow case is for
+ * arg1 = SHRT_MIN, arg2 = -1, where the correct result is -SHRT_MIN,
+ * which can't be represented on a two's-complement machine.
+ */
+ if (arg2 == -1 && arg1 < 0 && result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+ PG_RETURN_INT16(result);
}
Datum
@@ -724,8 +847,19 @@ int24pl(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 + arg2);
+ result = arg1 + arg2;
+ /*
+ * Overflow check. If the inputs are of different signs then their sum
+ * cannot overflow. If the inputs are of the same sign, their sum
+ * had better be that sign too.
+ */
+ if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -733,8 +867,19 @@ int24mi(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 - arg2);
+ result = arg1 - arg2;
+ /*
+ * Overflow check. If the inputs are of the same sign then their
+ * difference cannot overflow. If they are of different signs then
+ * the result should be of the same sign as the first input.
+ */
+ if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -742,8 +887,25 @@ int24mul(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
int32 arg2 = PG_GETARG_INT32(1);
+ int32 result;
- PG_RETURN_INT32(arg1 * arg2);
+ result = arg1 * arg2;
+ /*
+ * Overflow check. We basically check to see if result / arg2 gives
+ * arg1 again. There is one case where this fails: arg2 = 0 (which
+ * cannot overflow).
+ *
+ * Since the division is likely much more expensive than the actual
+ * multiplication, we'd like to skip it where possible. The best
+ * bang for the buck seems to be to check whether both inputs are in
+ * the int16 range; if so, no overflow is possible.
+ */
+ if (!(arg2 >= (int32) SHRT_MIN && arg2 <= (int32) SHRT_MAX) &&
+ result/arg2 != arg1)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -756,8 +918,8 @@ int24div(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
-
- PG_RETURN_INT32(arg1 / arg2);
+ /* No overflow is possible */
+ PG_RETURN_INT32((int32) arg1 / arg2);
}
Datum
@@ -765,8 +927,19 @@ int42pl(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result;
- PG_RETURN_INT32(arg1 + arg2);
+ result = arg1 + arg2;
+ /*
+ * Overflow check. If the inputs are of different signs then their sum
+ * cannot overflow. If the inputs are of the same sign, their sum
+ * had better be that sign too.
+ */
+ if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -774,8 +947,19 @@ int42mi(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result;
- PG_RETURN_INT32(arg1 - arg2);
+ result = arg1 - arg2;
+ /*
+ * Overflow check. If the inputs are of the same sign then their
+ * difference cannot overflow. If they are of different signs then
+ * the result should be of the same sign as the first input.
+ */
+ if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -783,8 +967,25 @@ int42mul(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result;
- PG_RETURN_INT32(arg1 * arg2);
+ result = arg1 * arg2;
+ /*
+ * Overflow check. We basically check to see if result / arg1 gives
+ * arg2 again. There is one case where this fails: arg1 = 0 (which
+ * cannot overflow).
+ *
+ * Since the division is likely much more expensive than the actual
+ * multiplication, we'd like to skip it where possible. The best
+ * bang for the buck seems to be to check whether both inputs are in
+ * the int16 range; if so, no overflow is possible.
+ */
+ if (!(arg1 >= (int32) SHRT_MIN && arg1 <= (int32) SHRT_MAX) &&
+ result/arg1 != arg2)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -792,13 +993,24 @@ int42div(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
int16 arg2 = PG_GETARG_INT16(1);
+ int32 result;
if (arg2 == 0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- PG_RETURN_INT32(arg1 / arg2);
+ result = arg1 / arg2;
+ /*
+ * Overflow check. The only possible overflow case is for
+ * arg1 = INT_MIN, arg2 = -1, where the correct result is -INT_MIN,
+ * which can't be represented on a two's-complement machine.
+ */
+ if (arg2 == -1 && arg1 < 0 && result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
@@ -811,6 +1023,7 @@ int4mod(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
+ /* No overflow is possible */
PG_RETURN_INT32(arg1 % arg2);
}
@@ -825,6 +1038,7 @@ int2mod(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
+ /* No overflow is possible */
PG_RETURN_INT16(arg1 % arg2);
}
@@ -839,6 +1053,7 @@ int24mod(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
+ /* No overflow is possible */
PG_RETURN_INT32(arg1 % arg2);
}
@@ -853,6 +1068,7 @@ int42mod(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
+ /* No overflow is possible */
PG_RETURN_INT32(arg1 % arg2);
}
@@ -865,16 +1081,30 @@ Datum
int4abs(PG_FUNCTION_ARGS)
{
int32 arg1 = PG_GETARG_INT32(0);
+ int32 result;
- PG_RETURN_INT32((arg1 < 0) ? -arg1 : arg1);
+ result = (arg1 < 0) ? -arg1 : arg1;
+ /* overflow check (needed for INT_MIN) */
+ if (result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ PG_RETURN_INT32(result);
}
Datum
int2abs(PG_FUNCTION_ARGS)
{
int16 arg1 = PG_GETARG_INT16(0);
+ int16 result;
- PG_RETURN_INT16((arg1 < 0) ? -arg1 : arg1);
+ result = (arg1 < 0) ? -arg1 : arg1;
+ /* overflow check (needed for SHRT_MIN) */
+ if (result < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+ PG_RETURN_INT16(result);
}
Datum
@@ -913,7 +1143,8 @@ int4smaller(PG_FUNCTION_ARGS)
PG_RETURN_INT32((arg1 < arg2) ? arg1 : arg2);
}
-/* Binary arithmetics
+/*
+ * Bit-pushing operators
*
* int[24]and - returns arg1 & arg2
* int[24]or - returns arg1 | arg2