Commit a4faef8f authored by Tom Lane's avatar Tom Lane

Fix some corner cases for window ranges with infinite offsets.

Many situations where the offset is infinity were not handled sanely.
We should generally allow the val versus base +/- offset comparison to
proceed according to the normal rules of IEEE arithmetic; however, we
must do something special for the corner cases where base +/- offset
would produce NaN due to subtracting two like-signed infinities.
That corresponds to asking which values infinitely precede +inf or
infinitely follow -inf, which should certainly be true of any finite
value or of the opposite-signed infinity.  After some discussion it
seems that the best decision is to make it true of the same-signed
infinity as well, ie, just return constant TRUE if the calculation
would produce a NaN.

(We could write this with a bit less code by subtracting anyway,
and then checking for a NaN result.  However, I prefer this
formulation because it'll be easier to transpose into numeric.c.)

Although this seems like clearly a bug fix with respect to finite
values, it is less obviously correct for infinite values.  Between
that and the fact that the whole issue only arises for very strange
window specifications (e.g. RANGE BETWEEN 'inf' PRECEDING AND 'inf'
PRECEDING), I'll desist from back-patching.

Noted by Dean Rasheed.

Discussion: https://postgr.es/m/3393130.1594925893@sss.pgh.pa.us
parent 4fb6aeb4
...@@ -1088,18 +1088,25 @@ in_range_float8_float8(PG_FUNCTION_ARGS) ...@@ -1088,18 +1088,25 @@ in_range_float8_float8(PG_FUNCTION_ARGS)
} }
/* /*
* Deal with infinite offset (necessarily +inf, at this point). We must * Deal with cases where both base and offset are infinite, and computing
* special-case this because if base happens to be -inf, their sum would * base +/- offset would produce NaN. This corresponds to a window frame
* be NaN, which is an overflow-ish condition we should avoid. * whose boundary infinitely precedes +inf or infinitely follows -inf,
* which is not well-defined. For consistency with other cases involving
* infinities, such as the fact that +inf infinitely follows +inf, we
* choose to assume that +inf infinitely precedes +inf and -inf infinitely
* follows -inf, and therefore that all finite and infinite values are in
* such a window frame.
*
* offset is known positive, so we need only check the sign of base in
* this test.
*/ */
if (isinf(offset)) if (isinf(offset) && isinf(base) &&
{ (sub ? base > 0 : base < 0))
PG_RETURN_BOOL(sub ? !less : less); PG_RETURN_BOOL(true);
}
/* /*
* Otherwise it should be safe to compute base +/- offset. We trust the * Otherwise it should be safe to compute base +/- offset. We trust the
* FPU to cope if base is +/-inf or the true sum would overflow, and * FPU to cope if an input is +/-inf or the true sum would overflow, and
* produce a suitably signed infinity, which will compare properly against * produce a suitably signed infinity, which will compare properly against
* val whether or not that's infinity. * val whether or not that's infinity.
*/ */
...@@ -1157,18 +1164,25 @@ in_range_float4_float8(PG_FUNCTION_ARGS) ...@@ -1157,18 +1164,25 @@ in_range_float4_float8(PG_FUNCTION_ARGS)
} }
/* /*
* Deal with infinite offset (necessarily +inf, at this point). We must * Deal with cases where both base and offset are infinite, and computing
* special-case this because if base happens to be -inf, their sum would * base +/- offset would produce NaN. This corresponds to a window frame
* be NaN, which is an overflow-ish condition we should avoid. * whose boundary infinitely precedes +inf or infinitely follows -inf,
* which is not well-defined. For consistency with other cases involving
* infinities, such as the fact that +inf infinitely follows +inf, we
* choose to assume that +inf infinitely precedes +inf and -inf infinitely
* follows -inf, and therefore that all finite and infinite values are in
* such a window frame.
*
* offset is known positive, so we need only check the sign of base in
* this test.
*/ */
if (isinf(offset)) if (isinf(offset) && isinf(base) &&
{ (sub ? base > 0 : base < 0))
PG_RETURN_BOOL(sub ? !less : less); PG_RETURN_BOOL(true);
}
/* /*
* Otherwise it should be safe to compute base +/- offset. We trust the * Otherwise it should be safe to compute base +/- offset. We trust the
* FPU to cope if base is +/-inf or the true sum would overflow, and * FPU to cope if an input is +/-inf or the true sum would overflow, and
* produce a suitably signed infinity, which will compare properly against * produce a suitably signed infinity, which will compare properly against
* val whether or not that's infinity. * val whether or not that's infinity.
*/ */
......
...@@ -1936,6 +1936,42 @@ window w as (order by f_float4 range between ...@@ -1936,6 +1936,42 @@ window w as (order by f_float4 range between
9 | NaN | 9 | 9 9 | NaN | 9 | 9
(10 rows) (10 rows)
select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float4 range between
'inf' preceding and 'inf' preceding);
id | f_float4 | first_value | last_value
----+-----------+-------------+------------
0 | -Infinity | 0 | 0
1 | -3 | 0 | 0
2 | -1 | 0 | 0
3 | 0 | 0 | 0
4 | 1.1 | 0 | 0
5 | 1.12 | 0 | 0
6 | 2 | 0 | 0
7 | 100 | 0 | 0
8 | Infinity | 0 | 8
9 | NaN | 9 | 9
(10 rows)
select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float4 range between
'inf' following and 'inf' following);
id | f_float4 | first_value | last_value
----+-----------+-------------+------------
0 | -Infinity | 0 | 8
1 | -3 | 8 | 8
2 | -1 | 8 | 8
3 | 0 | 8 | 8
4 | 1.1 | 8 | 8
5 | 1.12 | 8 | 8
6 | 2 | 8 | 8
7 | 100 | 8 | 8
8 | Infinity | 8 | 8
9 | NaN | 9 | 9
(10 rows)
select id, f_float4, first_value(id) over w, last_value(id) over w select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics from numerics
window w as (order by f_float4 range between window w as (order by f_float4 range between
...@@ -1995,6 +2031,42 @@ window w as (order by f_float8 range between ...@@ -1995,6 +2031,42 @@ window w as (order by f_float8 range between
9 | NaN | 9 | 9 9 | NaN | 9 | 9
(10 rows) (10 rows)
select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float8 range between
'inf' preceding and 'inf' preceding);
id | f_float8 | first_value | last_value
----+-----------+-------------+------------
0 | -Infinity | 0 | 0
1 | -3 | 0 | 0
2 | -1 | 0 | 0
3 | 0 | 0 | 0
4 | 1.1 | 0 | 0
5 | 1.12 | 0 | 0
6 | 2 | 0 | 0
7 | 100 | 0 | 0
8 | Infinity | 0 | 8
9 | NaN | 9 | 9
(10 rows)
select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float8 range between
'inf' following and 'inf' following);
id | f_float8 | first_value | last_value
----+-----------+-------------+------------
0 | -Infinity | 0 | 8
1 | -3 | 8 | 8
2 | -1 | 8 | 8
3 | 0 | 8 | 8
4 | 1.1 | 8 | 8
5 | 1.12 | 8 | 8
6 | 2 | 8 | 8
7 | 100 | 8 | 8
8 | Infinity | 8 | 8
9 | NaN | 9 | 9
(10 rows)
select id, f_float8, first_value(id) over w, last_value(id) over w select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics from numerics
window w as (order by f_float8 range between window w as (order by f_float8 range between
......
...@@ -524,6 +524,14 @@ window w as (order by f_float4 range between ...@@ -524,6 +524,14 @@ window w as (order by f_float4 range between
'inf' preceding and 'inf' following); 'inf' preceding and 'inf' following);
select id, f_float4, first_value(id) over w, last_value(id) over w select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics from numerics
window w as (order by f_float4 range between
'inf' preceding and 'inf' preceding);
select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float4 range between
'inf' following and 'inf' following);
select id, f_float4, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float4 range between window w as (order by f_float4 range between
1.1 preceding and 'NaN' following); -- error, NaN disallowed 1.1 preceding and 'NaN' following); -- error, NaN disallowed
...@@ -541,6 +549,14 @@ window w as (order by f_float8 range between ...@@ -541,6 +549,14 @@ window w as (order by f_float8 range between
'inf' preceding and 'inf' following); 'inf' preceding and 'inf' following);
select id, f_float8, first_value(id) over w, last_value(id) over w select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics from numerics
window w as (order by f_float8 range between
'inf' preceding and 'inf' preceding);
select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float8 range between
'inf' following and 'inf' following);
select id, f_float8, first_value(id) over w, last_value(id) over w
from numerics
window w as (order by f_float8 range between window w as (order by f_float8 range between
1.1 preceding and 'NaN' following); -- error, NaN disallowed 1.1 preceding and 'NaN' following); -- error, NaN disallowed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment