aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c1474
1 files changed, 1169 insertions, 305 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 1773fa292e4..ed825a1fddf 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -109,14 +109,13 @@ typedef int16 NumericDigit;
* If the high bits of the first word of a NumericChoice (n_header, or
* n_short.n_header, or n_long.n_sign_dscale) are NUMERIC_SHORT, then the
* numeric follows the NumericShort format; if they are NUMERIC_POS or
- * NUMERIC_NEG, it follows the NumericLong format. If they are NUMERIC_NAN,
- * it is a NaN. We currently always store a NaN using just two bytes (i.e.
- * only n_header), but previous releases used only the NumericLong format,
- * so we might find 4-byte NaNs on disk if a database has been migrated using
- * pg_upgrade. In either case, when the high bits indicate a NaN, the
- * remaining bits are never examined. Currently, we always initialize these
- * to zero, but it might be possible to use them for some other purpose in
- * the future.
+ * NUMERIC_NEG, it follows the NumericLong format. If they are NUMERIC_SPECIAL,
+ * the value is a NaN or Infinity. We currently always store SPECIAL values
+ * using just two bytes (i.e. only n_header), but previous releases used only
+ * the NumericLong format, so we might find 4-byte NaNs (though not infinities)
+ * on disk if a database has been migrated using pg_upgrade. In either case,
+ * the low-order bits of a special value's header are reserved and currently
+ * should always be set to zero.
*
* In the NumericShort format, the remaining 14 bits of the header word
* (n_short.n_header) are allocated as follows: 1 for sign (positive or
@@ -168,19 +167,19 @@ struct NumericData
#define NUMERIC_POS 0x0000
#define NUMERIC_NEG 0x4000
#define NUMERIC_SHORT 0x8000
-#define NUMERIC_NAN 0xC000
+#define NUMERIC_SPECIAL 0xC000
#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK)
-#define NUMERIC_IS_NAN(n) (NUMERIC_FLAGBITS(n) == NUMERIC_NAN)
#define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT)
+#define NUMERIC_IS_SPECIAL(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SPECIAL)
#define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16))
#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16))
/*
- * If the flag bits are NUMERIC_SHORT or NUMERIC_NAN, we want the short header;
- * otherwise, we want the long one. Instead of testing against each value, we
- * can just look at the high bit, for a slight efficiency gain.
+ * If the flag bits are NUMERIC_SHORT or NUMERIC_SPECIAL, we want the short
+ * header; otherwise, we want the long one. Instead of testing against each
+ * value, we can just look at the high bit, for a slight efficiency gain.
*/
#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0)
#define NUMERIC_HEADER_SIZE(n) \
@@ -188,6 +187,28 @@ struct NumericData
(NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16)))
/*
+ * Definitions for special values (NaN, positive infinity, negative infinity).
+ *
+ * The two bits after the NUMERIC_SPECIAL bits are 00 for NaN, 01 for positive
+ * infinity, 11 for negative infinity. (This makes the sign bit match where
+ * it is in a short-format value, though we make no use of that at present.)
+ * We could mask off the remaining bits before testing the active bits, but
+ * currently those bits must be zeroes, so masking would just add cycles.
+ */
+#define NUMERIC_EXT_SIGN_MASK 0xF000 /* high bits plus NaN/Inf flag bits */
+#define NUMERIC_NAN 0xC000
+#define NUMERIC_PINF 0xD000
+#define NUMERIC_NINF 0xF000
+#define NUMERIC_INF_SIGN_MASK 0x2000
+
+#define NUMERIC_EXT_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_EXT_SIGN_MASK)
+#define NUMERIC_IS_NAN(n) ((n)->choice.n_header == NUMERIC_NAN)
+#define NUMERIC_IS_PINF(n) ((n)->choice.n_header == NUMERIC_PINF)
+#define NUMERIC_IS_NINF(n) ((n)->choice.n_header == NUMERIC_NINF)
+#define NUMERIC_IS_INF(n) \
+ (((n)->choice.n_header & ~NUMERIC_INF_SIGN_MASK) == NUMERIC_PINF)
+
+/*
* Short format definitions.
*/
@@ -202,7 +223,13 @@ struct NumericData
#define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1))
/*
- * Extract sign, display scale, weight.
+ * Extract sign, display scale, weight. These macros extract field values
+ * suitable for the NumericVar format from the Numeric (on-disk) format.
+ *
+ * Note that we don't trouble to ensure that dscale and weight read as zero
+ * for an infinity; however, that doesn't matter since we never convert
+ * "special" numerics to NumericVar form. Only the constants defined below
+ * (const_nan, etc) ever represent a non-finite value as a NumericVar.
*/
#define NUMERIC_DSCALE_MASK 0x3FFF
@@ -210,7 +237,9 @@ struct NumericData
#define NUMERIC_SIGN(n) \
(NUMERIC_IS_SHORT(n) ? \
(((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \
- NUMERIC_NEG : NUMERIC_POS) : NUMERIC_FLAGBITS(n))
+ NUMERIC_NEG : NUMERIC_POS) : \
+ (NUMERIC_IS_SPECIAL(n) ? \
+ NUMERIC_EXT_FLAGBITS(n) : NUMERIC_FLAGBITS(n)))
#define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \
((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \
>> NUMERIC_SHORT_DSCALE_SHIFT \
@@ -227,7 +256,9 @@ struct NumericData
* complex.
*
* The value represented by a NumericVar is determined by the sign, weight,
- * ndigits, and digits[] array.
+ * ndigits, and digits[] array. If it is a "special" value (NaN or Inf)
+ * then only the sign field matters; ndigits should be zero, and the weight
+ * and dscale fields are ignored.
*
* Note: the first digit of a NumericVar's value is assumed to be multiplied
* by NBASE ** weight. Another way to say it is that there are weight+1
@@ -274,7 +305,7 @@ typedef struct NumericVar
{
int ndigits; /* # of digits in digits[] - can be 0! */
int weight; /* weight of first digit */
- int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
+ int sign; /* NUMERIC_POS, _NEG, _NAN, _PINF, or _NINF */
int dscale; /* display scale */
NumericDigit *buf; /* start of palloc'd space for digits[] */
NumericDigit *digits; /* base-NBASE digits */
@@ -354,16 +385,26 @@ typedef struct NumericSumAccum
* representations for numeric values in order to avoid depending on
* USE_FLOAT8_BYVAL. The type of abbreviation we use is based only on
* the size of a datum, not the argument-passing convention for float8.
+ *
+ * The range of abbreviations for finite values is from +PG_INT64/32_MAX
+ * to -PG_INT64/32_MAX. NaN has the abbreviation PG_INT64/32_MIN, and we
+ * define the sort ordering to make that work out properly (see further
+ * comments below). PINF and NINF share the abbreviations of the largest
+ * and smallest finite abbreviation classes.
*/
#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE)
#if SIZEOF_DATUM == 8
#define NumericAbbrevGetDatum(X) ((Datum) (X))
#define DatumGetNumericAbbrev(X) ((int64) (X))
#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT64_MIN)
+#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT64_MAX)
+#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT64_MAX)
#else
#define NumericAbbrevGetDatum(X) ((Datum) (X))
#define DatumGetNumericAbbrev(X) ((int32) (X))
#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT32_MIN)
+#define NUMERIC_ABBREV_PINF NumericAbbrevGetDatum(-PG_INT32_MAX)
+#define NUMERIC_ABBREV_NINF NumericAbbrevGetDatum(PG_INT32_MAX)
#endif
@@ -379,6 +420,9 @@ static const NumericDigit const_one_data[1] = {1};
static const NumericVar const_one =
{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_one_data};
+static const NumericVar const_minus_one =
+{1, 0, NUMERIC_NEG, 0, NULL, (NumericDigit *) const_one_data};
+
static const NumericDigit const_two_data[1] = {2};
static const NumericVar const_two =
{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_two_data};
@@ -416,6 +460,12 @@ static const NumericVar const_one_point_one =
static const NumericVar const_nan =
{0, 0, NUMERIC_NAN, 0, NULL, NULL};
+static const NumericVar const_pinf =
+{0, 0, NUMERIC_PINF, 0, NULL, NULL};
+
+static const NumericVar const_ninf =
+{0, 0, NUMERIC_NINF, 0, NULL, NULL};
+
#if DEC_DIGITS == 4
static const int round_powers[4] = {0, 1000, 100, 10};
#endif
@@ -465,10 +515,12 @@ static void set_var_from_var(const NumericVar *value, NumericVar *dest);
static char *get_str_from_var(const NumericVar *var);
static char *get_str_from_var_sci(const NumericVar *var, int rscale);
+static Numeric duplicate_numeric(Numeric num);
static Numeric make_result(const NumericVar *var);
static Numeric make_result_opt_error(const NumericVar *var, bool *error);
static void apply_typmod(NumericVar *var, int32 typmod);
+static void apply_typmod_special(Numeric num, int32 typmod);
static bool numericvar_to_int32(const NumericVar *var, int32 *result);
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
@@ -478,7 +530,6 @@ static bool numericvar_to_uint64(const NumericVar *var, uint64 *result);
static bool numericvar_to_int128(const NumericVar *var, int128 *result);
static void int128_to_numericvar(int128 val, NumericVar *var);
#endif
-static double numeric_to_double_no_overflow(Numeric num);
static double numericvar_to_double_no_overflow(const NumericVar *var);
static Datum numeric_abbrev_convert(Datum original_datum, SortSupport ssup);
@@ -587,23 +638,43 @@ numeric_in(PG_FUNCTION_ARGS)
}
/*
- * Check for NaN
+ * Check for NaN and infinities. We recognize the same strings allowed by
+ * float8in().
*/
if (pg_strncasecmp(cp, "NaN", 3) == 0)
{
res = make_result(&const_nan);
-
- /* Should be nothing left but spaces */
cp += 3;
- while (*cp)
- {
- if (!isspace((unsigned char) *cp))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "numeric", str)));
- cp++;
- }
+ }
+ else if (pg_strncasecmp(cp, "Infinity", 8) == 0)
+ {
+ res = make_result(&const_pinf);
+ cp += 8;
+ }
+ else if (pg_strncasecmp(cp, "+Infinity", 9) == 0)
+ {
+ res = make_result(&const_pinf);
+ cp += 9;
+ }
+ else if (pg_strncasecmp(cp, "-Infinity", 9) == 0)
+ {
+ res = make_result(&const_ninf);
+ cp += 9;
+ }
+ else if (pg_strncasecmp(cp, "inf", 3) == 0)
+ {
+ res = make_result(&const_pinf);
+ cp += 3;
+ }
+ else if (pg_strncasecmp(cp, "+inf", 4) == 0)
+ {
+ res = make_result(&const_pinf);
+ cp += 4;
+ }
+ else if (pg_strncasecmp(cp, "-inf", 4) == 0)
+ {
+ res = make_result(&const_ninf);
+ cp += 4;
}
else
{
@@ -620,7 +691,7 @@ numeric_in(PG_FUNCTION_ARGS)
* We duplicate a few lines of code here because we would like to
* throw any trailing-junk syntax error before any semantic error
* resulting from apply_typmod. We can't easily fold the two cases
- * together because we mustn't apply apply_typmod to a NaN.
+ * together because we mustn't apply apply_typmod to a NaN/Inf.
*/
while (*cp)
{
@@ -636,8 +707,24 @@ numeric_in(PG_FUNCTION_ARGS)
res = make_result(&value);
free_var(&value);
+
+ PG_RETURN_NUMERIC(res);
+ }
+
+ /* Should be nothing left but spaces */
+ while (*cp)
+ {
+ if (!isspace((unsigned char) *cp))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "numeric", str)));
+ cp++;
}
+ /* As above, throw any typmod error after finishing syntax check */
+ apply_typmod_special(res, typmod);
+
PG_RETURN_NUMERIC(res);
}
@@ -655,10 +742,17 @@ numeric_out(PG_FUNCTION_ARGS)
char *str;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_CSTRING(pstrdup("NaN"));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ PG_RETURN_CSTRING(pstrdup("Infinity"));
+ else if (NUMERIC_IS_NINF(num))
+ PG_RETURN_CSTRING(pstrdup("-Infinity"));
+ else
+ PG_RETURN_CSTRING(pstrdup("NaN"));
+ }
/*
* Get the number in the variable format.
@@ -682,6 +776,41 @@ numeric_is_nan(Numeric num)
}
/*
+ * numeric_is_inf() -
+ *
+ * Is Numeric value an infinity?
+ */
+bool
+numeric_is_inf(Numeric num)
+{
+ return NUMERIC_IS_INF(num);
+}
+
+/*
+ * numeric_is_integral() -
+ *
+ * Is Numeric value integral?
+ */
+static bool
+numeric_is_integral(Numeric num)
+{
+ NumericVar arg;
+
+ /* Reject NaN, but infinities are considered integral */
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_NAN(num))
+ return false;
+ return true;
+ }
+
+ /* Integral if there are no digits to the right of the decimal point */
+ init_var_from_num(num, &arg);
+
+ return (arg.ndigits == 0 || arg.ndigits <= arg.weight + 1);
+}
+
+/*
* numeric_maximum_size() -
*
* Maximum size of a numeric with given typmod, or -1 if unlimited/unknown.
@@ -732,10 +861,17 @@ numeric_out_sci(Numeric num, int scale)
char *str;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- return pstrdup("NaN");
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ return pstrdup("Infinity");
+ else if (NUMERIC_IS_NINF(num))
+ return pstrdup("-Infinity");
+ else
+ return pstrdup("NaN");
+ }
init_var_from_num(num, &x);
@@ -760,10 +896,17 @@ numeric_normalize(Numeric num)
int last;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- return pstrdup("NaN");
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ return pstrdup("Infinity");
+ else if (NUMERIC_IS_NINF(num))
+ return pstrdup("-Infinity");
+ else
+ return pstrdup("NaN");
+ }
init_var_from_num(num, &x);
@@ -823,7 +966,9 @@ numeric_recv(PG_FUNCTION_ARGS)
value.sign = (uint16) pq_getmsgint(buf, sizeof(uint16));
if (!(value.sign == NUMERIC_POS ||
value.sign == NUMERIC_NEG ||
- value.sign == NUMERIC_NAN))
+ value.sign == NUMERIC_NAN ||
+ value.sign == NUMERIC_PINF ||
+ value.sign == NUMERIC_NINF))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid sign in external \"numeric\" value")));
@@ -849,13 +994,29 @@ numeric_recv(PG_FUNCTION_ARGS)
* If the given dscale would hide any digits, truncate those digits away.
* We could alternatively throw an error, but that would take a bunch of
* extra code (about as much as trunc_var involves), and it might cause
- * client compatibility issues.
+ * client compatibility issues. Be careful not to apply trunc_var to
+ * special values, as it could do the wrong thing; we don't need it
+ * anyway, since make_result will ignore all but the sign field.
+ *
+ * After doing that, be sure to check the typmod restriction.
*/
- trunc_var(&value, value.dscale);
+ if (value.sign == NUMERIC_POS ||
+ value.sign == NUMERIC_NEG)
+ {
+ trunc_var(&value, value.dscale);
- apply_typmod(&value, typmod);
+ apply_typmod(&value, typmod);
+
+ res = make_result(&value);
+ }
+ else
+ {
+ /* apply_typmod_special wants us to make the Numeric first */
+ res = make_result(&value);
+
+ apply_typmod_special(res, typmod);
+ }
- res = make_result(&value);
free_var(&value);
PG_RETURN_NUMERIC(res);
@@ -961,21 +1122,21 @@ numeric (PG_FUNCTION_ARGS)
NumericVar var;
/*
- * Handle NaN
+ * Handle NaN and infinities: if apply_typmod_special doesn't complain,
+ * just return a copy of the input.
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ apply_typmod_special(num, typmod);
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
+ }
/*
* If the value isn't a valid type modifier, simply return a copy of the
* input value
*/
if (typmod < (int32) (VARHDRSZ))
- {
- new = (Numeric) palloc(VARSIZE(num));
- memcpy(new, num, VARSIZE(num));
- PG_RETURN_NUMERIC(new);
- }
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
/*
* Get the precision and scale out of the typmod value
@@ -997,8 +1158,7 @@ numeric (PG_FUNCTION_ARGS)
&& (NUMERIC_CAN_BE_SHORT(scale, NUMERIC_WEIGHT(num))
|| !NUMERIC_IS_SHORT(num)))
{
- new = (Numeric) palloc(VARSIZE(num));
- memcpy(new, num, VARSIZE(num));
+ new = duplicate_numeric(num);
if (NUMERIC_IS_SHORT(num))
new->choice.n_short.n_header =
(num->choice.n_short.n_header & ~NUMERIC_SHORT_DSCALE_MASK)
@@ -1100,20 +1260,19 @@ numeric_abs(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
- */
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
-
- /*
* Do it the easy way directly on the packed format
*/
- res = (Numeric) palloc(VARSIZE(num));
- memcpy(res, num, VARSIZE(num));
+ res = duplicate_numeric(num);
if (NUMERIC_IS_SHORT(num))
res->choice.n_short.n_header =
num->choice.n_short.n_header & ~NUMERIC_SHORT_SIGN_MASK;
+ else if (NUMERIC_IS_SPECIAL(num))
+ {
+ /* This changes -Inf to Inf, and doesn't affect NaN */
+ res->choice.n_short.n_header =
+ num->choice.n_short.n_header & ~NUMERIC_INF_SIGN_MASK;
+ }
else
res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(num);
@@ -1128,23 +1287,24 @@ numeric_uminus(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
- */
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
-
- /*
* Do it the easy way directly on the packed format
*/
- res = (Numeric) palloc(VARSIZE(num));
- memcpy(res, num, VARSIZE(num));
+ res = duplicate_numeric(num);
+
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ /* Flip the sign, if it's Inf or -Inf */
+ if (!NUMERIC_IS_NAN(num))
+ res->choice.n_short.n_header =
+ num->choice.n_short.n_header ^ NUMERIC_INF_SIGN_MASK;
+ }
/*
* The packed format is known to be totally zero digit trimmed always. So
- * we can identify a ZERO by the fact that there are no digits at all. Do
- * nothing to a zero.
+ * once we've eliminated specials, we can identify a zero by the fact that
+ * there are no digits at all. Do nothing to a zero.
*/
- if (NUMERIC_NDIGITS(num) != 0)
+ else if (NUMERIC_NDIGITS(num) != 0)
{
/* Else, flip the sign */
if (NUMERIC_IS_SHORT(num))
@@ -1166,12 +1326,42 @@ Datum
numeric_uplus(PG_FUNCTION_ARGS)
{
Numeric num = PG_GETARG_NUMERIC(0);
- Numeric res;
- res = (Numeric) palloc(VARSIZE(num));
- memcpy(res, num, VARSIZE(num));
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
+}
- PG_RETURN_NUMERIC(res);
+
+/*
+ * numeric_sign_internal() -
+ *
+ * Returns -1 if the argument is less than 0, 0 if the argument is equal
+ * to 0, and 1 if the argument is greater than zero. Caller must have
+ * taken care of the NaN case, but we can handle infinities here.
+ */
+static int
+numeric_sign_internal(Numeric num)
+{
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ Assert(!NUMERIC_IS_NAN(num));
+ /* Must be Inf or -Inf */
+ if (NUMERIC_IS_PINF(num))
+ return 1;
+ else
+ return -1;
+ }
+
+ /*
+ * The packed format is known to be totally zero digit trimmed always. So
+ * once we've eliminated specials, we can identify a zero by the fact that
+ * there are no digits at all.
+ */
+ else if (NUMERIC_NDIGITS(num) == 0)
+ return 0;
+ else if (NUMERIC_SIGN(num) == NUMERIC_NEG)
+ return -1;
+ else
+ return 1;
}
/*
@@ -1184,37 +1374,25 @@ Datum
numeric_sign(PG_FUNCTION_ARGS)
{
Numeric num = PG_GETARG_NUMERIC(0);
- Numeric res;
- NumericVar result;
/*
- * Handle NaN
+ * Handle NaN (infinities can be handled normally)
*/
if (NUMERIC_IS_NAN(num))
PG_RETURN_NUMERIC(make_result(&const_nan));
- init_var(&result);
-
- /*
- * The packed format is known to be totally zero digit trimmed always. So
- * we can identify a ZERO by the fact that there are no digits at all.
- */
- if (NUMERIC_NDIGITS(num) == 0)
- set_var_from_var(&const_zero, &result);
- else
+ switch (numeric_sign_internal(num))
{
- /*
- * And if there are some, we return a copy of ONE with the sign of our
- * argument
- */
- set_var_from_var(&const_one, &result);
- result.sign = NUMERIC_SIGN(num);
+ case 0:
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ case 1:
+ PG_RETURN_NUMERIC(make_result(&const_one));
+ case -1:
+ PG_RETURN_NUMERIC(make_result(&const_minus_one));
}
- res = make_result(&result);
- free_var(&result);
-
- PG_RETURN_NUMERIC(res);
+ Assert(false);
+ return (Datum) 0;
}
@@ -1234,10 +1412,10 @@ numeric_round(PG_FUNCTION_ARGS)
NumericVar arg;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
/*
* Limit the scale value to avoid possible overflow in calculations
@@ -1283,10 +1461,10 @@ numeric_trunc(PG_FUNCTION_ARGS)
NumericVar arg;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
/*
* Limit the scale value to avoid possible overflow in calculations
@@ -1328,8 +1506,11 @@ numeric_ceil(PG_FUNCTION_ARGS)
Numeric res;
NumericVar result;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ /*
+ * Handle NaN and infinities
+ */
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
init_var_from_num(num, &result);
ceil_var(&result, &result);
@@ -1353,8 +1534,11 @@ numeric_floor(PG_FUNCTION_ARGS)
Numeric res;
NumericVar result;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ /*
+ * Handle NaN and infinities
+ */
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
init_var_from_num(num, &result);
floor_var(&result, &result);
@@ -1390,26 +1574,46 @@ generate_series_step_numeric(PG_FUNCTION_ARGS)
Numeric stop_num = PG_GETARG_NUMERIC(1);
NumericVar steploc = const_one;
- /* handle NaN in start and stop values */
- if (NUMERIC_IS_NAN(start_num))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("start value cannot be NaN")));
-
- if (NUMERIC_IS_NAN(stop_num))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("stop value cannot be NaN")));
+ /* Reject NaN and infinities in start and stop values */
+ if (NUMERIC_IS_SPECIAL(start_num))
+ {
+ if (NUMERIC_IS_NAN(start_num))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("start value cannot be NaN")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("start value cannot be infinity")));
+ }
+ if (NUMERIC_IS_SPECIAL(stop_num))
+ {
+ if (NUMERIC_IS_NAN(stop_num))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("stop value cannot be NaN")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("stop value cannot be infinity")));
+ }
/* see if we were given an explicit step size */
if (PG_NARGS() == 3)
{
Numeric step_num = PG_GETARG_NUMERIC(2);
- if (NUMERIC_IS_NAN(step_num))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("step size cannot be NaN")));
+ if (NUMERIC_IS_SPECIAL(step_num))
+ {
+ if (NUMERIC_IS_NAN(step_num))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("step size cannot be NaN")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("step size cannot be infinity")));
+ }
init_var_from_num(step_num, &steploc);
@@ -1510,12 +1714,21 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("count must be greater than zero")));
- if (NUMERIC_IS_NAN(operand) ||
- NUMERIC_IS_NAN(bound1) ||
- NUMERIC_IS_NAN(bound2))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
- errmsg("operand, lower bound, and upper bound cannot be NaN")));
+ if (NUMERIC_IS_SPECIAL(operand) ||
+ NUMERIC_IS_SPECIAL(bound1) ||
+ NUMERIC_IS_SPECIAL(bound2))
+ {
+ if (NUMERIC_IS_NAN(operand) ||
+ NUMERIC_IS_NAN(bound1) ||
+ NUMERIC_IS_NAN(bound2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("operand, lower bound, and upper bound cannot be NaN")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("operand, lower bound, and upper bound cannot be infinity")));
+ }
init_var(&result_var);
init_var(&count_var);
@@ -1719,9 +1932,14 @@ numeric_abbrev_convert(Datum original_datum, SortSupport ssup)
else
value = (Numeric) original_varatt;
- if (NUMERIC_IS_NAN(value))
+ if (NUMERIC_IS_SPECIAL(value))
{
- result = NUMERIC_ABBREV_NAN;
+ if (NUMERIC_IS_PINF(value))
+ result = NUMERIC_ABBREV_PINF;
+ else if (NUMERIC_IS_NINF(value))
+ result = NUMERIC_ABBREV_NINF;
+ else
+ result = NUMERIC_ABBREV_NAN;
}
else
{
@@ -1847,7 +2065,7 @@ numeric_cmp_abbrev(Datum x, Datum y, SortSupport ssup)
{
/*
* NOTE WELL: this is intentionally backwards, because the abbreviation is
- * negated relative to the original value, to handle NaN.
+ * negated relative to the original value, to handle NaN/infinity cases.
*/
if (DatumGetNumericAbbrev(x) < DatumGetNumericAbbrev(y))
return 1;
@@ -2150,20 +2368,42 @@ cmp_numerics(Numeric num1, Numeric num2)
int result;
/*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
+ * We consider all NANs to be equal and larger than any non-NAN (including
+ * Infinity). This is somewhat arbitrary; the important thing is to have
+ * a consistent sort order.
*/
- if (NUMERIC_IS_NAN(num1))
+ if (NUMERIC_IS_SPECIAL(num1))
{
- if (NUMERIC_IS_NAN(num2))
- result = 0; /* NAN = NAN */
- else
- result = 1; /* NAN > non-NAN */
+ if (NUMERIC_IS_NAN(num1))
+ {
+ if (NUMERIC_IS_NAN(num2))
+ result = 0; /* NAN = NAN */
+ else
+ result = 1; /* NAN > non-NAN */
+ }
+ else if (NUMERIC_IS_PINF(num1))
+ {
+ if (NUMERIC_IS_NAN(num2))
+ result = -1; /* PINF < NAN */
+ else if (NUMERIC_IS_PINF(num2))
+ result = 0; /* PINF = PINF */
+ else
+ result = 1; /* PINF > anything else */
+ }
+ else /* num1 must be NINF */
+ {
+ if (NUMERIC_IS_NINF(num2))
+ result = 0; /* NINF = NINF */
+ else
+ result = -1; /* NINF < anything else */
+ }
}
- else if (NUMERIC_IS_NAN(num2))
+ else if (NUMERIC_IS_SPECIAL(num2))
{
- result = -1; /* non-NAN < NAN */
+ if (NUMERIC_IS_NINF(num2))
+ result = 1; /* normal > NINF */
+ else
+ result = -1; /* normal < NAN or PINF */
}
else
{
@@ -2190,10 +2430,12 @@ in_range_numeric_numeric(PG_FUNCTION_ARGS)
bool result;
/*
- * Reject negative or NaN offset. Negative is per spec, and NaN is
- * because appropriate semantics for that seem non-obvious.
+ * Reject negative (including -Inf) or NaN offset. Negative is per spec,
+ * and NaN is because appropriate semantics for that seem non-obvious.
*/
- if (NUMERIC_IS_NAN(offset) || NUMERIC_SIGN(offset) == NUMERIC_NEG)
+ if (NUMERIC_IS_NAN(offset) ||
+ NUMERIC_IS_NINF(offset) ||
+ NUMERIC_SIGN(offset) == NUMERIC_NEG)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("invalid preceding or following size in window function")));
@@ -2214,6 +2456,67 @@ in_range_numeric_numeric(PG_FUNCTION_ARGS)
{
result = less; /* non-NAN < NAN */
}
+
+ /*
+ * Deal with infinite offset (necessarily +Inf, at this point).
+ */
+ else if (NUMERIC_IS_SPECIAL(offset))
+ {
+ Assert(NUMERIC_IS_PINF(offset));
+ if (sub ? NUMERIC_IS_PINF(base) : NUMERIC_IS_NINF(base))
+ {
+ /*
+ * base +/- offset would produce NaN, so return true for any val
+ * (see in_range_float8_float8() for reasoning).
+ */
+ result = true;
+ }
+ else if (sub)
+ {
+ /* base - offset must be -inf */
+ if (less)
+ result = NUMERIC_IS_NINF(val); /* only -inf is <= sum */
+ else
+ result = true; /* any val is >= sum */
+ }
+ else
+ {
+ /* base + offset must be +inf */
+ if (less)
+ result = true; /* any val is <= sum */
+ else
+ result = NUMERIC_IS_PINF(val); /* only +inf is >= sum */
+ }
+ }
+
+ /*
+ * Deal with cases where val and/or base is infinite. The offset, being
+ * now known finite, cannot affect the conclusion.
+ */
+ else if (NUMERIC_IS_SPECIAL(val))
+ {
+ if (NUMERIC_IS_PINF(val))
+ {
+ if (NUMERIC_IS_PINF(base))
+ result = true; /* PINF = PINF */
+ else
+ result = !less; /* PINF > any other non-NAN */
+ }
+ else /* val must be NINF */
+ {
+ if (NUMERIC_IS_NINF(base))
+ result = true; /* NINF = NINF */
+ else
+ result = less; /* NINF < anything else */
+ }
+ }
+ else if (NUMERIC_IS_SPECIAL(base))
+ {
+ if (NUMERIC_IS_NINF(base))
+ result = !less; /* normal > NINF */
+ else
+ result = less; /* normal < PINF */
+ }
else
{
/*
@@ -2264,8 +2567,8 @@ hash_numeric(PG_FUNCTION_ARGS)
int hash_len;
NumericDigit *digits;
- /* If it's NaN, don't try to hash the rest of the fields */
- if (NUMERIC_IS_NAN(key))
+ /* If it's NaN or infinity, don't try to hash the rest of the fields */
+ if (NUMERIC_IS_SPECIAL(key))
PG_RETURN_UINT32(0);
weight = NUMERIC_WEIGHT(key);
@@ -2345,7 +2648,8 @@ hash_numeric_extended(PG_FUNCTION_ARGS)
int hash_len;
NumericDigit *digits;
- if (NUMERIC_IS_NAN(key))
+ /* If it's NaN or infinity, don't try to hash the rest of the fields */
+ if (NUMERIC_IS_SPECIAL(key))
PG_RETURN_UINT64(seed);
weight = NUMERIC_WEIGHT(key);
@@ -2429,10 +2733,32 @@ numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- return make_result(&const_nan);
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ return make_result(&const_nan);
+ if (NUMERIC_IS_PINF(num1))
+ {
+ if (NUMERIC_IS_NINF(num2))
+ return make_result(&const_nan); /* Inf + -Inf */
+ else
+ return make_result(&const_pinf);
+ }
+ if (NUMERIC_IS_NINF(num1))
+ {
+ if (NUMERIC_IS_PINF(num2))
+ return make_result(&const_nan); /* -Inf + Inf */
+ else
+ return make_result(&const_ninf);
+ }
+ /* by here, num1 must be finite, so num2 is not */
+ if (NUMERIC_IS_PINF(num2))
+ return make_result(&const_pinf);
+ Assert(NUMERIC_IS_NINF(num2));
+ return make_result(&const_ninf);
+ }
/*
* Unpack the values, let add_var() compute the result and return it.
@@ -2485,10 +2811,32 @@ numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- return make_result(&const_nan);
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ return make_result(&const_nan);
+ if (NUMERIC_IS_PINF(num1))
+ {
+ if (NUMERIC_IS_PINF(num2))
+ return make_result(&const_nan); /* Inf - Inf */
+ else
+ return make_result(&const_pinf);
+ }
+ if (NUMERIC_IS_NINF(num1))
+ {
+ if (NUMERIC_IS_NINF(num2))
+ return make_result(&const_nan); /* -Inf - -Inf */
+ else
+ return make_result(&const_ninf);
+ }
+ /* by here, num1 must be finite, so num2 is not */
+ if (NUMERIC_IS_PINF(num2))
+ return make_result(&const_ninf);
+ Assert(NUMERIC_IS_NINF(num2));
+ return make_result(&const_pinf);
+ }
/*
* Unpack the values, let sub_var() compute the result and return it.
@@ -2541,10 +2889,64 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- return make_result(&const_nan);
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ return make_result(&const_nan);
+ if (NUMERIC_IS_PINF(num1))
+ {
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ return make_result(&const_nan); /* Inf * 0 */
+ case 1:
+ return make_result(&const_pinf);
+ case -1:
+ return make_result(&const_ninf);
+ }
+ Assert(false);
+ }
+ if (NUMERIC_IS_NINF(num1))
+ {
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ return make_result(&const_nan); /* -Inf * 0 */
+ case 1:
+ return make_result(&const_ninf);
+ case -1:
+ return make_result(&const_pinf);
+ }
+ Assert(false);
+ }
+ /* by here, num1 must be finite, so num2 is not */
+ if (NUMERIC_IS_PINF(num2))
+ {
+ switch (numeric_sign_internal(num1))
+ {
+ case 0:
+ return make_result(&const_nan); /* 0 * Inf */
+ case 1:
+ return make_result(&const_pinf);
+ case -1:
+ return make_result(&const_ninf);
+ }
+ Assert(false);
+ }
+ Assert(NUMERIC_IS_NINF(num2));
+ switch (numeric_sign_internal(num1))
+ {
+ case 0:
+ return make_result(&const_nan); /* 0 * -Inf */
+ case 1:
+ return make_result(&const_ninf);
+ case -1:
+ return make_result(&const_pinf);
+ }
+ Assert(false);
+ }
/*
* Unpack the values, let mul_var() compute the result and return it.
@@ -2605,10 +3007,67 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
*have_error = false;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- return make_result(&const_nan);
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ return make_result(&const_nan);
+ if (NUMERIC_IS_PINF(num1))
+ {
+ if (NUMERIC_IS_SPECIAL(num2))
+ return make_result(&const_nan); /* Inf / [-]Inf */
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ if (have_error)
+ {
+ *have_error = true;
+ return NULL;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ break;
+ case 1:
+ return make_result(&const_pinf);
+ case -1:
+ return make_result(&const_ninf);
+ }
+ Assert(false);
+ }
+ if (NUMERIC_IS_NINF(num1))
+ {
+ if (NUMERIC_IS_SPECIAL(num2))
+ return make_result(&const_nan); /* -Inf / [-]Inf */
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ if (have_error)
+ {
+ *have_error = true;
+ return NULL;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ break;
+ case 1:
+ return make_result(&const_ninf);
+ case -1:
+ return make_result(&const_pinf);
+ }
+ Assert(false);
+ }
+ /* by here, num1 must be finite, so num2 is not */
+
+ /*
+ * POSIX would have us return zero or minus zero if num1 is zero, and
+ * otherwise throw an underflow error. But the numeric type doesn't
+ * really do underflow, so let's just return zero.
+ */
+ return make_result(&const_zero);
+ }
/*
* Unpack the arguments
@@ -2661,10 +3120,57 @@ numeric_div_trunc(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_PINF(num1))
+ {
+ if (NUMERIC_IS_SPECIAL(num2))
+ PG_RETURN_NUMERIC(make_result(&const_nan)); /* Inf / [-]Inf */
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ break;
+ case 1:
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ case -1:
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+ }
+ Assert(false);
+ }
+ if (NUMERIC_IS_NINF(num1))
+ {
+ if (NUMERIC_IS_SPECIAL(num2))
+ PG_RETURN_NUMERIC(make_result(&const_nan)); /* -Inf / [-]Inf */
+ switch (numeric_sign_internal(num2))
+ {
+ case 0:
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ break;
+ case 1:
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+ case -1:
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ }
+ Assert(false);
+ }
+ /* by here, num1 must be finite, so num2 is not */
+
+ /*
+ * POSIX would have us return zero or minus zero if num1 is zero, and
+ * otherwise throw an underflow error. But the numeric type doesn't
+ * really do underflow, so let's just return zero.
+ */
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ }
/*
* Unpack the arguments
@@ -2723,8 +3229,34 @@ numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
if (have_error)
*have_error = false;
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- return make_result(&const_nan);
+ /*
+ * Handle NaN and infinities. We follow POSIX fmod() on this, except that
+ * POSIX treats x-is-infinite and y-is-zero identically, raising EDOM and
+ * returning NaN. We choose to throw error only for y-is-zero.
+ */
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ return make_result(&const_nan);
+ if (NUMERIC_IS_INF(num1))
+ {
+ if (numeric_sign_internal(num2) == 0)
+ {
+ if (have_error)
+ {
+ *have_error = true;
+ return NULL;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ }
+ /* Inf % any nonzero = NaN */
+ return make_result(&const_nan);
+ }
+ /* num2 must be [-]Inf; result is num1 regardless of sign of num2 */
+ return duplicate_numeric(num1);
+ }
init_var_from_num(num1, &arg1);
init_var_from_num(num2, &arg2);
@@ -2763,10 +3295,10 @@ numeric_inc(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
/*
* Compute the result and return it
@@ -2850,9 +3382,10 @@ numeric_gcd(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities: we consider the result to be NaN in all such
+ * cases.
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
PG_RETURN_NUMERIC(make_result(&const_nan));
/*
@@ -2892,9 +3425,10 @@ numeric_lcm(PG_FUNCTION_ARGS)
Numeric res;
/*
- * Handle NaN
+ * Handle NaN and infinities: we consider the result to be NaN in all such
+ * cases.
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
PG_RETURN_NUMERIC(make_result(&const_nan));
/*
@@ -3003,10 +3537,18 @@ numeric_sqrt(PG_FUNCTION_ARGS)
int rscale;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ /* error should match that in sqrt_var() */
+ if (NUMERIC_IS_NINF(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
+ errmsg("cannot take square root of a negative number")));
+ /* For NAN or PINF, just duplicate the input */
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
+ }
/*
* Unpack the argument and determine the result scale. We choose a scale
@@ -3054,10 +3596,16 @@ numeric_exp(PG_FUNCTION_ARGS)
double val;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ /* Per POSIX, exp(-Inf) is zero */
+ if (NUMERIC_IS_NINF(num))
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ /* For NAN or PINF, just duplicate the input */
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
+ }
/*
* Unpack the argument and determine the result scale. We choose a scale
@@ -3115,10 +3663,17 @@ numeric_ln(PG_FUNCTION_ARGS)
int rscale;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_NINF(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
+ errmsg("cannot take logarithm of a negative number")));
+ /* For NAN or PINF, just duplicate the input */
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
+ }
init_var_from_num(num, &arg);
init_var(&result);
@@ -3157,10 +3712,39 @@ numeric_log(PG_FUNCTION_ARGS)
NumericVar result;
/*
- * Handle NaN
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
+ {
+ int sign1,
+ sign2;
+
+ if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ /* fail on negative inputs including -Inf, as log_var would */
+ sign1 = numeric_sign_internal(num1);
+ sign2 = numeric_sign_internal(num2);
+ if (sign1 < 0 || sign2 < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
+ errmsg("cannot take logarithm of a negative number")));
+ /* fail on zero inputs, as log_var would */
+ if (sign1 == 0 || sign2 == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
+ errmsg("cannot take logarithm of zero")));
+ if (NUMERIC_IS_PINF(num1))
+ {
+ /* log(Inf, Inf) reduces to Inf/Inf, so it's NaN */
+ if (NUMERIC_IS_PINF(num2))
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ /* log(Inf, finite-positive) is zero (we don't throw underflow) */
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ }
+ Assert(NUMERIC_IS_PINF(num2));
+ /* log(finite-positive, Inf) is Inf */
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ }
/*
* Initialize things
@@ -3186,7 +3770,7 @@ numeric_log(PG_FUNCTION_ARGS)
/*
* numeric_power() -
*
- * Raise b to the power of x
+ * Raise x to the power of y
*/
Datum
numeric_power(PG_FUNCTION_ARGS)
@@ -3196,61 +3780,171 @@ numeric_power(PG_FUNCTION_ARGS)
Numeric res;
NumericVar arg1;
NumericVar arg2;
- NumericVar arg2_trunc;
NumericVar result;
+ int sign1,
+ sign2;
/*
- * Handle NaN cases. We follow the POSIX spec for pow(3), which says that
- * NaN ^ 0 = 1, and 1 ^ NaN = 1, while all other cases with NaN inputs
- * yield NaN (with no error).
+ * Handle NaN and infinities
*/
- if (NUMERIC_IS_NAN(num1))
+ if (NUMERIC_IS_SPECIAL(num1) || NUMERIC_IS_SPECIAL(num2))
{
- if (!NUMERIC_IS_NAN(num2))
+ /*
+ * We follow the POSIX spec for pow(3), which says that NaN ^ 0 = 1,
+ * and 1 ^ NaN = 1, while all other cases with NaN inputs yield NaN
+ * (with no error).
+ */
+ if (NUMERIC_IS_NAN(num1))
+ {
+ if (!NUMERIC_IS_SPECIAL(num2))
+ {
+ init_var_from_num(num2, &arg2);
+ if (cmp_var(&arg2, &const_zero) == 0)
+ PG_RETURN_NUMERIC(make_result(&const_one));
+ }
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ }
+ if (NUMERIC_IS_NAN(num2))
+ {
+ if (!NUMERIC_IS_SPECIAL(num1))
+ {
+ init_var_from_num(num1, &arg1);
+ if (cmp_var(&arg1, &const_one) == 0)
+ PG_RETURN_NUMERIC(make_result(&const_one));
+ }
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ }
+ /* At least one input is infinite, but error rules still apply */
+ sign1 = numeric_sign_internal(num1);
+ sign2 = numeric_sign_internal(num2);
+ if (sign1 == 0 && sign2 < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
+ errmsg("zero raised to a negative power is undefined")));
+ if (sign1 < 0 && !numeric_is_integral(num2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
+ errmsg("a negative number raised to a non-integer power yields a complex result")));
+
+ /*
+ * POSIX gives this series of rules for pow(3) with infinite inputs:
+ *
+ * For any value of y, if x is +1, 1.0 shall be returned.
+ */
+ if (!NUMERIC_IS_SPECIAL(num1))
{
- init_var_from_num(num2, &arg2);
- if (cmp_var(&arg2, &const_zero) == 0)
+ init_var_from_num(num1, &arg1);
+ if (cmp_var(&arg1, &const_one) == 0)
PG_RETURN_NUMERIC(make_result(&const_one));
}
- PG_RETURN_NUMERIC(make_result(&const_nan));
- }
- if (NUMERIC_IS_NAN(num2))
- {
- init_var_from_num(num1, &arg1);
- if (cmp_var(&arg1, &const_one) == 0)
+
+ /*
+ * For any value of x, if y is [-]0, 1.0 shall be returned.
+ */
+ if (sign2 == 0)
PG_RETURN_NUMERIC(make_result(&const_one));
- PG_RETURN_NUMERIC(make_result(&const_nan));
- }
- /*
- * Initialize things
- */
- init_var(&arg2_trunc);
- init_var(&result);
- init_var_from_num(num1, &arg1);
- init_var_from_num(num2, &arg2);
+ /*
+ * For any odd integer value of y > 0, if x is [-]0, [-]0 shall be
+ * returned. For y > 0 and not an odd integer, if x is [-]0, +0 shall
+ * be returned. (Since we don't deal in minus zero, we need not
+ * distinguish these two cases.)
+ */
+ if (sign1 == 0 && sign2 > 0)
+ PG_RETURN_NUMERIC(make_result(&const_zero));
- set_var_from_var(&arg2, &arg2_trunc);
- trunc_var(&arg2_trunc, 0);
+ /*
+ * If x is -1, and y is [-]Inf, 1.0 shall be returned.
+ *
+ * For |x| < 1, if y is -Inf, +Inf shall be returned.
+ *
+ * For |x| > 1, if y is -Inf, +0 shall be returned.
+ *
+ * For |x| < 1, if y is +Inf, +0 shall be returned.
+ *
+ * For |x| > 1, if y is +Inf, +Inf shall be returned.
+ */
+ if (NUMERIC_IS_INF(num2))
+ {
+ bool abs_x_gt_one;
+
+ if (NUMERIC_IS_SPECIAL(num1))
+ abs_x_gt_one = true; /* x is either Inf or -Inf */
+ else
+ {
+ init_var_from_num(num1, &arg1);
+ if (cmp_var(&arg1, &const_minus_one) == 0)
+ PG_RETURN_NUMERIC(make_result(&const_one));
+ arg1.sign = NUMERIC_POS; /* now arg1 = abs(x) */
+ abs_x_gt_one = (cmp_var(&arg1, &const_one) > 0);
+ }
+ if (abs_x_gt_one == (sign2 > 0))
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ else
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ }
+
+ /*
+ * For y < 0, if x is +Inf, +0 shall be returned.
+ *
+ * For y > 0, if x is +Inf, +Inf shall be returned.
+ */
+ if (NUMERIC_IS_PINF(num1))
+ {
+ if (sign2 > 0)
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ else
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+ }
+
+ Assert(NUMERIC_IS_NINF(num1));
+
+ /*
+ * For y an odd integer < 0, if x is -Inf, -0 shall be returned. For
+ * y < 0 and not an odd integer, if x is -Inf, +0 shall be returned.
+ * (Again, we need not distinguish these two cases.)
+ */
+ if (sign2 < 0)
+ PG_RETURN_NUMERIC(make_result(&const_zero));
+
+ /*
+ * For y an odd integer > 0, if x is -Inf, -Inf shall be returned. For
+ * y > 0 and not an odd integer, if x is -Inf, +Inf shall be returned.
+ */
+ init_var_from_num(num2, &arg2);
+ if (arg2.ndigits > 0 && arg2.ndigits == arg2.weight + 1 &&
+ (arg2.digits[arg2.ndigits - 1] & 1))
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+ else
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ }
/*
* The SQL spec requires that we emit a particular SQLSTATE error code for
* certain error conditions. Specifically, we don't return a
* divide-by-zero error code for 0 ^ -1.
*/
- if (cmp_var(&arg1, &const_zero) == 0 &&
- cmp_var(&arg2, &const_zero) < 0)
+ sign1 = numeric_sign_internal(num1);
+ sign2 = numeric_sign_internal(num2);
+
+ if (sign1 == 0 && sign2 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("zero raised to a negative power is undefined")));
- if (cmp_var(&arg1, &const_zero) < 0 &&
- cmp_var(&arg2, &arg2_trunc) != 0)
+ if (sign1 < 0 && !numeric_is_integral(num2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("a negative number raised to a non-integer power yields a complex result")));
/*
+ * Initialize things
+ */
+ init_var(&result);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
+
+ /*
* Call power_var() to compute and return the result; note it handles
* scale selection itself.
*/
@@ -3259,7 +3953,6 @@ numeric_power(PG_FUNCTION_ARGS)
res = make_result(&result);
free_var(&result);
- free_var(&arg2_trunc);
PG_RETURN_NUMERIC(res);
}
@@ -3274,7 +3967,7 @@ numeric_scale(PG_FUNCTION_ARGS)
{
Numeric num = PG_GETARG_NUMERIC(0);
- if (NUMERIC_IS_NAN(num))
+ if (NUMERIC_IS_SPECIAL(num))
PG_RETURN_NULL();
PG_RETURN_INT32(NUMERIC_DSCALE(num));
@@ -3341,7 +4034,7 @@ numeric_min_scale(PG_FUNCTION_ARGS)
NumericVar arg;
int min_scale;
- if (NUMERIC_IS_NAN(num))
+ if (NUMERIC_IS_SPECIAL(num))
PG_RETURN_NULL();
init_var_from_num(num, &arg);
@@ -3361,8 +4054,8 @@ numeric_trim_scale(PG_FUNCTION_ARGS)
Numeric res;
NumericVar result;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (NUMERIC_IS_SPECIAL(num))
+ PG_RETURN_NUMERIC(duplicate_numeric(num));
init_var_from_num(num, &result);
result.dscale = get_min_scale(&result);
@@ -3408,8 +4101,7 @@ numeric_int4_opt_error(Numeric num, bool *have_error)
if (have_error)
*have_error = false;
- /* XXX would it be better to return NULL? */
- if (NUMERIC_IS_NAN(num))
+ if (NUMERIC_IS_SPECIAL(num))
{
if (have_error)
{
@@ -3418,9 +4110,14 @@ numeric_int4_opt_error(Numeric num, bool *have_error)
}
else
{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert NaN to integer")));
+ if (NUMERIC_IS_NAN(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert NaN to integer")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to integer")));
}
}
@@ -3499,11 +4196,17 @@ numeric_int8(PG_FUNCTION_ARGS)
NumericVar x;
int64 result;
- /* XXX would it be better to return NULL? */
- if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert NaN to bigint")));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_NAN(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert NaN to bigint")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to bigint")));
+ }
/* Convert to variable format and thence to int8 */
init_var_from_num(num, &x);
@@ -3544,11 +4247,17 @@ numeric_int2(PG_FUNCTION_ARGS)
int64 val;
int16 result;
- /* XXX would it be better to return NULL? */
- if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert NaN to smallint")));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_NAN(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert NaN to smallint")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to smallint")));
+ }
/* Convert to variable format and thence to int8 */
init_var_from_num(num, &x);
@@ -3583,9 +4292,12 @@ float8_numeric(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(make_result(&const_nan));
if (isinf(val))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert infinity to numeric")));
+ {
+ if (val < 0)
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+ else
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ }
snprintf(buf, sizeof(buf), "%.*g", DBL_DIG, val);
@@ -3609,8 +4321,15 @@ numeric_float8(PG_FUNCTION_ARGS)
char *tmp;
Datum result;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_FLOAT8(get_float8_nan());
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ PG_RETURN_FLOAT8(get_float8_infinity());
+ else if (NUMERIC_IS_NINF(num))
+ PG_RETURN_FLOAT8(-get_float8_infinity());
+ else
+ PG_RETURN_FLOAT8(get_float8_nan());
+ }
tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(num)));
@@ -3634,10 +4353,22 @@ numeric_float8_no_overflow(PG_FUNCTION_ARGS)
Numeric num = PG_GETARG_NUMERIC(0);
double val;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_FLOAT8(get_float8_nan());
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ val = HUGE_VAL;
+ else if (NUMERIC_IS_NINF(num))
+ val = -HUGE_VAL;
+ else
+ val = get_float8_nan();
+ }
+ else
+ {
+ NumericVar x;
- val = numeric_to_double_no_overflow(num);
+ init_var_from_num(num, &x);
+ val = numericvar_to_double_no_overflow(&x);
+ }
PG_RETURN_FLOAT8(val);
}
@@ -3654,9 +4385,12 @@ float4_numeric(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(make_result(&const_nan));
if (isinf(val))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert infinity to numeric")));
+ {
+ if (val < 0)
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+ else
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ }
snprintf(buf, sizeof(buf), "%.*g", FLT_DIG, val);
@@ -3680,8 +4414,15 @@ numeric_float4(PG_FUNCTION_ARGS)
char *tmp;
Datum result;
- if (NUMERIC_IS_NAN(num))
- PG_RETURN_FLOAT4(get_float4_nan());
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_PINF(num))
+ PG_RETURN_FLOAT4(get_float4_infinity());
+ else if (NUMERIC_IS_NINF(num))
+ PG_RETURN_FLOAT4(-get_float4_infinity());
+ else
+ PG_RETURN_FLOAT4(get_float4_nan());
+ }
tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(num)));
@@ -3701,10 +4442,17 @@ numeric_pg_lsn(PG_FUNCTION_ARGS)
NumericVar x;
XLogRecPtr result;
- if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert NaN to pg_lsn")));
+ if (NUMERIC_IS_SPECIAL(num))
+ {
+ if (NUMERIC_IS_NAN(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert NaN to pg_lsn")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to pg_lsn")));
+ }
/* Convert to variable format and thence to pg_lsn */
init_var_from_num(num, &x);
@@ -3741,9 +4489,15 @@ typedef struct NumericAggState
NumericSumAccum sumX2; /* sum of squares of processed numbers */
int maxScale; /* maximum scale seen so far */
int64 maxScaleCount; /* number of values seen with maximum scale */
- int64 NaNcount; /* count of NaN values (not included in N!) */
+ /* These counts are *not* included in N! Use NA_TOTAL_COUNT() as needed */
+ int64 NaNcount; /* count of NaN values */
+ int64 pInfcount; /* count of +Inf values */
+ int64 nInfcount; /* count of -Inf values */
} NumericAggState;
+#define NA_TOTAL_COUNT(na) \
+ ((na)->N + (na)->NaNcount + (na)->pInfcount + (na)->nInfcount)
+
/*
* Prepare state data for a numeric aggregate function that needs to compute
* sum, count and optionally sum of squares of the input.
@@ -3795,10 +4549,15 @@ do_numeric_accum(NumericAggState *state, Numeric newval)
NumericVar X2;
MemoryContext old_context;
- /* Count NaN inputs separately from all else */
- if (NUMERIC_IS_NAN(newval))
+ /* Count NaN/infinity inputs separately from all else */
+ if (NUMERIC_IS_SPECIAL(newval))
{
- state->NaNcount++;
+ if (NUMERIC_IS_PINF(newval))
+ state->pInfcount++;
+ else if (NUMERIC_IS_NINF(newval))
+ state->nInfcount++;
+ else
+ state->NaNcount++;
return;
}
@@ -3860,10 +4619,15 @@ do_numeric_discard(NumericAggState *state, Numeric newval)
NumericVar X2;
MemoryContext old_context;
- /* Count NaN inputs separately from all else */
- if (NUMERIC_IS_NAN(newval))
+ /* Count NaN/infinity inputs separately from all else */
+ if (NUMERIC_IS_SPECIAL(newval))
{
- state->NaNcount--;
+ if (NUMERIC_IS_PINF(newval))
+ state->pInfcount--;
+ else if (NUMERIC_IS_NINF(newval))
+ state->nInfcount--;
+ else
+ state->NaNcount--;
return true;
}
@@ -3986,6 +4750,8 @@ numeric_combine(PG_FUNCTION_ARGS)
state1 = makeNumericAggStateCurrentContext(true);
state1->N = state2->N;
state1->NaNcount = state2->NaNcount;
+ state1->pInfcount = state2->pInfcount;
+ state1->nInfcount = state2->nInfcount;
state1->maxScale = state2->maxScale;
state1->maxScaleCount = state2->maxScaleCount;
@@ -3999,6 +4765,8 @@ numeric_combine(PG_FUNCTION_ARGS)
state1->N += state2->N;
state1->NaNcount += state2->NaNcount;
+ state1->pInfcount += state2->pInfcount;
+ state1->nInfcount += state2->nInfcount;
if (state2->N > 0)
{
@@ -4074,6 +4842,8 @@ numeric_avg_combine(PG_FUNCTION_ARGS)
state1 = makeNumericAggStateCurrentContext(false);
state1->N = state2->N;
state1->NaNcount = state2->NaNcount;
+ state1->pInfcount = state2->pInfcount;
+ state1->nInfcount = state2->nInfcount;
state1->maxScale = state2->maxScale;
state1->maxScaleCount = state2->maxScaleCount;
@@ -4086,6 +4856,8 @@ numeric_avg_combine(PG_FUNCTION_ARGS)
state1->N += state2->N;
state1->NaNcount += state2->NaNcount;
+ state1->pInfcount += state2->pInfcount;
+ state1->nInfcount += state2->nInfcount;
if (state2->N > 0)
{
@@ -4164,6 +4936,12 @@ numeric_avg_serialize(PG_FUNCTION_ARGS)
/* NaNcount */
pq_sendint64(&buf, state->NaNcount);
+ /* pInfcount */
+ pq_sendint64(&buf, state->pInfcount);
+
+ /* nInfcount */
+ pq_sendint64(&buf, state->nInfcount);
+
result = pq_endtypsend(&buf);
PG_RETURN_BYTEA_P(result);
@@ -4218,6 +4996,12 @@ numeric_avg_deserialize(PG_FUNCTION_ARGS)
/* NaNcount */
result->NaNcount = pq_getmsgint64(&buf);
+ /* pInfcount */
+ result->pInfcount = pq_getmsgint64(&buf);
+
+ /* nInfcount */
+ result->nInfcount = pq_getmsgint64(&buf);
+
pq_getmsgend(&buf);
pfree(buf.data);
@@ -4286,6 +5070,12 @@ numeric_serialize(PG_FUNCTION_ARGS)
/* NaNcount */
pq_sendint64(&buf, state->NaNcount);
+ /* pInfcount */
+ pq_sendint64(&buf, state->pInfcount);
+
+ /* nInfcount */
+ pq_sendint64(&buf, state->nInfcount);
+
result = pq_endtypsend(&buf);
PG_RETURN_BYTEA_P(result);
@@ -4349,6 +5139,12 @@ numeric_deserialize(PG_FUNCTION_ARGS)
/* NaNcount */
result->NaNcount = pq_getmsgint64(&buf);
+ /* pInfcount */
+ result->pInfcount = pq_getmsgint64(&buf);
+
+ /* nInfcount */
+ result->nInfcount = pq_getmsgint64(&buf);
+
pq_getmsgend(&buf);
pfree(buf.data);
@@ -5141,12 +5937,20 @@ numeric_avg(PG_FUNCTION_ARGS)
state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
/* If there were no non-null inputs, return NULL */
- if (state == NULL || (state->N + state->NaNcount) == 0)
+ if (state == NULL || NA_TOTAL_COUNT(state) == 0)
PG_RETURN_NULL();
if (state->NaNcount > 0) /* there was at least one NaN input */
PG_RETURN_NUMERIC(make_result(&const_nan));
+ /* adding plus and minus infinities gives NaN */
+ if (state->pInfcount > 0 && state->nInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (state->pInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ if (state->nInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+
N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
init_var(&sumX_var);
@@ -5167,12 +5971,20 @@ numeric_sum(PG_FUNCTION_ARGS)
state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
/* If there were no non-null inputs, return NULL */
- if (state == NULL || (state->N + state->NaNcount) == 0)
+ if (state == NULL || NA_TOTAL_COUNT(state) == 0)
PG_RETURN_NULL();
if (state->NaNcount > 0) /* there was at least one NaN input */
PG_RETURN_NUMERIC(make_result(&const_nan));
+ /* adding plus and minus infinities gives NaN */
+ if (state->pInfcount > 0 && state->nInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+ if (state->pInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_pinf));
+ if (state->nInfcount > 0)
+ PG_RETURN_NUMERIC(make_result(&const_ninf));
+
init_var(&sumX_var);
accum_sum_final(&state->sumX, &sumX_var);
result = make_result(&sumX_var);
@@ -5208,9 +6020,9 @@ numeric_stddev_internal(NumericAggState *state,
/*
* Sample stddev and variance are undefined when N <= 1; population stddev
* is undefined when N == 0. Return NULL in either case (note that NaNs
- * count as normal inputs for this purpose).
+ * and infinities count as normal inputs for this purpose).
*/
- if (state == NULL || (totCount = state->N + state->NaNcount) == 0)
+ if (state == NULL || (totCount = NA_TOTAL_COUNT(state)) == 0)
{
*is_null = true;
return NULL;
@@ -5225,9 +6037,10 @@ numeric_stddev_internal(NumericAggState *state,
*is_null = false;
/*
- * Deal with NaN inputs.
+ * Deal with NaN and infinity cases. By analogy to the behavior of the
+ * float8 functions, any infinity input produces NaN output.
*/
- if (state->NaNcount > 0)
+ if (state->NaNcount > 0 || state->pInfcount > 0 || state->nInfcount > 0)
return make_result(&const_nan);
/* OK, normal calculation applies */
@@ -5870,6 +6683,12 @@ dump_numeric(const char *str, Numeric num)
case NUMERIC_NAN:
printf("NaN");
break;
+ case NUMERIC_PINF:
+ printf("Infinity");
+ break;
+ case NUMERIC_NINF:
+ printf("-Infinity");
+ break;
default:
printf("SIGN=0x%x", NUMERIC_SIGN(num));
break;
@@ -5901,6 +6720,12 @@ dump_var(const char *str, NumericVar *var)
case NUMERIC_NAN:
printf("NaN");
break;
+ case NUMERIC_PINF:
+ printf("Infinity");
+ break;
+ case NUMERIC_NINF:
+ printf("-Infinity");
+ break;
default:
printf("SIGN=0x%x", var->sign);
break;
@@ -5918,8 +6743,9 @@ dump_var(const char *str, NumericVar *var)
*
* Local functions follow
*
- * In general, these do not support NaNs --- callers must eliminate
- * the possibility of NaN first. (make_result() is an exception.)
+ * In general, these do not support "special" (NaN or infinity) inputs;
+ * callers should handle those possibilities first.
+ * (There are one or two exceptions, noted in their header comments.)
*
* ----------------------------------------------------------------------
*/
@@ -5979,9 +6805,9 @@ zero_var(NumericVar *var)
*
* Parse a string and put the number into a variable
*
- * This function does not handle leading or trailing spaces, and it doesn't
- * accept "NaN" either. It returns the end+1 position so that caller can
- * check for trailing spaces/garbage if deemed necessary.
+ * This function does not handle leading or trailing spaces. It returns
+ * the end+1 position parsed, so that caller can check for trailing
+ * spaces/garbage if deemed necessary.
*
* cp is the place to actually start parsing; str is what to use in error
* reports. (Typically cp would be the same except advanced over spaces.)
@@ -6456,12 +7282,28 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
/*
+ * duplicate_numeric() - copy a packed-format Numeric
+ *
+ * This will handle NaN and Infinity cases.
+ */
+static Numeric
+duplicate_numeric(Numeric num)
+{
+ Numeric res;
+
+ res = (Numeric) palloc(VARSIZE(num));
+ memcpy(res, num, VARSIZE(num));
+ return res;
+}
+
+/*
* make_result_opt_error() -
*
* Create the packed db numeric format in palloc()'d memory from
- * a variable. If "*have_error" flag is provided, on error it's set to
- * true, NULL returned. This is helpful when caller need to handle errors
- * by itself.
+ * a variable. This will handle NaN and Infinity cases.
+ *
+ * If "have_error" isn't NULL, on overflow *have_error is set to true and
+ * NULL is returned. This is helpful when caller needs to handle errors.
*/
static Numeric
make_result_opt_error(const NumericVar *var, bool *have_error)
@@ -6476,12 +7318,22 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
if (have_error)
*have_error = false;
- if (sign == NUMERIC_NAN)
+ if ((sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL)
{
+ /*
+ * Verify valid special value. This could be just an Assert, perhaps,
+ * but it seems worthwhile to expend a few cycles to ensure that we
+ * never write any nonzero reserved bits to disk.
+ */
+ if (!(sign == NUMERIC_NAN ||
+ sign == NUMERIC_PINF ||
+ sign == NUMERIC_NINF))
+ elog(ERROR, "invalid numeric sign value 0x%x", sign);
+
result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);
SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT);
- result->choice.n_header = NUMERIC_NAN;
+ result->choice.n_header = sign;
/* the header word is all we need */
dump_numeric("make_result()", result);
@@ -6572,8 +7424,8 @@ make_result(const NumericVar *var)
/*
* apply_typmod() -
*
- * Do bounds checking and rounding according to the attributes
- * typmod field.
+ * Do bounds checking and rounding according to the specified typmod.
+ * Note that this is only applied to normal finite values.
*/
static void
apply_typmod(NumericVar *var, int32 typmod)
@@ -6647,6 +7499,45 @@ apply_typmod(NumericVar *var, int32 typmod)
}
/*
+ * apply_typmod_special() -
+ *
+ * Do bounds checking according to the specified typmod, for an Inf or NaN.
+ * For convenience of most callers, the value is presented in packed form.
+ */
+static void
+apply_typmod_special(Numeric num, int32 typmod)
+{
+ int precision;
+ int scale;
+
+ Assert(NUMERIC_IS_SPECIAL(num)); /* caller error if not */
+
+ /*
+ * NaN is allowed regardless of the typmod; that's rather dubious perhaps,
+ * but it's a longstanding behavior. Inf is rejected if we have any
+ * typmod restriction, since an infinity shouldn't be claimed to fit in
+ * any finite number of digits.
+ */
+ if (NUMERIC_IS_NAN(num))
+ return;
+
+ /* Do nothing if we have a default typmod (-1) */
+ if (typmod < (int32) (VARHDRSZ))
+ return;
+
+ typmod -= VARHDRSZ;
+ precision = (typmod >> 16) & 0xffff;
+ scale = typmod & 0xffff;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("numeric field overflow"),
+ errdetail("A field with precision %d, scale %d cannot hold an infinite value.",
+ precision, scale)));
+}
+
+
+/*
* Convert numeric to int8, rounding if needed.
*
* If overflow, return false (no error is raised). Return true if okay.
@@ -6961,36 +7852,9 @@ int128_to_numericvar(int128 val, NumericVar *var)
#endif
/*
- * Convert numeric to float8; if out of range, return +/- HUGE_VAL
+ * Convert a NumericVar to float8; if out of range, return +/- HUGE_VAL
*/
static double
-numeric_to_double_no_overflow(Numeric num)
-{
- char *tmp;
- double val;
- char *endptr;
-
- tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
- NumericGetDatum(num)));
-
- /* unlike float8in, we ignore ERANGE from strtod */
- val = strtod(tmp, &endptr);
- if (*endptr != '\0')
- {
- /* shouldn't happen ... */
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type %s: \"%s\"",
- "double precision", tmp)));
- }
-
- pfree(tmp);
-
- return val;
-}
-
-/* As above, but work from a NumericVar */
-static double
numericvar_to_double_no_overflow(const NumericVar *var)
{
char *tmp;