Re: first_value()/nth_value() with IGNORE NULLS loop forever (EXCLUDE GROUP/TIES, frame past partition end, no ORDER BY)

From: Tatsuo Ishii <ishii(at)postgresql(dot)org>
To: masao(dot)fujii(at)gmail(dot)com
Cc: zhong950419(at)gmail(dot)com, c(dot)bhuvan(at)iitg(dot)ac(dot)in, pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: first_value()/nth_value() with IGNORE NULLS loop forever (EXCLUDE GROUP/TIES, frame past partition end, no ORDER BY)
Date: 2026-11-07 00:18:33
Message-ID: 20261107.091833.1799286331446051531.ishii@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

> The following query succeeded before this fix but fails afterward.
> Is this intentional?
>
> WITH t AS MATERIALIZED (
> SELECT NULLIF(i, 1) / (2 - i) AS v
> FROM generate_series(1, 2) AS g(i)
> )
> SELECT first_value(v) IGNORE NULLS OVER (ROWS CURRENT ROW)
> FROM t
> LIMIT 1;

Not intentional. I think it just happened to work by chance. The
outer node (WITH t AS MATERIALIZED ... FROM generate_series(1, 2)...)
raises an error (division by zero) as soon as the second row is
fetched.

When the window aggregate node (SELECT first_value(v) IGNORE NULLS
OVER (ROWS CURRENT ROW) FROM t) is executed, the outer node provides
the first row with v = NULL. So the window agg node tries to fetch the
second row to find out a non NULL row.

Previously we called row_is_in_frame() to determine if the second row
is in the frame without fetching the row in this
case. row_is_in_frame() determined that the second row is out of frame
because the framing option is "ROWS CURRENT ROW", meaning only the
current row (in this case the first row) is within a frame. Thus it
gave up searching further rows and return NULL without an error.

After the fix, before calling row_is_in_frame(), we call
spool_tuples() which tries to read rows from outer node to check
whether we hit the partition end. In the case above, we want to read
up to the second row. Unfortunately it raises the error in the outer
node.

I think this kind of behavior (reading beyond frame end) is not
specific to window functions with IGNORE NULLLS. Consider following
query:

WITH t AS MATERIALIZED (
SELECT NULLIF(i, 1) / (2 - i) AS v
FROM generate_series(1, 2) AS g(i)
)
SELECT nth_value(v,2) RESPECT NULLS OVER (ROWS CURRENT ROW)
FROM t
LIMIT 1;

This throws an error:
ERROR: division by zero

The window agg node tries to fetch the second row to evaluate
nth_value(v,2) and fails. One may ask why it fetches the second row
even if it is out of frame. In my understanding to determine whether a
row is in frame, we need to fetch the row first. We need to check the
tuple content in some frame options.

Regards,
--
Tatsuo Ishii
SRA OSS K.K.
English: http://www.sraoss.co.jp/index_en/
Japanese:http://www.sraoss.co.jp

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message PG Bug reporting form 2026-11-07 01:23:41 BUG #19839: Assert in ReorderBufferRestoreCleanup for aborted, spilled transaction
Previous Message Zsolt Parragi 2026-11-06 22:10:27 Re: BUG #19382: Server crash at __nss_database_lookup