Expand description
Incremental driver over the row-pattern Nfa.
The batch matcher Nfa::find_matches_dynamic rescans the whole buffer from position 0 on every
call. Under append-only input (rows arriving in ORDER BY order) most of that work is redundant:
AFTER MATCH SKIP makes the matches before a committed skip-resume point immutable, because no
row appended after them can change a match that already terminated before them. This wrapper
keeps that skip-resume point as a scan cursor and, on each IncrementalMatcher::advance, reruns
find_matches_dynamic only over the suffix that can still change — never reimplementing the
NFA traversal (and so never bypassing its greedy/reluctant preference, fresh-visited-scope, or
WITHIN invariants).
Matches are anchored by row seq rather than buffer position so they stay stable when earlier
rows are evicted and finalized (see IncrementalMatcher::finalize_evicted_prefix); positions are an
internal detail of the current buffer.
Freezing rule (see IncrementalMatcher::advance): a match — and the scan region behind it up
to its skip-resume position — freezes only once every position in that region is dead at the
current boundary per Nfa::reaches_boundary_alive (the same liveness predicate row eviction
uses). A dead position’s scan outcome can never change under appended rows, because no path from
it can consume past the old boundary; so the whole region’s scan behavior — matches found, gaps
skipped, and the resume point — is final. Any live position (a still-open trailing match, or a
gap where a longer, higher-preference alternative is still in flight) keeps the region
provisional and re-attempted on the next advance.
A late (out-of-order) row that sorts before rows already fed is handled by
IncrementalMatcher::truncate_from_seq: it rolls state back to a scan-resume point at or before
the insertion, re-verifying the freezing gate against the truncation boundary (freezing is only
sound against the boundary it was checked at), after which the caller re-feeds the corrected
sorted suffix through advance.
Scaffolding note: under the EVENT_TIME plan the upstream EowcSort makes out-of-order
feeds unreachable, so truncate_from_seq and the provisional-changelog helpers
(diff_provisional, plan_provisional_rows, fed_seqs) have no production caller and are
compiled #[cfg(test)]. They are kept — proven by the randomized differential oracle in this
module — as the invalidation machinery a future input mode that revises already-fed rows (e.g.
an emit-on-update or arrival-order mode) would need, rather than shipped as live surface.
Structs§
- Incremental
Matcher - Incremental wrapper around
Nfa::find_matches_dynamicfor append-only input. - Offset
Matcher 🔒 - Adapts a
CandidateMatcherso that a scan over the suffix[offset, ..)sees suffix-relative positions0, 1, ...while the underlying matcher still resolves absolute buffer positions. This lets us driveNfa::find_matches_dynamicover just the mutable suffix using the same matcher the batch path uses, without adding a start-offset parameter to the NFA. - Seq
- A row’s stable sequence number: the buffer-table PK tiebreaker minted at ingest, unique for the
buffer’s lifetime and stable across eviction. A newtype (not a bare
i64) so a seq can never be confused with a buffer position (a bareusize) at the incremental-matcher seams — the two are different integer spaces. The rawi64is unwrapped (.0) only at the two real boundaries: the state-table storage (de)serialization, and where the_match_idoutput datum is built. - SeqMatch
- A match anchored by row seqs (stable across eviction), not buffer positions.
start_seqis the seq of the match’s first row;end_seqis one past the seq of its last row (soend_seq - start_seqequals the row count only while seqs are contiguous).labels[i]is the pattern variable bound to the match’si-th row.
Enums§
- Finalized
- Outcome of
IncrementalMatcher::finalize_evicted_prefix: whether the matcher could finalize the evicted prefix in place (staying reusable) or the eviction shape forces the caller to drop and rebuild it.