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 1w6qmg-004iVq-09 for pgsql-hackers@arkaria.postgresql.org; Sun, 29 Mar 2026 14:06:34 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w6qme-00GYJq-1d for pgsql-hackers@arkaria.postgresql.org; Sun, 29 Mar 2026 14:06:32 +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 1w6qme-00GYJb-0U for pgsql-hackers@lists.postgresql.org; Sun, 29 Mar 2026 14:06:32 +0000 Received: from mail-pj1-x102f.google.com ([2607:f8b0:4864:20::102f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w6qmW-00000001pZT-2zxq for pgsql-hackers@postgresql.org; Sun, 29 Mar 2026 14:06:27 +0000 Received: by mail-pj1-x102f.google.com with SMTP id 98e67ed59e1d1-3591cc98871so1547618a91.3 for ; Sun, 29 Mar 2026 07:06:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1774793183; cv=none; d=google.com; s=arc-20240605; b=hJy38FCe5FyaoEr2uF1ky2P5rIHT2H/lTU2WyzkGZg9aKbT+XkzIQIWIAubXYFUnOr 3KmPYEabYziix+fHR2KMQU4ASKiyL62rRkZsfN944WHSa5RU0CZnptIyeMmkZaCv0IbB 2aH/NNqZmfF8bk7tMOqFIxFgOhhC14eRVxZad0dIP/7HfPMy9tVbfXhmsrn4TW+WV51Y LkPJEG81uo6hMXUCOnUcbQ/gNFozPm0gTtS76di6VSUTQtdXWaLnC0shMbGkezWGCAKg HursUgeUmGOCBgr5wnskLBq/PcToS1VCHSoCUAGBFug6XTECwHjd7VQC3bkfAhvuYpsF aDMQ== 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=CjwI+2+pkHGtMSv68n7G4qx7yW4zK7HnTBysFVH+hjk=; fh=539V9jz0FJIbNczCzULf9NvdL2PCrRvdxpOGANUFJpk=; b=TBTUZ7HPjs5EDAhVOE34OaF/Q0I8P15Y/6YdNydcjrR/i3H0suxuMptf1l1FaU7QrV 1X6tTrt7v+h+91x4Y1PYN49/nE93EqiNUPdGY3qpzXB3YtU3zauJwzjbr4Z7ZNlEXF47 CQSCdpV236uge1RyKyNc6F+zFKlL0vuYqbEiv/PGSyY0S4kv+2Nk3aREI+YvNZlRgOrF SPnvlfc+Gzun4dHm9Yyyjqhm0dtQLyAC4y6i3h4cFtsPDlJXQsrxSs825SEO0DBHHLt/ BcZ/oyTsoqCiQKRegSRqmtynT062GvcfvD50VVcK30VMEOuDk7NRxVsxLCrMeAaQD9kO L4Bg==; 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=20251104; t=1774793183; x=1775397983; 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=CjwI+2+pkHGtMSv68n7G4qx7yW4zK7HnTBysFVH+hjk=; b=AdcaztM6EY8uPHmj9LPKR7etxJFFwLS62I7CF+rv8DAi2kF7J/wfaSkrCxqgQQKe8P j/AO13JOI2MgBa547dODOTk8P8MRg3bPSOPu+UY6mcvPyrqFv8IBe+KU7HmEM8zw+MLD tPDalAW16j7DmuGaFXh70xXkten3P6cnTIad1HY7dgRYa2AOsAoBGCUvsyknjji46M3U oZ5NXd1ZNNuV7azc7cxfW3nLqadm5qPkQGojsJGr27+wQAFqOKoo9xtxsIz6tzVoGTck 76eq5FmytaPvPpUHGYVdA5QuxTrup9uqoW4iZEQJs5ZUbD8svSfpEHweiLEIDqveYZr2 Eopg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774793183; x=1775397983; 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=CjwI+2+pkHGtMSv68n7G4qx7yW4zK7HnTBysFVH+hjk=; b=jEWfjahcZUVHjlOY231/LdRG0ZtcT3KEEFQ48X05TkqP/bFTK2oJa6oONpjMgiBf3y gtKwS+79/Xmf2lYd8Eu9OWOE/OsnWL+g4xlpZZgLgliKNhGhxEjjsXVikAlOMhmmw3Jd c+5JGovIN/4Ck+Dw4NHhDAR1xf5kKZtHY/YCOilwSiA6qkNY/SQ8qykDdSfskWBlMbvq /8Hxfu422QSnykcFrRcwOaBr5pX405xceqN81zE7QrQIHVcCbqMn3ro+j1hO0gq3oIhP YbeRq4igbPeV9Ds2JXeNmqDfn2CY7WfGX585tZyqrEm+NiX6jkGN1sYZdmpsHCGuJIJp DHsQ== X-Forwarded-Encrypted: i=1; AJvYcCXODYJlI3QyQlC3OrVZCWsyU+9EujrHSQqkYkBoBBhPQ6a8aPt4je7QzOHmLWW3sv8HXATcr+5AOAsqB0A5@postgresql.org X-Gm-Message-State: AOJu0YzbAi+dL1etVMmlPuFth9OY9zgcmgudHV5ocQVLx0RoM28a8zIK +2yh8x3yXhduHwuuZwdko1XMmy7J/Y9tHVy3pDEA9c/k8TCNrMJb+CXKaDb5jryNg37HXnF85sE NWpcnu5usE7uF820Eod08KRxnpO6/Enw= X-Gm-Gg: ATEYQzyMHyTpkhmN6qqmqfcC5gfTau5+FxYnA+W4xfVY/M3S+UTWwBiyAegJ4bREODk AcqEz+FlEBYjLj4d2ZD9lSEy8LLejed5qSdOQZhDSCagKXEejk0/TZpjFC9ZxmdLuMCVVsn1k98 YYxjbrf5duIQxuEdRkNRDGF6c2EyfuZVTFhy0/e7c2FcSSwInJlkg3608zAKemp22A6kF/TlnpX TFHEZaanZF/ksV4s2wLi/BbsrcCvITHNhYTXf1NviVSWb8AiO5an80Agkk955DUkDhze/c7Rjeo RtRUN8gShKjzpjJ05Gq+0Z2ivZNAf3hdnUE/iGyG X-Received: by 2002:a17:90b:28cd:b0:35d:99c2:8e6f with SMTP id 98e67ed59e1d1-35d99c29090mr3457852a91.20.1774793182687; Sun, 29 Mar 2026 07:06:22 -0700 (PDT) MIME-Version: 1.0 References: <20260321.231629.266287379124559013.ishii@postgresql.org> <20260322.142326.546998002680149319.ishii@postgresql.org> In-Reply-To: Reply-To: assam258@gmail.com From: Henson Choi Date: Sun, 29 Mar 2026 23:06:11 +0900 X-Gm-Features: AQROBzCw20bjWQhO_aq54udTbPz0nKOgrE0Rpm3NWf9DZfS7vXYZuPhndyv3thc Message-ID: Subject: Re: Row pattern recognition To: Tatsuo Ishii Cc: 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, pgsql-hackers@postgresql.org Content-Type: multipart/alternative; boundary="0000000000003435c3064e2a3d30" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000003435c3064e2a3d30 Content-Type: text/plain; charset="UTF-8" Hi Tatsuo, Separately from the PREV/NEXT patches, I have been analyzing how FIRST and LAST navigation would interact with the current NFA engine -- particularly context absorption and DEFINE evaluation sharing. This is not a proposal to implement them now; rather, I wanted to document the design constraints while the analysis is fresh, so we have a reference when we are ready to add them. Design Note: FIRST/LAST Navigation for RPR =========================================== Scope: This note covers only patterns without quantifiers, where each pattern variable consumes exactly one row. 1. Navigation Function Classification -------------------------------------- The SQL standard (ISO/IEC 19075-5:2021) defines four navigation functions in two categories: PREV/NEXT -- physical navigation: - Scope: entire row pattern partition - Offset: physical (row position within partition) - Base row: the row denoted by the variable under running semantics (in our no-qualifier scope: current eval row) - Default offset: 1 - Can reach rows outside the match (standard 5.6.2) - Always RUNNING semantics only FIRST/LAST -- logical navigation: - Standard scope: rows mapped to a specific variable - Our scope (no quantifier): match_start to currentpos range - Offset: logical (in standard), but equivalent to physical when each variable maps to at most one row - Base: match_start (FIRST) or currentpos (LAST) - Default offset: 0 (unlike PREV/NEXT which default to 1) - RUNNING semantics in DEFINE; FINAL available in MEASURES FIRST/LAST results depend on the match start position (match_start). Each current row in window RPR triggers an independent match attempt with its own match_start. FIRST always depends on match_start. LAST(price) depends only on currentpos and is match_start-independent, but LAST(price, N) depends on match_start because the NULL boundary check (currentpos - N < match_start) varies with match_start. Example (PATTERN (A B C), match_start = 3): FIRST(price) = R3.price (match_start row) FIRST(price, 1) = R4.price (match_start + 1) LAST(price) = R5.price (currentpos row) LAST(price, 1) = R4.price (currentpos - 1) LAST(price, 3) = NULL (before match_start) PREV(price, 2) = R3.price (pos - 2, physical) NEXT(price, 2) = R7.price (pos + 2, beyond match) PREV/NEXT can reach any row in the partition regardless of match boundaries. FIRST/LAST are valid only within the match_start to currentpos range. 2. match_start and NFA Contexts ------------------------------- Each match_start defines one match attempt, which corresponds to one NFA context. Different contexts have different match_start values. Context absorption merges contexts when one context's pattern states are covered by another's. If the merged contexts have different match_start values, FIRST/LAST would produce incorrect results because the surviving context's match_start would be used for both. 3. Compound Navigation (PREV/NEXT wrapping FIRST/LAST) ------------------------------------------------------ The standard (5.6.4) permits nesting FIRST/LAST inside PREV/NEXT. Using the standard's notation with variable qualifiers (which our no-quantifier scope does not yet require): PREV(LAST(A.Price + A.Tax, 1), 3) Evaluation proceeds in three steps: 1. Inner FIRST/LAST: determines target row position only (does not evaluate the expression) 2. Outer PREV/NEXT: applies physical offset from that row 3. Expression evaluation: at the final destination row only The reverse nesting (FIRST/LAST wrapping PREV/NEXT) and same-kind nesting (PREV inside PREV, etc.) are prohibited. Since the inner function only determines a position, the compound operation can be flattened into a single RPRNavExpr node internally, requiring only one slot swap. 4. Context Absorption Rules ---------------------------- Absorption safety depends on whether the navigation function depends on match_start: DEFINE content | match_start dep. | absorption --------------------------------|------------------|---------- No navigation | none | safe PREV/NEXT only | none | safe LAST (no offset) | none | safe LAST (with offset) | boundary check | unsafe FIRST (any) | direct | unsafe Compound (inner FIRST) | direct | unsafe Compound (inner LAST, no off.) | none | safe Compound (inner LAST, with off.)| boundary check | unsafe The criterion: if the expression depends on match_start in any way, absorption is unsafe. Comparing match_start values at runtime is impractical, so we treat any dependency as unconditionally unsafe. 5. DEFINE Clause Evaluation Rules --------------------------------- Currently, nfa_evaluate_row(pos) evaluates all DEFINE variables once per row and shares the results across all NFA contexts. DEFINE content | evaluation --------------------------------|-------------------------- No navigation | shared (once per row) PREV/NEXT only | shared (once per row) LAST (no offset) | shared (once per row) LAST (with offset) | per-context FIRST (any) | per-context Compound (inner FIRST) | per-context Compound (inner LAST, no off.) | shared (once per row) Compound (inner LAST, with off.)| per-context 6. Slot Swap Elision -------------------- When the final target position resolves to currentpos, the slot swap can be elided. This applies to: - PREV(price, 0), NEXT(price, 0): offset 0 - LAST(price), LAST(price, 0): always currentpos - Compound cases where offsets cancel, e.g., NEXT(LAST(price, 1), 1) The PREV/NEXT infrastructure (RPRNavExpr, EEOP_RPR_NAV_SET/ RESTORE, nav_winobj) can be reused directly for FIRST/LAST -- it would mainly require adding two RPRNavKind values and match_start arithmetic in the ExecEvalRPRNavSet switch. No variable qualifiers, match history tracking, or CLASSIFIER infrastructure would be needed for this no-quantifier scope. I do think CLASSIFIER, MEASURES, and match history tracking should stay out of the initial patch set -- they add significant complexity to NFA contexts and affect absorption in ways that are hard to validate incrementally. Variable qualifiers and quantifier support for navigation functions are also tied to match history infrastructure, so they should be deferred as well. Better to get the current feature set stable first. With PREV/NEXT now resolved (0006), the next major milestone would be match history infrastructure (needed for CLASSIFIER, MEASURES, variable qualifiers, and quantifier-aware navigation). Before that larger step, there are a few remaining items that can be done within the current architecture: Optimization: - PREFIX pattern absorption (e.g., A (B+) C) [1][2] - Fixed-length iteration group absorption (e.g., (A B{2})+ ) [3] Feature: - FIRST/LAST navigation (this note) - LLVM JIT proper support for slot swap (0006 uses interpreter fallback) [4][5] [1] https://www.postgresql.org/message-id/CAAAe_zAEg7sVM%3DWDwXMyE-odGmQyXSVi5ZzWgye6SupSjdMKpg%40mail.gmail.com [2] https://www.postgresql.org/message-id/CAAAe_zDq7R8CaDfmh8C%2BH3_639Y5LtJD%2B2Z%3D1txDt%3DvaOr90rQ%40mail.gmail.com [3] https://www.postgresql.org/message-id/CAAAe_zBY%2BEqk_DQpS0cy1Eb67H9v92tyaf%2BU%2BSKKuLGUs_z%2BEA%40mail.gmail.com [4] https://www.postgresql.org/message-id/CAAAe_zBbrnx2fjK2s%2BJgx6TSOdnKAPawXbHeX49WqmX9ji%2BHdg%40mail.gmail.com [5] https://www.postgresql.org/message-id/CAAAe_zBCF3dwSjStmG0kJqw_y1z8QD73Rf1G58QTKEvd9tScwA%40mail.gmail.com The question is how much of this to complete before the current patches are committed. The patch set is already substantial, and the next CommitFest is in July. What do you think -- should we try to include some of these items in the current round, or focus on getting what we have committed first? Also, do you see any blockers that might prevent the current patch set from being committed? Best regards, Henson > --0000000000003435c3064e2a3d30 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Tatsuo,

Separately from the PRE= V/NEXT patches, I have been analyzing
how FIRST and LAST navigation woul= d interact with the current
NFA engine -- particularly context absorptio= n and DEFINE
evaluation sharing. This is not a proposal to implement the= m
now; rather, I wanted to document the design constraints
while the = analysis is fresh, so we have a reference when we
are ready to add them.=


Design Note: FIRST/LAST Navigation for RPR
=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

Scope: This note covers only= patterns without quantifiers, where
each pattern variable consumes exac= tly one row.


1. Navigation Function Classification
----------= ----------------------------

The SQL standard (ISO/IEC 19075-5:2021)= defines four navigation
functions in two categories:

PREV/NEXT -= - physical navigation:
=C2=A0 - Scope: entire row pattern partition
= =C2=A0 - Offset: physical (row position within partition)
=C2=A0 - Base = row: the row denoted by the variable under running
=C2=A0 =C2=A0 semanti= cs (in our no-qualifier scope: current eval row)
=C2=A0 - Default offset= : 1
=C2=A0 - Can reach rows outside the match (standard 5.6.2)
=C2=A0= - Always RUNNING semantics only

FIRST/LAST -- logical navigation:=C2=A0 - Standard scope: rows mapped to a specific variable
=C2=A0 - O= ur scope (no quantifier): match_start to currentpos range
=C2=A0 - Offse= t: logical (in standard), but equivalent to physical
=C2=A0 =C2=A0 when = each variable maps to at most one row
=C2=A0 - Base: match_start (FIRST)= or currentpos (LAST)
=C2=A0 - Default offset: 0 (unlike PREV/NEXT which= default to 1)
=C2=A0 - RUNNING semantics in DEFINE; FINAL available in = MEASURES

FIRST/LAST results depend on the match start position
(m= atch_start). Each current row in window RPR triggers an
independent matc= h attempt with its own match_start. FIRST
always depends on match_start.= LAST(price) depends only on
currentpos and is match_start-independent, = but LAST(price, N)
depends on match_start because the NULL boundary chec= k
(currentpos - N < match_start) varies with match_start.

Exam= ple (PATTERN (A B C), match_start =3D 3):

= =C2=A0 =C2=A0 FIRST(price) =C2=A0 =C2=A0=3D R3.price =C2=A0 (match_start ro= w)
=C2=A0 =C2=A0 FIRST(price, 1) =3D R4.price =C2=A0 (match_start + 1)=C2=A0 =C2=A0 LAST(price) =C2=A0 =C2=A0 =3D R5.price =C2=A0 (currentpos r= ow)
=C2=A0 =C2=A0 LAST(price, 1) =C2=A0=3D R4.price =C2=A0 (currentpos -= 1)
=C2=A0 =C2=A0 LAST(price, 3) =C2=A0=3D NULL =C2=A0 =C2=A0 =C2=A0 (be= fore match_start)

=C2=A0 =C2=A0 PREV(price, 2) =C2=A0=3D R3.price = =C2=A0 (pos - 2, physical)
=C2=A0 =C2=A0 NEXT(price, 2) =C2=A0=3D R7.pri= ce =C2=A0 (pos + 2, beyond match)


PREV/NEXT can reach any row= in the partition regardless of
match boundaries. FIRST/LAST are valid o= nly within the
match_start to currentpos range.


2. match_star= t and NFA Contexts
-------------------------------

Each match_sta= rt defines one match attempt, which corresponds
to one NFA context. Diff= erent contexts have different
match_start values. Context absorption mer= ges contexts when
one context's pattern states are covered by anothe= r's. If the
merged contexts have different match_start values, FIRST= /LAST
would produce incorrect results because the surviving context'= s
match_start would be used for both.


3. Compound Navigation = (PREV/NEXT wrapping FIRST/LAST)
----------------------------------------= --------------

The standard (5.6.4) permits nesting FIRST/LAST insid= e
PREV/NEXT. Using the standard's notation with variable
qualifie= rs (which our no-quantifier scope does not yet
require):

=C2=A0 = =C2=A0 PREV(LAST(A.Price + A.Tax, 1), 3)

Evaluation proceeds in thre= e steps:
=C2=A0 1. Inner FIRST/LAST: determines target row position only=
=C2=A0 =C2=A0 =C2=A0(does not evaluate the expression)
=C2=A0 2. Out= er PREV/NEXT: applies physical offset from that row
=C2=A0 3. Expression= evaluation: at the final destination row only

The reverse nesting (= FIRST/LAST wrapping PREV/NEXT) and
same-kind nesting (PREV inside PREV, = etc.) are prohibited.

Since the inner function only determines a pos= ition, the
compound operation can be flattened into a single RPRNavExpr<= br>node internally, requiring only one slot swap.


4. Context Abs= orption Rules
----------------------------

Absorption safety depe= nds on whether the navigation function
depends on match_start:

=C2=A0 DEFINE content =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| match_start dep. | absorption
=C2=A0= --------------------------------|------------------|----------
=C2=A0 N= o navigation =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= | none =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | safe
=C2=A0 PREV/NEX= T only =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| none= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | safe
=C2=A0 LAST (no offset= ) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| none =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | safe
=C2=A0 LAST (with offset) =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| boundary check =C2=A0 | unsafe=C2=A0 FIRST (any) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 | direct =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | unsafe
= =C2=A0 Compound (inner FIRST) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| direct = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | unsafe
=C2=A0 Compound (inner LAST,= no off.) =C2=A0| none =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | safe
= =C2=A0 Compound (inner LAST, with off.)| boundary check =C2=A0 | unsafe
=
The criterion: if the expression depends on match_start in
an= y way, absorption is unsafe. Comparing match_start values
at runtime is = impractical, so we treat any dependency as
unconditionally unsafe.

5. DEFINE Clause Evaluation Rules
--------------------------------= -

Currently, nfa_evaluate_row(pos) evaluates all DEFINE
variables= once per row and shares the results across all NFA
contexts.

=C2=A0 DEFINE content =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| evaluation
=C2=A0 ---------------------= -----------|--------------------------
=C2=A0 No navigation =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | shared (once per row= )
=C2=A0 PREV/NEXT only =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0| shared (once per row)
=C2=A0 LAST (no offset) =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| shared (once per row)
=C2= =A0 LAST (with offset) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| pe= r-context
=C2=A0 FIRST (any) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 | per-context
=C2=A0 Compound (inner FIRST) = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| per-context
=C2=A0 Compound (inner L= AST, no off.) =C2=A0| shared (once per row)
=C2=A0 Compound (inner LAST,= with off.)| per-context


6. Slot Swap Elision
--------= ------------

When the final target position resolves to currentpos, = the
slot swap can be elided. This applies to:
=C2=A0 - PREV(price, 0)= , NEXT(price, 0): offset 0
=C2=A0 - LAST(price), LAST(price, 0): always = currentpos
=C2=A0 - Compound cases where offsets cancel, e.g.,
=C2=A0= =C2=A0 NEXT(LAST(price, 1), 1)


The PREV/NEXT infrastructure (RP= RNavExpr, EEOP_RPR_NAV_SET/
RESTORE, nav_winobj) can be reused directly = for FIRST/LAST --
it would mainly require adding two RPRNavKind values a= nd
match_start arithmetic in the ExecEvalRPRNavSet switch.
No variabl= e qualifiers, match history tracking, or CLASSIFIER
infrastructure would= be needed for this no-quantifier scope.

I do think CLASSIFIER, MEAS= URES, and match history tracking
should stay out of the initial patch se= t -- they add
significant complexity to NFA contexts and affect absorpti= on
in ways that are hard to validate incrementally. Variable
qualifie= rs and quantifier support for navigation functions
are also tied to matc= h history infrastructure, so they should
be deferred as well. Better to = get the current feature set
stable first.

With PREV/NEXT now reso= lved (0006), the next major milestone
would be match history infrastruct= ure (needed for CLASSIFIER,
MEASURES, variable qualifiers, and quantifie= r-aware
navigation). Before that larger step, there are a few
remaini= ng items that can be done within the current
architecture:

Optimi= zation:
=C2=A0 - PREFIX pattern absorption (e.g., A (B+) C) [1][2]
= =C2=A0 - Fixed-length iteration group absorption
=C2=A0 =C2=A0 (e.g., (A= B{2})+ ) [3]

Feature:
=C2=A0 - FIRST/LAST navigation (this note)=
=C2=A0 - LLVM JIT proper support for slot swap (0006 uses
=C2=A0 =C2= =A0 interpreter fallback) [4][5]

[1] https://www.postgresql.org/message-id/CAAAe_zAEg7sVM%3DWDwXM= yE-odGmQyXSVi5ZzWgye6SupSjdMKpg%40mail.gmail.com
[2] https://www.postgresql.org/message-id/= CAAAe_zDq7R8CaDfmh8C%2BH3_639Y5LtJD%2B2Z%3D1txDt%3DvaOr90rQ%40mail.gmail.co= m
[3] https://ww= w.postgresql.org/message-id/CAAAe_zBY%2BEqk_DQpS0cy1Eb67H9v92tyaf%2BU%2BSKK= uLGUs_z%2BEA%40mail.gmail.com
[4] https://www.postgresql.org/message-id/CAAAe_zBbrnx2fjK2s%2BJg= x6TSOdnKAPawXbHeX49WqmX9ji%2BHdg%40mail.gmail.com
[5] https://www.postgresql.org/message-id/CAAAe_z= BCF3dwSjStmG0kJqw_y1z8QD73Rf1G58QTKEvd9tScwA%40mail.gmail.com

Th= e question is how much of this to complete before the
current patches ar= e committed. The patch set is already
substantial, and the next CommitFe= st is in July. What do
you think -- should we try to include some of the= se items
in the current round, or focus on getting what we have
commi= tted first? Also, do you see any blockers that might
prevent the current= patch set from being committed?


Best regards,
Henson
--0000000000003435c3064e2a3d30--