| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | 303677365(at)qq(dot)com |
| Subject: | BUG #19870: WinGetFuncArgInPartition(..., WINDOW_SEEK_TAIL, ...) returns wrong rows under IGNORE NULLS |
| Date: | 2026-11-11 08:48:00 |
| Message-ID: | 19870-a58b65793ce2a466@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19870
Logged by: chunling qin
Email address: 303677365(at)qq(dot)com
PostgreSQL version: 19rc1
Operating system: Linux x86_64; platform-independent (reproduced on
Description:
**Description:**
A user-defined C window function that calls the public API
`WinGetFuncArgInPartition(winobj, argno, relpos, WINDOW_SEEK_TAIL, ...)`
gets wrong rows when the call runs with `IGNORE NULLS`:
1. **Wrong results on data with NO NULLs** — where `IGNORE NULLS` must be a
no-op: with `seektype=TAIL, relpos=-2` the IGNORE result differs from the
RESPECT result for every row (5/5 diffs), and the returned values come from
the wrong rows. With `seektype=CURRENT` or `HEAD` the two agree (0 diffs),
so the defect is specific to the TAIL branch.
2. **Internal error for large positive relpos**: `relpos = INT_MAX` with
TAIL + IGNORE NULLS raises `ERROR: XX000: invalid memory alloc request size
1073741824` (it tries to expand the not-null bitmap for an out-of-range row
position), while TAIL + RESPECT NULLS correctly returns NULL. The server
stays up; only the statement fails.
Commit b8d82d17875 fixed wrong results of some built-in paths under IGNORE
NULLS, but this public-API TAIL branch still computes the starting row
position as if the not-null bitmap could be consulted there, and applies
relpos twice.
**Reproduction (minimal C window function + SQL; verified on master):**
C probe (compile with `cc -fPIC -shared -I"$(pg_config --includedir-server)"
-o /tmp/probe.so probe.c`):
```c
#include "postgres.h"
#include "fmgr.h"
#include "windowapi.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(win_partition_arg);
/* win_partition_arg(value, relpos int4, seektype int4) -> value
seektype: 0=CURRENT 1=HEAD 2=TAIL (WINDOW_SEEK_*) */
Datum
win_partition_arg(PG_FUNCTION_ARGS)
{
WindowObject winobj = PG_WINDOW_OBJECT();
bool isnull = false;
bool isout = false;
int32 relpos;
int32 seektype;
Datum result;
WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
relpos = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
seektype = DatumGetInt32(WinGetFuncArgCurrent(winobj, 2, &isnull));
result = WinGetFuncArgInPartition(winobj, 0, relpos, seektype,
false, &isnull, &isout);
if (isnull)
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
```
SQL (5 rows, **no NULLs** — IGNORE NULLS must be a no-op here):
```sql
CREATE FUNCTION win_partition_arg(anyelement, int4, int4) RETURNS anyelement
AS '/tmp/probe.so', 'win_partition_arg' LANGUAGE C WINDOW;
CREATE TABLE b1_txt (k int, x text);
INSERT INTO b1_txt VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e');
-- (1) diffs per seektype (RESPECT vs IGNORE must be 0 everywhere on no-NULL
data):
SELECT 'CURRENT rp=2' AS seektype, count(*) FILTER (WHERE r IS DISTINCT FROM
i) AS diffs
FROM (SELECT win_partition_arg(x,2,0) RESPECT NULLS OVER w AS r,
win_partition_arg(x,2,0) IGNORE NULLS OVER w AS i
FROM b1_txt WINDOW w AS (ORDER BY k)) s
UNION ALL
SELECT 'HEAD rp=2', count(*) FILTER (WHERE r IS DISTINCT FROM i)
FROM (SELECT win_partition_arg(x,2,1) RESPECT NULLS OVER w AS r,
win_partition_arg(x,2,1) IGNORE NULLS OVER w AS i
FROM b1_txt WINDOW w AS (ORDER BY k)) s
UNION ALL
SELECT 'TAIL rp=-2', count(*) FILTER (WHERE r IS DISTINCT FROM i)
FROM (SELECT win_partition_arg(x,-2,2) RESPECT NULLS OVER w AS r,
win_partition_arg(x,-2,2) IGNORE NULLS OVER w AS i
FROM b1_txt WINDOW w AS (ORDER BY k)) s;
-- CURRENT rp=2 | 0
-- HEAD rp=2 | 0
-- TAIL rp=-2 | 5 <- bug: every row wrong
-- (2) the actual wrong values (TAIL, relpos=-1):
SELECT k, win_partition_arg(x,-1,2) RESPECT NULLS OVER w AS respect,
win_partition_arg(x,-1,2) IGNORE NULLS OVER w AS ignore
FROM b1_txt WINDOW w AS (ORDER BY k) ORDER BY k;
-- 1..5: respect = d on every row; ignore = c on every row (wrong rows
returned)
-- (3) large positive relpos, TAIL:
SELECT win_partition_arg(x,2147483647,2) RESPECT NULLS OVER w FROM b1_txt
WINDOW w AS (ORDER BY k) LIMIT 1;
-- NULL (correct)
SELECT win_partition_arg(x,2147483647,2) IGNORE NULLS OVER w FROM b1_txt
WINDOW w AS (ORDER BY k) LIMIT 1;
-- ERROR: XX000: invalid memory alloc request size 1073741824 (should
return NULL)
```
**Expected behavior:** on data with no NULLs, `IGNORE NULLS` must return
exactly what `RESPECT NULLS` returns for every seektype; and an out-of-range
relpos must yield NULL, not an internal error — as already happens for
CURRENT/HEAD.
**Related:** b8d82d17875 (fixed some built-in IGNORE NULLS paths) and
69b20fdd20d (fixed the first_value/nth_value infinite loop) — this TAIL
branch of the public API is the remaining face of the same family.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-11-11 08:53:50 | BUG #19871: JSON_TABLE evaluates each PASSING argument once per column - a named variable $v gets different valu |
| Previous Message | PG Bug reporting form | 2026-11-11 08:34:31 | BUG #19869: Wrong results: planner silently drops the second ORDER BY sort key when both keys reference the same |