diff options
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r-- | src/backend/utils/adt/numeric.c | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 5b34badd5bc..6f400729713 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -2165,6 +2165,81 @@ cmp_numerics(Numeric num1, Numeric num2) return result; } +/* + * in_range support function for numeric. + */ +Datum +in_range_numeric_numeric(PG_FUNCTION_ARGS) +{ + Numeric val = PG_GETARG_NUMERIC(0); + Numeric base = PG_GETARG_NUMERIC(1); + Numeric offset = PG_GETARG_NUMERIC(2); + bool sub = PG_GETARG_BOOL(3); + bool less = PG_GETARG_BOOL(4); + bool result; + + /* + * Reject negative 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) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PRECEDING_FOLLOWING_SIZE), + errmsg("invalid preceding or following size in window function"))); + + /* + * Deal with cases where val and/or base is NaN, following the rule that + * NaN sorts after non-NaN (cf cmp_numerics). The offset cannot affect + * the conclusion. + */ + if (NUMERIC_IS_NAN(val)) + { + if (NUMERIC_IS_NAN(base)) + result = true; /* NAN = NAN */ + else + result = !less; /* NAN > non-NAN */ + } + else if (NUMERIC_IS_NAN(base)) + { + result = less; /* non-NAN < NAN */ + } + else + { + /* + * Otherwise go ahead and compute base +/- offset. While it's + * possible for this to overflow the numeric format, it's unlikely + * enough that we don't take measures to prevent it. + */ + NumericVar valv; + NumericVar basev; + NumericVar offsetv; + NumericVar sum; + + init_var_from_num(val, &valv); + init_var_from_num(base, &basev); + init_var_from_num(offset, &offsetv); + init_var(&sum); + + if (sub) + sub_var(&basev, &offsetv, &sum); + else + add_var(&basev, &offsetv, &sum); + + if (less) + result = (cmp_var(&valv, &sum) <= 0); + else + result = (cmp_var(&valv, &sum) >= 0); + + free_var(&sum); + } + + PG_FREE_IF_COPY(val, 0); + PG_FREE_IF_COPY(base, 1); + PG_FREE_IF_COPY(offset, 2); + + PG_RETURN_BOOL(result); +} + Datum hash_numeric(PG_FUNCTION_ARGS) { |