| From: | Henson Choi <assam258(at)gmail(dot)com> |
|---|---|
| To: | Tatsuo Ishii <ishii(at)postgresql(dot)org>, jian(dot)universality(at)gmail(dot)com |
| Cc: | pgsql-hackers(at)postgresql(dot)org, zsolt(dot)parragi(at)percona(dot)com, sjjang112233(at)gmail(dot)com, vik(at)postgresfriends(dot)org, er(at)xs4all(dot)nl, jacob(dot)champion(at)enterprisedb(dot)com, david(dot)g(dot)johnston(at)gmail(dot)com, peter(at)eisentraut(dot)org, li(dot)evan(dot)chao(at)gmail(dot)com |
| Subject: | Re: Row pattern recognition |
| Date: | 2026-07-08 08:04:34 |
| Message-ID: | CAAAe_zDOz9T=Rr8rY2CCAxOCgiMcN6QF=7rAQgyAQkiEgvyNjg@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi Tatsuo, Jian,
I hit a wrong result in row pattern recognition: an alternation
whose last branch is a concatenation of quantified groups matches
one group short.
WITH d(id, dd, ee) AS (VALUES
(1, true, false), (2, false, true),
(3, true, false), (4, false, true))
SELECT id, count(*) OVER w AS cnt
FROM d
WINDOW w AS (
ORDER BY id
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
PATTERN (A | (B C)+ (D E)+)
DEFINE A AS false, B AS false, C AS false,
D AS dd, E AS ee);
id | cnt
----+-----
1 | 4 <- wrong; should be 0 (no match)
2 | 0
3 | 0
4 | 0
The rows are D E D E, with no B and no C. By precedence
PATTERN (A | (B C)+ (D E)+) means A | ((B C)+ (D E)+), so a bare
(D E)+ satisfies no branch and no row should match. Instead row 1
matches [1,4]: the pattern behaves as if it were written
A | (B C)+ | (D E)+.
Cause: the ALT branch walk follows the branch head's jump to reach
the next branch. That jump was the branch link, but the group
BEGIN's skip-past-END path was later layered onto the same field,
so jump is now overloaded. When the last branch is (B C)+ (D E)+,
the BEGIN of (B C)+ jumps past its END to the BEGIN of (D E)+, and
the walk mistakes that following group for another alternative.
The same overloaded jump is walked in three places:
nfa_advance_alt (match advance), computeAbsorbabilityRecursive
(absorption marking), and the deparse. nfa_advance_alt gives the
wrong result above. computeAbsorbabilityRecursive over-marks the
trailing group as absorbable, though that turns out inert at run
time. The deparse already sidesteps the overload in
rpr_next_branch() with the relative test elem[j-1].next != j, so
EXPLAIN prints the pattern correctly.
Fix direction: at its core, separate the BEGIN jump (group skip)
from the ALT branch-link jump -- they share one field today, which
is the overload. I am weighing two ways to do that.
One is to move the group BEGIN's skip-past-END onto next. Group
entry is already the contiguous BEGIN+1, so next is free, and jump
is left to be the branch link only. No new elements.
The other is explicit branch-separator markers (a new SEP varid)
that carry the branch link, chained ALT.jump -> SEP -> ... ->
jump = -1 on the last one; branch content still reaches the
post-ALT element through next.
Either way the ALT walk sees jump as a branch link or -1, so
nfa_advance_alt, computeAbsorbabilityRecursive and the deparse can
enumerate branches the same clean way, dropping the relative test
and the depth-based break. Deparse output stays the same.
I can put together a patch along these lines if the direction
looks right.
Best regards,
Henson
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Daniel Gustafsson | 2026-07-08 08:11:13 | Re: allow spread checkpoints when changing checksums online |
| Previous Message | Тестова Екатерина | 2026-07-08 07:43:47 | Re: VACUUM FULL or CREATE INDEX fails with error: missing chunk number 0 for toast value XXX |