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 1vpzG4-002MVg-13 for pgsql-hackers@arkaria.postgresql.org; Wed, 11 Feb 2026 01:43:13 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vpzG3-001nVk-0W for pgsql-hackers@arkaria.postgresql.org; Wed, 11 Feb 2026 01:43:11 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vpzG2-001nVc-2K for pgsql-hackers@lists.postgresql.org; Wed, 11 Feb 2026 01:43:11 +0000 Received: from mail-pj1-x102e.google.com ([2607:f8b0:4864:20::102e]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vpzG0-000000003K2-3UsV for pgsql-hackers@postgresql.org; Wed, 11 Feb 2026 01:43:10 +0000 Received: by mail-pj1-x102e.google.com with SMTP id 98e67ed59e1d1-3567e2b4159so85931a91.0 for ; Tue, 10 Feb 2026 17:43:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1770774188; cv=none; d=google.com; s=arc-20240605; b=LeqNrgbAEJA2crJfi+VsvmVQHsh3UUW7b9yseMKlvG+gBr4bulKWwCO6jIYzmuJuFO jLT1MW4gPSyJmHy8/b04UAZPmm+lSQeH+sPPkEXe2uV68r1obY/kdgZnwCasi8LOueLA QOIsL8ZceXWVF6ayTZT5HhD1Cf8uEoZxWT62aYEq+F3aI6IhDvhIA7GZ+JUkOiqW+Dcv NqInQFMn10RZcGiCZJTIzlE3vJcyIpNaiPNT8QDVq38KoqlUEWIpMX1gVDu0ZTK+Us7a V0Ao7FbYmxjq6kZOIkZpwIKjFPKDg9Gh9gBDq0f+yWkCSp32xWnGGVySu4hZziT8/0Mm Mb4g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:reply-to:in-reply-to:references :mime-version:dkim-signature; bh=JtJqszvlbINlYRlaPzbxUgTVsQvBDoR5+Kwg6XyaxMQ=; fh=WXKgJjyRg5pwfzraik38aVZFWgRl7NLj2lAudEKJO48=; b=acqDgTWnpTj1NokEx26VMgCe4lQvrZkIxhFYJdu01/H9DA+lVmZobLxYiAVsNBdE6Z I572qcb3V3Srs/6ZzY7oLZV9mexdpZCTYq47OljWoyQ6ieyaDG3kDUyc8tOZrcjDkwPn l6qQjJaxYHVYtmFspdpEoe8OHeiC0OY+Z8g5AsNMv4qutLOZjeCNUImUHPpGZcrTsIAy tE2lXEF/zKVfcomKVQQSKGQKypEU9TlZhzI2BwezADdkUnK6ceB5qzZcs7O0kr9V3k/J 0T2fBbP/Df2cKVXrU2rJeLPP5H48rQ95TxX/5JRvn0Taw2m0Zlzjo/Oo7+bpQ6cBumLN XjPw==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770774188; x=1771378988; darn=postgresql.org; h=cc:to:subject:message-id:date:from:reply-to:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=JtJqszvlbINlYRlaPzbxUgTVsQvBDoR5+Kwg6XyaxMQ=; b=EV2ahEbMQ3wSmsVzisK9CnEvI5qr3pfaRTM04Ce/U9NJx6c5miQLMuV8U/B7uooRqp v3w1Rat1IuOOdh6fxIvP2bSAx1RuIRcQtmzItdzRHEqXoulLDVBP/FwFTcfOe6iz8DtQ pHDj75ZF0M4HwMay/cCUjMF35Z9QhnXxSAS3AoDLZRB+dsomjGoHvTI2xQUXbWF8Yt5K Ur1k+AcGN8KWFC2sOz13Faz4FAURZwRudMcAum13VpXNKeez7lLOydyBEf6eLw1HvEDH x1VHxNrin4uHtCwHiwcxIcnUYTjCKyTCeCg7/YyVJTO2Ha33A0KHymHs4ZCLOG1bzBaO 7YnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770774188; x=1771378988; h=cc:to:subject:message-id:date:from:reply-to:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=JtJqszvlbINlYRlaPzbxUgTVsQvBDoR5+Kwg6XyaxMQ=; b=pVF2rxE0YjGAzXThJWs2vlRYdl6WGOHD3Kh/73cNgYrgWBFc2bdNbQYqJZzE83Wiqu IGS5uV05JPQVTQAReEmU2T2MBvqcu4K0jStnbobd31GC1QW+2lcWhkMFSAxvAAW0pd+q PMiq9F3Pqw/gUUpEi8E5ta25zOvwHEWPF4Zkn8gI3u4NNJQPONvOUP4J8MSU5k0u1L2A Fd4a5/98LoOqEYlcWn80EqsxtYM1pXROHp2VltYUVf2rtJvo8X/G/9J8W3SnKWonMIHp VUNgpXoOrtQFwlvn6R5TzXJDqFyPeM+ogOvOlFvE/DHFbUrgvkh5102kURURsQZM6whn 5tEA== X-Forwarded-Encrypted: i=1; AJvYcCUIFRKfX08BNQHw5ZxX6N7uh/9Ok/1fACFC2rPLcOwSmvUI/OCTkhi5d/uaRChexwIU38m7G7n5LOC8tv75@postgresql.org X-Gm-Message-State: AOJu0Yz9jbHPF/Rt0WphpMsa/2IORAdDuyu/Q5ckJjpHN5tVTeeXKydM ZWAZiiXp7DfJSXkgkWOuAi+ykV0lEuNGuUFPHSfX+gUy4SoK8KFzwG9k6qMWUiEgogr3y3F3pnP yQ1EI2Iq/fT6VF3IFBS26SRLnABXM6vU= X-Gm-Gg: AZuq6aJikicfZg+7Fx3lOd6KViI1HFXcUk0AoG8ifb6KlVmhbkQkwvWMoZp2X7Iuqf6 w/HU1UYuLdiYRkD9uRu6hhQMYsH9z7Grz2dZG/cpihM26CZuB60TcF0Krgjt041/gVa18aHovkg kI1MtMnH4206LslRJkzEPaRJR6Cc8YbmD/VDwyHOKNwQgvTMgeaoqtqa82VterGPVNfg0QBiKZ9 evn459fjQrFSBA27ePQI8T7bMGMaThb3jIU9Vpp6LciILg9KeXno6tphJgV3iEld1JlX24sNxaa 90s343QufxrcW4sibOn4KRhxn1qhvEjmYrj89Ro= X-Received: by 2002:a17:90a:d00d:b0:354:7be4:a241 with SMTP id 98e67ed59e1d1-354b3bf0630mr14652855a91.15.1770774187517; Tue, 10 Feb 2026 17:43:07 -0800 (PST) MIME-Version: 1.0 References: <20260209.160520.873483599710391856.ishii@postgresql.org> <20260209.190209.470246357440525054.ishii@postgresql.org> In-Reply-To: <20260209.190209.470246357440525054.ishii@postgresql.org> Reply-To: assam258@gmail.com From: Henson Choi Date: Wed, 11 Feb 2026 10:42:56 +0900 X-Gm-Features: AZwV_Qj2qKAg51af0yj5WBBJPxS7WA-JHvso_XuGxdhzP8SZSkymK2PqNA0rXBw Message-ID: Subject: Re: Row pattern recognition To: Tatsuo Ishii Cc: jacob.champion@enterprisedb.com, david.g.johnston@gmail.com, vik@postgresfriends.org, er@xs4all.nl, peter@eisentraut.org, pgsql-hackers@postgresql.org Content-Type: multipart/alternative; boundary="0000000000006cac3b064a827e13" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000006cac3b064a827e13 Content-Type: text/plain; charset="UTF-8" Hi Ishii-san, During code review of nodeWindowAgg.c, I discovered that update_reduced_frame() can receive the same pos value repeatedly for RANGE/GROUPS frames. ---------------------------------------------------------------------- PATTERN DISCOVERED ---------------------------------------------------------------------- For RANGE/GROUPS frames with peer rows (rows having the same ORDER BY value): Data: id=1,2,3 all have val=10 (peer rows) id=4,5 both have val=20 (peer rows) RANGE frame processing: - id=1 processing: pos=0 (frameheadpos points to first peer) - id=2 processing: pos=0 (same frameheadpos) - id=3 processing: pos=0 (same frameheadpos) - id=4 processing: pos=3 - id=5 processing: pos=3 ROWS frames always have sequential pos values (0,1,2,3,4...), but RANGE/GROUPS can have repeated pos values (0,0,0,3,3...). ---------------------------------------------------------------------- CURRENT IMPLEMENTATION ISSUE ---------------------------------------------------------------------- The current implementation appears to assume sequential processing only, which is valid for ROWS frames. Two specific issues found: 1. In update_reduced_frame(): - The pos <= nfaLastProcessedRow check assumes "already processed pos doesn't need reprocessing", but RANGE/GROUPS require processing the same pos multiple times. 2. In nfa_process_row(): - Frame boundary calculation: ctxFrameEnd = matchStartRow + frameOffset + 1 - This adds frameOffset directly to row position, which works for ROWS but may be incorrect for RANGE (value-based) and GROUPS (group-based) frames. ---------------------------------------------------------------------- TEST COVERAGE LIMITATION ---------------------------------------------------------------------- The existing RANGE/GROUPS test cases in rpr_base.sql only cover simple scenarios that don't reach the frame end, which is why this issue wasn't detected. Most tests target ROWS frames, with only a small number testing RANGE/GROUPS frames. ---------------------------------------------------------------------- PROPOSAL ---------------------------------------------------------------------- Rather than directly modifying these functions, we should consider an alternative approach for non-UNBOUNDED frames: - Strictly respect the frame head position - Minimize lower-level optimizations (absorption/pruning) - Avoid creating subsequent contexts for limited reuse benefit - For bounded frames, the benefit of context reuse across rows may be limited compared to UNBOUNDED frames where contexts can be reused extensively - This would simplify RANGE/GROUPS handling at the cost of some optimization To implement this properly, we need to broaden our understanding of the execution structure through code review of Window clauses and Aggregate functions. Therefore, for this round of nodeWindowAgg.c review: 1. Perform code simplification/refactoring for ROWS frames only 2. Add FIXME comments to RANGE/GROUPS test cases 3. Defer RANGE/GROUPS handling to a future phase There may be other issues beyond RANGE/GROUPS. After this code review round is complete, I would like to regroup and discuss the direction forward. Best regards, Henson --0000000000006cac3b064a827e13 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Ishii-san,

During code review o= f nodeWindowAgg.c, I discovered that update_reduced_frame()
can receive = the same pos value repeatedly for RANGE/GROUPS frames.


-----------------------------------------------------------= -----------
PATTERN DISCOVERED
--------------------------------------= --------------------------------


For RANGE/GROUPS frames with= peer rows (rows having the same ORDER BY value):

=C2=A0 Data: id=3D= 1,2,3 all have val=3D10 (peer rows)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 id=3D4,5= both have val=3D20 (peer rows)

=C2=A0 RANGE frame processing:
= =C2=A0 - id=3D1 processing: pos=3D0 (frameheadpos points to first peer)
= =C2=A0 - id=3D2 processing: pos=3D0 (same frameheadpos)
=C2=A0 - id=3D3 = processing: pos=3D0 (same frameheadpos)
=C2=A0 - id=3D4 processing: pos= =3D3
=C2=A0 - id=3D5 processing: pos=3D3

ROWS frames always have = sequential pos values (0,1,2,3,4...), but RANGE/GROUPS
can have repeated= pos values (0,0,0,3,3...).


-----------= -----------------------------------------------------------
CURRENT IMPL= EMENTATION ISSUE
-------------------------------------------------------= ---------------


The current implementation appears to assume = sequential processing only, which
is valid for ROWS frames. Two specific= issues found:

1. In update_reduced_frame():
=C2=A0 =C2=A0- The p= os <=3D nfaLastProcessedRow check assumes "already processed pos do= esn't
=C2=A0 =C2=A0 =C2=A0need reprocessing", but RANGE/GROUPS = require processing the same pos
=C2=A0 =C2=A0 =C2=A0multiple times.
<= br>2. In nfa_process_row():
=C2=A0 =C2=A0- Frame boundary calculation: c= txFrameEnd =3D matchStartRow + frameOffset + 1
=C2=A0 =C2=A0- This adds = frameOffset directly to row position, which works for ROWS but
=C2=A0 = =C2=A0 =C2=A0may be incorrect for RANGE (value-based) and GROUPS (group-bas= ed) frames.


---------------------------= -------------------------------------------
TEST COVERAGE LIMITATION
= ----------------------------------------------------------------------

The existing RANGE/GROUPS test cases in rpr_base.sql only cover s= imple scenarios
that don't reach the frame end, which is why this is= sue wasn't detected.

Most tests target ROWS frames, with only a = small number testing RANGE/GROUPS
frames.


---------------------------------------------------------------------= -
PROPOSAL
----------------------------------------------------------= ------------

Rather than directly modifying these functions, = we should consider an
alternative approach for non-UNBOUNDED frames:
= - Strictly respect the frame head position
- Minimize lower-level optimi= zations (absorption/pruning)
- Avoid creating subsequent contexts for li= mited reuse benefit
- For bounded frames, the benefit of context reuse a= cross rows may be limited
=C2=A0 compared to UNBOUNDED frames where cont= exts can be reused extensively
- This would simplify RANGE/GROUPS handli= ng at the cost of some optimization

To implem= ent this properly, we need to broaden our understanding of the
execution= structure through code review of Window clauses and Aggregate
functions= .

Therefore, for this round of nodeWindowAgg.c review:
1. Perform= code simplification/refactoring for ROWS frames only
2. Add FIXME comme= nts to RANGE/GROUPS test cases
3. Defer RANGE/GROUPS handling to a futur= e phase

There may be other issues beyond RANGE/GROUPS.
After this= code review round is complete, I would like to regroup and discuss
the = direction forward.

Best regards,
Henson
--0000000000006cac3b064a827e13--