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 1w20gO-000ncW-0B for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 05:40:05 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w20gN-007UQU-0P for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 05:40:03 +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 1w20gM-007UQL-2d for pgsql-hackers@lists.postgresql.org; Mon, 16 Mar 2026 05:40:03 +0000 Received: from mail-pl1-x62e.google.com ([2607:f8b0:4864:20::62e]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w20gL-00000000N9a-0ZFi for pgsql-hackers@postgresql.org; Mon, 16 Mar 2026 05:40:03 +0000 Received: by mail-pl1-x62e.google.com with SMTP id d9443c01a7336-2ae3a2f6007so31890125ad.2 for ; Sun, 15 Mar 2026 22:40:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773639599; cv=none; d=google.com; s=arc-20240605; b=OrUwnRCBhuyRfDl/i9ZqiSKjjJ3qabEvCB8mCIWPMbUw8gsKUwnyQKigVVzqZS/2Ym ofYc8QGV7mz76xDjyl+Q/NNaOSJqjkgrgu5tI4XXCZqJWPTACGYQW3w6yrOF8yjS30RA +XQPaJ2Ie0QM7tUsw1ysP53PDnCG2aDbJVDYplYRxmupDC9nRBgpTXbVkYuFpEw2bRFk JidqdDPg6Pexhu09i8S6mIinI51tvX6JvJBkFz95cLDxcJaMYiu7bRyuL8ToA2+H5oXf yc0JBHwGZXGu6jUGaK/27yyfbx/1GeBvScSvs3aG81oGidFKPKg9HuZ/EJHPvujlifTz TDPg== 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=rKjKUYnvhQ7r0ePsGSgOZWQD/vgKQCxZWigCE2v/ZEo=; fh=BwS6kHdux9dVqwxaxHTHVrT7jsnbPz+NrBoxkroF9+8=; b=U9fHz21bxZImq2obNKB+sBzjgioAFQFTyNHqpiIpcNx7zpw5GIZwVWA6Rhd63UVih8 tl9Ji8AypAbFmFhFGFP5Oi9iJrQewFjFc5yeNrqPIvni46qfdCeEnd8LD+iyqgy6zSDv SiKQXAs0jUQoCSrJxvfAqx94sDziGdidIMNTICctm0Mi8o+ZVrEiuzd3IhHN+13eIDQR gkXbH7qK1eTlcoZuA/njMg3V4r1PYI7ydvCLmYQGugu4OBe0sD5eWpbjLGT0V8tBh8dW w46Q53da+4ePwKFEfnq3Asp4ZBEdgaMB1tkgownNf2/l/SGAt13W80gZh/8cDxMLt77O Ay0A==; 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=1773639599; x=1774244399; 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=rKjKUYnvhQ7r0ePsGSgOZWQD/vgKQCxZWigCE2v/ZEo=; b=gTyztTDE2lA0fF/3Djg8jMJAGLZ5vwh/sJjZbHb7eY/+bi6lieMpjiK8m/4j6ijFrY orCyHVyY94t2OYuyAy4Sq4fWKNwRK7IibQJP04aG8BTHp+uTRT43CnfDoFi6zZemiJco 5nTEn/p+vpIeu+twvPAAuQcgV9kJxKegji3lOgsXTeROESfguG76Gz6Vwuc06x98Xmww IUKs/qBtz1dajlPnO3jsPL4W+erP51gdI0DOVZ0jxI9V8D6zJ6uL4U3zWLapsEnIYKXZ os0Pj4eSHimAskj0p/oHjz5vySRQbEW9OkoufBhr/sgKci4+klRAe98oiktQ3rWDQfv2 Gb/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773639599; x=1774244399; 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=rKjKUYnvhQ7r0ePsGSgOZWQD/vgKQCxZWigCE2v/ZEo=; b=gPdN8mG5futRYIJOA3FlZooFeHV+fuP1boS0lU1lZi0rXWmqPa6yDKAAZa7bGEUmiO cwVDY8AJEUkOIXPIPJ3Dnz4H6z4eT1VGvzYj/WH0EWFnzBSeHzOggEHwa/nDdQ0diIcd m/poyW5+iVvH3WyFCVu/mY3C1z4sC/H76zW0TmtSsxnKsBiZkW4mG5Ao1c55urJCxdLe 0R6jnsVHKt2n4/kx1rHn4p8mqdbb67b/bQRPrP7y7MM5DZlu615RJ/L3lTPWmNBaJzEH sbMkI5BFrsz6lmS9ZyIi8QxrPy7hzWWYsIYlSLULVDa/b+wxCwqY1NT2knyWzIeEuR8e Z8xg== X-Forwarded-Encrypted: i=1; AJvYcCVgYOGI14VBudeXYeK3YzSmL6oluwbmU5zF7h5FCrrswqiaDN5D5orXTr2zNnRAt49EWxQDlanh+VUu/BaY@postgresql.org X-Gm-Message-State: AOJu0Yx9X7RvFvFOtnRIUL7zC2f3F/0isUurQj3vWnE3tSc0A69OEQ0q WD8aWziaSYCOBfUFp8tvvbkA+4MK2inu48+Gqxn7fQ+Q/nWTL6waaJGwf+elUon1NvJkOcUGzDY xjruykgjE8DV/2v7ZqKMoNYCOIFZ/Inw= X-Gm-Gg: ATEYQzz12toWYWKgR2XO+kEOtmHXA3UOZPpcILqSR/1AUotb8OKFdniMTWnVCEpzvlT Coc+8fV/78qU5d+7nWPMK/H/7lUUB47N5RCic8UersHdj8w3im+RsMrebCmuSszmbEDzImMtjpK 6dN9zVPhNhryHTwbFEQNCQgpxr5LvP3pF5j+xXWlqCRZfEx+wuiFnDisrj3R2XQSah9fZU6mYAP BhsX8lKI+RKpYGfPYn1k9/akQggdaVc2Aerit749M/hBVHbd7duDlvXNUrbqCFdoY/+VP4/UKtS U+a7cjbav4eIXzdFMLsOQinJls//ptNS5zPedA2TO8elibWUWw== X-Received: by 2002:a17:902:d501:b0:2ae:bd12:f830 with SMTP id d9443c01a7336-2aecac9b8e0mr134267165ad.49.1773639599444; Sun, 15 Mar 2026 22:39:59 -0700 (PDT) MIME-Version: 1.0 References: <20260314.164003.2119775273148039288.ishii@postgresql.org> <20260315.185936.1050650813705779576.ishii@postgresql.org> In-Reply-To: <20260315.185936.1050650813705779576.ishii@postgresql.org> Reply-To: assam258@gmail.com From: Henson Choi Date: Mon, 16 Mar 2026 14:39:47 +0900 X-Gm-Features: AaiRm525b8jjqfCwPSPWZ7zymB_8krL4kvWj80_XiKs7tSOcqbkT_mohOavOKk4 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="00000000000048d90a064d1da66a" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000048d90a064d1da66a Content-Type: text/plain; charset="UTF-8" Hi Tatsuo, Thank you for the careful review and excellent questions. I was not able to find the definition for EEOP_GT. Is this a new one? > You're right -- EEOP_GT does not exist. I oversimplified the example. In PostgreSQL, comparison operators like `>` are not dedicated opcodes. They are dispatched through EEOP_FUNCEXPR variants as regular function calls. For example, `price > PREV(price)`: - The parser resolves `>` to the appropriate pg_operator entry - The expression compiler emits an EEOP_FUNCEXPR variant - At runtime, op->d.func.fn_addr points to the operator function, I am afraid ExecEvalExpr does not allow this because ExecEvalExpr does > not accept arguments like WindowObject or WindowAggState that are > necessary for accessing the tuplestore created for the Window > aggregate node. This is the key question. You are right that ExecEvalExpr's signature does not accept WindowAggState, and I do not intend to change it. The approach is to pass WindowAggState through the step's own payload, the same way EEOP_WINDOW_FUNC carries WindowFuncExprState in op->d.window_func.wfstate. Each ExprEvalStep has a union of per-opcode payload structs. A new payload for RPR navigation would look like: struct { WindowAggState *winstate; /* access to tuplestore */ int64 offset; /* signed offset: PREV=-1, NEXT=+1 */ } rpr_nav; The expression compiler (ExecInitExprRec) populates this when it encounters a PREV/NEXT node, storing the pointer to the enclosing WindowAggState. At runtime, EEOP_RPR_NAV_SET retrieves it from the payload and uses it to fetch the target row: /* pseudo-code for EEOP_RPR_NAV_SET */ winstate = op->d.rpr_nav.winstate; /* 1. save current slot into winstate for later restore */ winstate->saved_outertuple = econtext->ecxt_outertuple; /* 2. compute target position */ target_pos = winstate->currentpos + op->d.rpr_nav.offset; /* 3. fetch target row from tuplestore via winstate */ target_slot = fetch_row_from_tuplestore(winstate, target_pos); /* 4. swap */ econtext->ecxt_outertuple = target_slot; /* pseudo-code for EEOP_RPR_NAV_RESTORE */ winstate = op->d.rpr_nav.winstate; econtext->ecxt_outertuple = winstate->saved_outertuple; The key difference from EEOP_WINDOW_FUNC: existing steps read precomputed values, but PREV/NEXT requires fetching a *different* row at runtime -- because the target depends on the current row position during pattern matching, which is not known in advance. The mechanism for passing state is the same (step payload), but the runtime behavior is a departure from the existing pattern. I have examined the alternatives carefully: - The precomputation approach (EEOP_WINDOW_FUNC style) does not apply here: there is no fixed set of values to precompute, because the target row depends on the current position during pattern matching. - The three-slot model (current implementation) cannot support variable offsets like PREV(price, 3), and requires varno rewriting that adds complexity elsewhere. - A callback-based approach (registering a fetch function in econtext) would still require passing WindowAggState somehow, just through a different indirection. The step payload approach is the only path I have found that: - Leaves ExecEvalExpr's signature unchanged - Eliminates varno rewriting entirely - Supports variable offsets naturally - Extends to FIRST/LAST navigation later If anyone sees a cleaner path, I would genuinely welcome the discussion. I plan to have an experimental implementation ready before next week. The initial version will pass WindowAggState broadly; we can narrow the interface to the minimum required during review. Let's continue the discussion with actual code. Best regards, Henson --00000000000048d90a064d1da66a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Tatsuo,

Thank = you for the careful review and excellent questions.
<= br>
I was not able to find the definition for EEOP_GT. Is this a new one?

You're right -- EEOP_GT does not exist. I ove= rsimplified the example.

In PostgreSQL, comparison operators like `&= gt;` are not dedicated opcodes.
They are dispatched through EEOP_FUNCEXP= R variants as regular function
calls. For example, `price > PREV(pric= e)`:

=C2=A0 - The parser resolves `>` to the appropriate pg_opera= tor entry
=C2=A0 - The expression compiler emits an EEOP_FUNCEXPR varian= t
=C2=A0 - At runtime, op->d.func.fn_addr points to the operator func= tion,

I am afraid ExecEvalExpr does not allow this because ExecEvalExpr does
not accept arguments like WindowObject or WindowAggState that are
necessary for accessing the tuplestore created for the Window
aggregate node.

This is the key question. You ar= e right that ExecEvalExpr's signature
does not accept WindowAggState= , and I do not intend to change it.

The approach is to pass WindowAg= gState through the step's own payload,
the same way EEOP_WINDOW_FUNC= carries WindowFuncExprState in
op->d.window_func.wfstate. Each ExprE= valStep has a union of per-opcode
payload structs. A new payload for RPR= navigation would look like:

=C2=A0 =C2=A0 = struct
=C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 WindowAggState *wi= nstate; =C2=A0 /* access to tuplestore */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 in= t64 =C2=A0 =C2=A0 =C2=A0 offset; =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* signed offs= et: PREV=3D-1, NEXT=3D+1 */
=C2=A0 =C2=A0 } =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 rpr_nav;

The expression compiler (ExecInitExprRec) pop= ulates this when it
encounters a PREV/NEXT node, storing the pointer to = the enclosing
WindowAggState. At runtime, EEOP_RPR_NAV_SET retrieves it = from the
payload and uses it to fetch the target row:

=C2=A0 =C2=A0 /* pseudo-code for EEOP_RPR_NAV_SET */
=C2= =A0 =C2=A0 winstate =3D op->d.rpr_nav.winstate;
=C2=A0 =C2=A0 /* 1. s= ave current slot into winstate for later restore */
=C2=A0 =C2=A0 winsta= te->saved_outertuple =3D econtext->ecxt_outertuple;
=C2=A0 =C2=A0 = /* 2. compute target position */
=C2=A0 =C2=A0 target_pos =3D winstate-&= gt;currentpos + op->d.rpr_nav.offset;
=C2=A0 =C2=A0 /* 3. fetch targe= t row from tuplestore via winstate */
=C2=A0 =C2=A0 target_slot =3D fetc= h_row_from_tuplestore(winstate, target_pos);
=C2=A0 =C2=A0 /* 4. swap */=
=C2=A0 =C2=A0 econtext->ecxt_outertuple =3D target_slot;

=C2= =A0 =C2=A0 /* pseudo-code for EEOP_RPR_NAV_RESTORE */
=C2=A0 =C2=A0 wins= tate =3D op->d.rpr_nav.winstate;
=C2=A0 =C2=A0 econtext->ecxt_oute= rtuple =3D winstate->saved_outertuple;


The key difference = from EEOP_WINDOW_FUNC: existing steps read
precomputed values, but PREV/= NEXT requires fetching a *different*
row at runtime -- because the targe= t depends on the current row
position during pattern matching, which is = not known in advance.
The mechanism for passing state is the same (step = payload), but
the runtime behavior is a departure from the existing patt= ern.

I have examined the alternatives carefully:

=C2=A0 - The= precomputation approach (EEOP_WINDOW_FUNC style) does not apply
=C2=A0 = =C2=A0 here: there is no fixed set of values to precompute, because the
= =C2=A0 =C2=A0 target row depends on the current position during pattern mat= ching.

=C2=A0 - The three-slot model (current implementation) cannot= support variable
=C2=A0 =C2=A0 offsets like PREV(price, 3), and require= s varno rewriting that adds
=C2=A0 =C2=A0 complexity elsewhere.

= =C2=A0 - A callback-based approach (registering a fetch function in econtex= t)
=C2=A0 =C2=A0 would still require passing WindowAggState somehow, jus= t through a
=C2=A0 =C2=A0 different indirection.

The step payload= approach is the only path I have found that:

=C2=A0 - Leaves ExecEv= alExpr's signature unchanged
=C2=A0 - Eliminates varno rewriting ent= irely
=C2=A0 - Supports variable offsets naturally
=C2=A0 - Extends t= o FIRST/LAST navigation later

If anyone sees a cleaner path, I would= genuinely welcome the
discussion.

I plan to have an experimental= implementation ready before next week.
The initial version will pass Wi= ndowAggState broadly; we can
narrow the interface to the minimum require= d during review.
Let's continue the discussion with actual code.
=
Best regards,
Henson=C2=A0
--00000000000048d90a064d1da66a--