aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-02-24 13:23:38 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2018-02-24 13:23:38 -0500
commit8b29e88cdce17705f0b2c43e50219ce1d7d2f603 (patch)
treeb8a8fefc8ac1643e760d9bbf0f03ce4c2d8689b0 /src/backend/utils/adt/numeric.c
parent081bfc19b3b7914b78eb44e00af9dd45325dda3e (diff)
downloadpostgresql-8b29e88cdce17705f0b2c43e50219ce1d7d2f603.tar.gz
postgresql-8b29e88cdce17705f0b2c43e50219ce1d7d2f603.zip
Add window RANGE support for float4, float8, numeric.
Commit 0a459cec9 left this for later, but since time's running out, I went ahead and took care of it. There are more data types that somebody might someday want RANGE support for, but this is enough to satisfy all expectations of the SQL standard, which just says that "numeric, datetime, and interval" types should have RANGE support.
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c75
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)
{