From 2e062833fb363874896bc2ba2d931d6f522a55c4 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Wed, 27 May 2026 14:15:09 +0900 Subject: [PATCH 20/26] Add reluctant bounded mid-band test to rpr_nfa PATTERN (A{3,5}? B) drives the VAR-level count in nfa_advance_var through 3, 4, 5 within a single match attempt -- a band the existing A{1,3}? B test does not exercise, because A and B match the same row there and advance's early-termination path frees the loop state before nfa_advance_var sees count > 2. This explicitly exercises the count > 2 && reluctant && !isAbsorbable path that absorbability analysis structurally constrains (reluctant quantifiers are excluded, so isAbsorbable stays false). --- src/test/regress/expected/rpr_nfa.out | 38 +++++++++++++++++++++++++++ src/test/regress/sql/rpr_nfa.sql | 29 ++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/test/regress/expected/rpr_nfa.out b/src/test/regress/expected/rpr_nfa.out index fe5bb324df0..1f494d2db34 100644 --- a/src/test/regress/expected/rpr_nfa.out +++ b/src/test/regress/expected/rpr_nfa.out @@ -2237,6 +2237,44 @@ WINDOW w AS ( 4 | {B,_} | | (4 rows) +-- A{3,5}? B (reluctant bounded mid-band): the VAR-level count in +-- nfa_advance_var cycles through 3, 4, 5 within a single match +-- attempt. Exercises the count > 2 && reluctant && !isAbsorbable +-- branch (absorbability analysis excludes reluctant quantifiers, so +-- isAbsorbable stays false for A). +WITH test_reluctant_mid_band AS ( + SELECT * FROM (VALUES + (1, ARRAY['A']), + (2, ARRAY['A']), + (3, ARRAY['A']), + (4, ARRAY['A']), + (5, ARRAY['A']), + (6, ARRAY['B']) + ) AS t(id, flags) +) +SELECT id, flags, + first_value(id) OVER w AS match_start, + last_value(id) OVER w AS match_end +FROM test_reluctant_mid_band +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + PATTERN (A{3,5}? B) + DEFINE + A AS 'A' = ANY(flags), + B AS 'B' = ANY(flags) +); + id | flags | match_start | match_end +----+-------+-------------+----------- + 1 | {A} | 1 | 6 + 2 | {A} | | + 3 | {A} | | + 4 | {A} | | + 5 | {A} | | + 6 | {B} | | +(6 rows) + -- ============================================================ -- Pathological Pattern Runtime Protection -- ============================================================ diff --git a/src/test/regress/sql/rpr_nfa.sql b/src/test/regress/sql/rpr_nfa.sql index 7a5b5c41b24..76dfc4d88bc 100644 --- a/src/test/regress/sql/rpr_nfa.sql +++ b/src/test/regress/sql/rpr_nfa.sql @@ -1554,6 +1554,35 @@ WINDOW w AS ( B AS 'B' = ANY(flags) ); +-- A{3,5}? B (reluctant bounded mid-band): the VAR-level count in +-- nfa_advance_var cycles through 3, 4, 5 within a single match +-- attempt. Exercises the count > 2 && reluctant && !isAbsorbable +-- branch (absorbability analysis excludes reluctant quantifiers, so +-- isAbsorbable stays false for A). +WITH test_reluctant_mid_band AS ( + SELECT * FROM (VALUES + (1, ARRAY['A']), + (2, ARRAY['A']), + (3, ARRAY['A']), + (4, ARRAY['A']), + (5, ARRAY['A']), + (6, ARRAY['B']) + ) AS t(id, flags) +) +SELECT id, flags, + first_value(id) OVER w AS match_start, + last_value(id) OVER w AS match_end +FROM test_reluctant_mid_band +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + PATTERN (A{3,5}? B) + DEFINE + A AS 'A' = ANY(flags), + B AS 'B' = ANY(flags) +); + -- ============================================================ -- Pathological Pattern Runtime Protection -- ============================================================ -- 2.50.1 (Apple Git-155)