Fix infinite loop in first_value()/nth_value() with IGNORE NULLS.

ignorenulls_getfuncarginframe() walks the frame row by row looking for
the n-th non-null value.  It calls row_is_in_frame() on each position
without first checking that the position is inside the partition,
although row_is_in_frame() requires its caller to have done so ("The
caller must have already determined that the row is in the
partition").  When the frame ends at UNBOUNDED FOLLOWING,
row_is_in_frame() has no end-of-frame test, and with EXCLUDE GROUP (or
EXCLUDE TIES, for rows other than the current one) and no ORDER BY it
returns 0 ("excluded") for every position, including positions past
the last row.  The loop then never calls window_gettupleslot(), which
is what would have noticed the end of the partition, and it advances
abs_pos forever.  The fix checks the partition end before calling
row_is_in_frame(), the same way window_gettupleslot() does
(spool_tuples(), then compare with spooled_rows).  It also adds
CHECK_FOR_INTERRUPTS() at the top of the loop: the loop can pass over
many excluded or known-NULL rows without fetching any tuple, and
window_gettupleslot() is where the interrupt check normally happens.

Author: BHUVAN CHILUKUTI <[email protected]>
Reviewed-by: shihao zhong <[email protected]>
Reviewed-by: Tatsuo Ishii <[email protected]>
Discussion: 
https://postgr.es/m/PN0P287MB09038F3BD9921EBACF5646F6A7632%40PN0P287MB0903.INDP287.PROD.OUTLOOK.COM
Backpatch-through: 19

Branch
------
REL_19_STABLE

Details
-------
https://git.postgresql.org/pg/commitdiff/50f4c42722492e7db792dc5afd0e02d70061b175

Modified Files
--------------
src/backend/executor/nodeWindowAgg.c | 15 ++++++++++++++
src/test/regress/expected/window.out | 40 ++++++++++++++++++++++++++++++++++++
src/test/regress/sql/window.sql      | 20 ++++++++++++++++++
3 files changed, 75 insertions(+)

Reply via email to