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 1w1PlO-000HWf-0N for pgsql-hackers@arkaria.postgresql.org; Sat, 14 Mar 2026 14:14:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w1PlL-001utA-36 for pgsql-hackers@arkaria.postgresql.org; Sat, 14 Mar 2026 14:14:44 +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 1w1PlL-001ut2-1W for pgsql-hackers@lists.postgresql.org; Sat, 14 Mar 2026 14:14:44 +0000 Received: from mail-pj1-x102b.google.com ([2607:f8b0:4864:20::102b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w1PlJ-0000000076r-2EYe for pgsql-hackers@postgresql.org; Sat, 14 Mar 2026 14:14:43 +0000 Received: by mail-pj1-x102b.google.com with SMTP id 98e67ed59e1d1-35a1f3f07ebso1141101a91.3 for ; Sat, 14 Mar 2026 07:14:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773497680; cv=none; d=google.com; s=arc-20240605; b=jPk8RUsqPfs1xYOQ5xsO3Jb0/N36tQa/fh22wWgamz29wGI7wFH5AwQxkjMSS6/SQO nNvPgucEN5MQAhFoSALfdlUhBC/Z5a18lhiWI8XcF4q7+HZIJorSsqfXq8uN49X9iOue jK5Ztppw6P0M7udesDN+6xVTsAMS07NBE2PC8KffcLIdsIx0SB6oRnIFLxyO42I7LkKM 6g/2e03pYXRKaQwQj5SpK9zQVk38wth3M3hsdJmiiR+z0f+01Bfx/Cso6Rq3ReZmYuCO REcPkg5tik1CEUZnI/73P6rh026BqrgDD1zk1rAXv5rS1THdmUYBsMc9Fhu9yhFaEwFi 5v0A== 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=U2jsISBrwHYfIw/fykWc7x+hRH6TeNcSQFMUv3wJA2w=; fh=SM0M30O/1RNxxhoD/moOJ6heJcJpxvKWl6AYP+kIYNY=; b=Xfrn+lvE6YoN7f3ROwRg9t6zkh5UQexU2+pJapV2nL0X5eLNyCNXSmPnsKSOFB7PXd l/fnKbbd8u/OIPDlf6Hvrm7SNHNTsrmBwYZG01ATliHYwSYhEcoJ2MbpadOuV8INnRoA pjkQ1WKPgYi5zGXNXjArXkTeS78t7YDQo9v8Si3hDJK0c/fUfvOOCN0SIOWYSP4/BsEa VkGY9me8IMmjnJIie4JSwoQIsm13AY8NdQvsGyytp6fABy8o5UkxuSE+b7idf9/mJVyK +dX28nD+eQUEuq4njYXZbs/KHVH04/LO7mG8UJHHDp+hGvsKGVY+0nQVErvp8KjgZGMa 6qxg==; 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=1773497680; x=1774102480; 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=U2jsISBrwHYfIw/fykWc7x+hRH6TeNcSQFMUv3wJA2w=; b=SD1b1ttTOH4v71Qwak3MqkPHgcrGrFR3mi+AFCrguhjZep04eRV/yMzNuj6PiiYQeZ vMvD7BrVjOo+jLasMLYBEKsb/jCRRosoZ1t9D6383wTDCTEf74dgYc5+HF8PZzgZ97Bg 7JwMzYNO4jOo0v7Kqk+tOvlkwOtAElkhqze0ZS8eDbrsV/lPG4QCIatOL6fUUPk/dCdT 1nj7B8SLqvs5mBhhy287De8rSim9NPbqvcOLH8uN+baLi9Fjgk0MR5bBnSCI4SZ9StOG KrYKO14Iz/eNrOf364V6RhA3VH4WlsleLMKvUzQhAwmjggOINyQcFlyXn1TD1IUqI79u lN5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773497680; x=1774102480; 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=U2jsISBrwHYfIw/fykWc7x+hRH6TeNcSQFMUv3wJA2w=; b=hjjwhAfcvT15p6VQEVVektLscaxRrZsi+zR94Ku1UjLd1/w8mqZ54mnwODK7BIb2Vw zCUR/WVb/V8XCh/fkUrS8GnHFAH8YJWyofCRGt9oH0lnUu7EUmxSvsGG3uvt09jVyC0T G6yC1MZDtjjGislDjwXHqtmZmtLEB+8OSkynQc53j1mlLIYYblTuUu7mxtXqDh4YKu5V 2F5g4znC2YYbddsHwHCHWGPLddqJWDfB+pwlEqVMVREOV6t0U+Mb/y5KriGYzlb2j8q1 8WwZ88Eabit1Jykay/bogNd2p7lVhOO2yCluH+ZVJpLyYepDx7Y6CK9lVl6eNptU/wXd lJxA== X-Forwarded-Encrypted: i=1; AJvYcCWTLSV99Q2U2lrCc94FdF6iyexmVd2oXF8sx4ZUDGapIO8li1PMIu4SOYsq6z9FlgLF0E8/L3bQi6uS9AfR@postgresql.org X-Gm-Message-State: AOJu0YxIE1yLt6dPeCKEFoKCEGCLYoOs/9G0wLBw21YNJJ71KgZ86Sop 6sutk1YHMk9MClqD1+XWo4tGeqaZ68pvAG1PaGlGFxy/8qdoiL503/BAY4MRsydrosg7CbZdvjh qSfMtedNAMoqyttE0LjvMNoL4fPptgTM= X-Gm-Gg: ATEYQzwm3gFwQhNIAQB9vTh9PROqP7XUxHCI1HdZlDMijXRP8m2GTLxzAs0f3ZY92/a V281gm6T3u3/noqDtqmqdpgvqC4nNxVNFH0UXOLJSbfiq5KB0B5P/FFfvzpFobZ6utuC2QZxlUQ xosAjccG8nlTbyQxmC1rkMmgOw2UUvw3Bt+4uh9uB2DS4hzLg6sIfwBoLSccwsKwcFlJpkVElFx Ia/xjf2rBkeFweOkJfwHZ06XLA7jD95mYhNOQndeNXqlOvqEoM7gIcOY+/whYzpoS5/2367fD0r eCOxZ6MphP/7eFQHg8Z5gGAwZhDqP7uuqycp6VZTuvVGkYEAVi4= X-Received: by 2002:a17:90b:3b87:b0:341:88d5:a74e with SMTP id 98e67ed59e1d1-35a220811a4mr6069836a91.29.1773497680092; Sat, 14 Mar 2026 07:14:40 -0700 (PDT) MIME-Version: 1.0 References: <20260312.105500.450470685315646651.ishii@postgresql.org> <20260314.164003.2119775273148039288.ishii@postgresql.org> In-Reply-To: <20260314.164003.2119775273148039288.ishii@postgresql.org> Reply-To: assam258@gmail.com From: Henson Choi Date: Sat, 14 Mar 2026 23:14:26 +0900 X-Gm-Features: AaiRm52Umsgs5t7QmErHA2irDdqJhMFMEpG8hIgfFUOZcc_11BuW-ZaEfYkEN6I Message-ID: Subject: Re: Row pattern recognition To: Tatsuo Ishii Cc: zsolt.parragi@percona.com, vik@postgresfriends.org, er@xs4all.nl, jacob.champion@enterprisedb.com, david.g.johnston@gmail.com, peter@eisentraut.org, pgsql-hackers@postgresql.org Content-Type: multipart/alternative; boundary="0000000000003b6283064cfc9bb3" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000003b6283064cfc9bb3 Content-Type: text/plain; charset="UTF-8" Hi Tatsuo, Looks great. Do you have any idea how to let the existing ExecEvalExpr > handle the swap/restore mechanism? Yes. I think the key insight is that ExecEvalExpr compiles expressions into a flat array of ExprEvalStep operations, which makes the swap/restore natural to implement without any changes to the ExecEvalExpr caller interface. I should also say that this approach was shaped by the constraints you identified early on -- the three-slot model's limitations and the need for variable offsets. Without that clear problem framing, I would not have thought in this direction. The idea is to introduce two new ExprEvalOp steps: EEOP_RPR_NAV_SET -- save current slot, swap to target row EEOP_RPR_NAV_RESTORE -- restore original slot These two steps bracket the inner expression steps in the flat array. For `price > PREV(price)`: 1. EEOP_OUTER_VAR -- fetch price from current slot --> datum_a 2. EEOP_RPR_NAV_SET -- save current slot, swap to (currentpos - 1) 3. EEOP_OUTER_VAR -- fetch price from swapped slot --> datum_b 4. EEOP_RPR_NAV_RESTORE -- restore original slot 5. EEOP_GT -- evaluate datum_a > datum_b 6. EEOP_DONE For `price > PREV(price, 3)` (with offset): 1. EEOP_OUTER_VAR -- fetch price from current slot --> datum_a 2. EEOP_CONST -- evaluate offset constant 3 --> datum_off 3. EEOP_RPR_NAV_SET -- save current slot, swap to (currentpos - 3) 4. EEOP_OUTER_VAR -- fetch price from swapped slot --> datum_b 5. EEOP_RPR_NAV_RESTORE -- restore original slot 6. EEOP_GT -- evaluate datum_a > datum_b 7. EEOP_DONE For `price < NEXT(price)`: 1. EEOP_OUTER_VAR -- fetch price from current slot --> datum_a 2. EEOP_RPR_NAV_SET -- save current slot, swap to (currentpos + 1) 3. EEOP_OUTER_VAR -- fetch price from swapped slot --> datum_b 4. EEOP_RPR_NAV_RESTORE -- restore original slot 5. EEOP_LT -- evaluate datum_a < datum_b 6. EEOP_DONE ExprState holds the flat array of ExprEvalSteps, and each step carries its own payload. The RPR navigation steps would store a pointer to the window execution state in their payload, so that they can access the current row position and the tuplestore at evaluation time. Since PREV/NEXT are only permitted in the DEFINE clause, the window execution state is always valid when these steps are evaluated, which eliminates the risk of accessing stale or invalid state. The EEOP_RPR_NAV_SET step would: 1. Save the current slot for later restore 2. Compute the target position from currentpos and the direction offset 3. Fetch the corresponding TupleTableSlot from the tuplestore 4. Replace econtext->ecxt_outertuple with the target slot The EEOP_RPR_NAV_RESTORE step would: 1. Retrieve the previously saved slot 2. Restore econtext->ecxt_outertuple to the original slot This design also fits well with the future roadmap for FIRST/LAST navigation. Once the match history infrastructure is in place, FIRST/LAST can look up the first or last row matched by a given pattern variable from the history, compute the target position from that, and pass it to EEOP_RPR_NAV_SET -- the save/restore mechanism itself stays the same. I would like to work on an experimental implementation of this approach, including the parser-level check to reject nested PREV/NEXT calls as required by the standard. The nesting check will be part of the same implementation rather than a separate fix, as the parser structure may need to be adjusted as part of the redesign anyway. Since the expression compilation infrastructure is not an area I am deeply familiar with, it may take some time to get right. I would appreciate your patience in reviewing it when it is ready. Best regards, Henson --0000000000003b6283064cfc9bb3 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Tatsuo,

Looks great. Do you have any idea how to let the existing ExecEvalExpr
handle the swap/restore mechanism?


Yes. I th= ink the key insight is that ExecEvalExpr compiles expressions into
a fla= t array of ExprEvalStep operations, which makes the swap/restore naturalto implement without any changes to the ExecEvalExpr caller interface.
=
I should also say that this approach was shaped by the constraints you<= br>identified early on -- the three-slot model's limitations and the ne= ed
for variable offsets. Without that clear problem framing, I would not=
have thought in this direction.

The idea is to introduce two new= ExprEvalOp steps:

=C2=A0 EEOP_RPR_NAV_SET = =C2=A0 =C2=A0 -- save current slot, swap to target row
=C2=A0 EEOP_RPR_N= AV_RESTORE -- restore original slot

These two steps bracket t= he inner expression steps in the flat array.

For `price > PREV(price)`:
=C2=A0 1. EEOP_OUTER_VAR =C2=A0 =C2=A0 = =C2=A0-- fetch price from current slot --> datum_a
=C2=A0 2. EEOP_RPR= _NAV_SET =C2=A0 =C2=A0-- save current slot, swap to (currentpos - 1)
=C2= =A0 3. EEOP_OUTER_VAR =C2=A0 =C2=A0 =C2=A0-- fetch price from swapped slot = --> datum_b
=C2=A0 4. EEOP_RPR_NAV_RESTORE -- restore original slot=C2=A0 5. EEOP_GT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- evaluate d= atum_a > datum_b
=C2=A0 6. EEOP_DONE

For `price > PREV(pric= e, 3)` (with offset):
=C2=A0 1. EEOP_OUTER_VAR =C2=A0 =C2=A0 =C2=A0-- fe= tch price from current slot --> datum_a
=C2=A0 2. EEOP_CONST =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0-- evaluate offset constant 3 --> datum_off=C2=A0 3. EEOP_RPR_NAV_SET =C2=A0 =C2=A0-- save current slot, swap to (cu= rrentpos - 3)
=C2=A0 4. EEOP_OUTER_VAR =C2=A0 =C2=A0 =C2=A0-- fetch pric= e from swapped slot --> datum_b
=C2=A0 5. EEOP_RPR_NAV_RESTORE -- res= tore original slot
=C2=A0 6. EEOP_GT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 -- evaluate datum_a > datum_b
=C2=A0 7. EEOP_DONE

For `= price < NEXT(price)`:
=C2=A0 1. EEOP_OUTER_VAR =C2=A0 =C2=A0 =C2=A0--= fetch price from current slot --> datum_a
=C2=A0 2. EEOP_RPR_NAV_SET= =C2=A0 =C2=A0-- save current slot, swap to (currentpos + 1)
=C2=A0 3. E= EOP_OUTER_VAR =C2=A0 =C2=A0 =C2=A0-- fetch price from swapped slot --> d= atum_b
=C2=A0 4. EEOP_RPR_NAV_RESTORE -- restore original slot
=C2=A0= 5. EEOP_LT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- evaluate datum_a &= lt; datum_b
=C2=A0 6. EEOP_DONE

ExprState holds the flat a= rray of ExprEvalSteps, and each step carries
its own payload. The RPR na= vigation steps would store a pointer to the
window execution state in th= eir payload, so that they can access the
current row position and the tu= plestore at evaluation time. Since
PREV/NEXT are only permitted in the D= EFINE clause, the window execution
state is always valid when these step= s are evaluated, which eliminates
the risk of accessing stale or invalid= state.

The EEOP_RPR_NAV_SET step would:
=C2=A0 1. Save the curre= nt slot for later restore
=C2=A0 2. Compute the target position from cur= rentpos and the direction offset
=C2=A0 3. Fetch the corresponding Tuple= TableSlot from the tuplestore
=C2=A0 4. Replace econtext->ecxt_outert= uple with the target slot

The EEOP_RPR_NAV_RESTORE step would:
= =C2=A0 1. Retrieve the previously saved slot
=C2=A0 2. Restore econtext-= >ecxt_outertuple to the original slot

This design also fits well = with the future roadmap for FIRST/LAST navigation.
Once the match histor= y infrastructure is in place, FIRST/LAST can look
up the first or last r= ow matched by a given pattern variable from the
history, compute the tar= get position from that, and pass it to
EEOP_RPR_NAV_SET -- the save/rest= ore mechanism itself stays the same.

I would like to work on an expe= rimental implementation of this approach,
including the parser-level che= ck to reject nested PREV/NEXT calls as
required by the standard. The nes= ting check will be part of the same
implementation rather than a separat= e fix, as the parser structure may
need to be adjusted as part of the re= design anyway.

Since the expression compilation infrastructure is no= t an area I am
deeply familiar with, it may take some time to get right.= I would
appreciate your patience in reviewing it when it is ready.
<= br>Best regards,
Henson=C2=A0
--0000000000003b6283064cfc9bb3--