Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wZq5c-001Ji4-0K for pgsql-hackers@arkaria.postgresql.org; Wed, 17 Jun 2026 13:13:56 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wZq5Z-0065hk-1b for pgsql-hackers@arkaria.postgresql.org; Wed, 17 Jun 2026 13:13:53 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wZq5Z-0065hc-08 for pgsql-hackers@lists.postgresql.org; Wed, 17 Jun 2026 13:13:53 +0000 Received: from meldrar.postgresql.org ([2a02:c0:301:0:ffff::31]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wZq5W-00000000ugZ-0fB4 for pgsql-hackers@postgresql.org; Wed, 17 Jun 2026 13:13:52 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Content-Transfer-Encoding:Content-Type: Mime-Version:References:In-Reply-To:From:Subject:Cc:To:Message-Id:Date:Sender :Reply-To:Content-ID:Content-Description; bh=gtXRWI9jUlprthRWDEwahgrhm16b3SY/QdVdWzMVn7Y=; b=m3iBlsJyKf/U96C7gH3UW++ZQ5 /94m0r0cQdgkCfocA2hxqqzwb1+kAIbTiXOlg0/vxad3SVyl/ajB2GHjsAd0448GZYpkX9PzmRUMo JxlYZ7TEvlOOLAbBrBysYKKGers+vgouh6b//cnzfLxOp1WSzA5k66Rta/seeDy3hNxK/9NJAEQo1 1cnVDZceoYGxzu9A6/lSMUPPXzU6/iaxCmrdmgJBFqMGNt+XPoVTyW1/IK6tmjziSCUO5bRzMdscN FxkaLGQmCkb90GE80qklTO6mSyBJBnhf98626RUsiAwnZluplTJeoVeKYQ1hI+Zd/10EKNfMQps1m ZSY4B+ag==; Received: from [2409:11:4120:300:4c7b:1e55:b860:a64d] (helo=localhost) by meldrar.postgresql.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wZq5N-002M9a-2y; Wed, 17 Jun 2026 13:13:44 +0000 Date: Wed, 17 Jun 2026 22:13:27 +0900 (JST) Message-Id: <20260617.221327.809417533229490738.ishii@postgresql.org> To: assam258@gmail.com Cc: jian.universality@gmail.com, zsolt.parragi@percona.com, sjjang112233@gmail.com, vik@postgresfriends.org, er@xs4all.nl, jacob.champion@enterprisedb.com, david.g.johnston@gmail.com, peter@eisentraut.org, li.evan.chao@gmail.com, pgsql-hackers@postgresql.org Subject: Re: Row pattern recognition From: Tatsuo Ishii In-Reply-To: References: X-Mailer: Mew version 6.8 on Emacs 29.3 Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Host-Lookup-Failed: Reverse DNS lookup failed for 2409:11:4120:300:4c7b:1e55:b860:a64d (failed) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi Henson, > Hi Tatsuo, Jian, > > I think there's a correctness problem in the RPR patch: a window function's > result can change depending on which other, unrelated window functions are > in the same query. > > Pattern matching only advances when a window function reads the frame. > nth_value(x, n) returns NULL without reading the frame when n is NULL > (correct per the standard), so if it is the only window function in an RPR > window, the match never advances over those rows and the reduced frame no > longer matches a full scan. Ouch. > Example -- one partition, 60 rows, price = id * 10: > > CREATE TABLE rpr_dormant (id int, price int); > INSERT INTO rpr_dormant SELECT g, g*10 FROM generate_series(1,60) g; > > SELECT id, nth_value(price, CASE WHEN id < 50 THEN NULL ELSE 1 END) OVER w > FROM rpr_dormant > WINDOW w AS ( > ORDER BY id > ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING > AFTER MATCH SKIP PAST LAST ROW > PATTERN (A+) > DEFINE A AS price > PREV(FIRST(price), 50) > ); > > Run alone, the nth_value column does not follow the actual match structure; > adding an unrelated first_value(id) OVER w, which reads the frame every row, > changes it. And while the match is dormant the mark position keeps > advancing, running ahead and trimming rows that the backward navigation > later needs -- so the same query can instead fail with "cannot fetch row N > before WindowObject's mark position". > > I think an RPR window should perform the match for each row up front, > building its reduced frame during the row scan before the window functions > are evaluated, regardless of whether any function reads the frame. The fix > belongs in the executor, not in nth_value -- the early return is standard, > and the same gap is reachable from any user-defined function that skips the > frame. > > Does this direction seem right, or is the lazy, frame-driven matching > intentional in a way I'm missing? Happy to prepare a patch. Yes, I think the direction is correct. Probably the patch would someting like this? diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index cb6a484b7de..b6c12096c85 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -2523,6 +2523,9 @@ ExecWindowAgg(PlanState *pstate) { if (winstate->rpSkipTo == ST_NEXT_ROW) clear_reduced_frame(winstate); + + update_reduced_frame(winstate->nav_winobj, + winstate->frameheadpos); } /* > is the lazy, frame-driven matching > intentional in a way I'm missing? Not intentional. Regards, -- Tatsuo Ishii SRA OSS K.K. English: http://www.sraoss.co.jp/index_en/ Japanese:http://www.sraoss.co.jp