Skip to main content

solverforge_scoring/stream/joiner/
plan.rs

1/* Typed condition planning: concrete indexes and exact residual matching.
2
3Equality conjunctions normalize into one heterogeneous composite hash key,
4including equality components separated by predicates or range conditions.
5Mixed plans index equality and retain the authored residual predicates.
6Ordered and interval plans execute their concrete candidate indexes; arbitrary
7predicates explicitly scan. No closure inspection or dynamic condition list.
8*/
9
10use super::match_condition::AndJoiner;
11use super::{EqualJoiner, FilteringJoiner};
12use super::{GreaterThanJoiner, GreaterThanOrEqualJoiner, LessThanJoiner, LessThanOrEqualJoiner};
13use super::{Joiner, OverlappingJoiner};
14mod composite;
15mod conjunction;
16pub use composite::{
17    ComposePlans, CompositeEquality, EqualityKeys, EqualityKind, EqualityPlan, ResidualKind,
18};
19mod executable;
20mod hash_scan;
21mod mixed_equality;
22pub use mixed_equality::EqualityWithResidual;
23mod ordered_interval;
24pub use executable::{CompileCondition, ExecutablePlan, IndexedPlan};
25
26/* Deterministic execution strategy compiled from a condition's structure. */
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum Strategy {
29    /* Typed hash indexes on both inputs plus exact equality residual. */
30    EquiHash,
31    /* Typed ordered bounds plus exact strict/inclusive residual. */
32    OrderedScan,
33    /* Interval candidate narrowing plus exact half-open predicate. */
34    IntervalNarrow,
35    /* Explicit opposite-input scan with the exact predicate. */
36    Scan,
37}
38
39impl Strategy {
40    /* Prefer the most selective usable structural index; retain all residuals.
41    Equality > ordered > interval > explicit scan. */
42    #[inline]
43    pub const fn combine(self, other: Strategy) -> Strategy {
44        match (self, other) {
45            (Strategy::EquiHash, _) | (_, Strategy::EquiHash) => Strategy::EquiHash,
46            (Strategy::OrderedScan, _) | (_, Strategy::OrderedScan) => Strategy::OrderedScan,
47            (Strategy::IntervalNarrow, _) | (_, Strategy::IntervalNarrow) => {
48                Strategy::IntervalNarrow
49            }
50            (Strategy::Scan, Strategy::Scan) => Strategy::Scan,
51        }
52    }
53}
54
55/* A condition with a statically planned execution strategy.
56
57`check` is the exact residual semantic check over one borrowed left row
58and one right entity — the same predicate `matches` evaluates, exposed
59so operators apply residuals without re-dispatching through `Joiner`.
60*/
61pub trait PlannedCondition<Row, C>: Send + Sync {
62    /* The deterministic strategy for this condition's structure. */
63    const STRATEGY: Strategy;
64
65    /* Exact residual check: must agree with `Joiner::matches`. */
66    fn check(&self, row: &Row, c: &C) -> bool;
67}
68
69/* Reads the planned strategy for one condition value. */
70#[inline]
71pub fn plan_strategy<Row, C, P>(condition: &P) -> Strategy
72where
73    P: PlannedCondition<Row, C>,
74{
75    let _ = condition;
76    P::STRATEGY
77}
78
79impl<Row, C, Fa, Fb, T, Mode> PlannedCondition<Row, C> for EqualJoiner<Fa, Fb, T, Mode>
80where
81    T: PartialEq,
82    Fa: Fn(&Row) -> T + Send + Sync,
83    Fb: Fn(&C) -> T + Send + Sync,
84{
85    const STRATEGY: Strategy = Strategy::EquiHash;
86
87    #[inline]
88    fn check(&self, row: &Row, c: &C) -> bool {
89        self.matches(row, c)
90    }
91}
92
93impl<Row, C, Fa, Fb, T> PlannedCondition<Row, C> for LessThanJoiner<Fa, Fb, T>
94where
95    T: Ord,
96    Fa: Fn(&Row) -> T + Send + Sync,
97    Fb: Fn(&C) -> T + Send + Sync,
98{
99    const STRATEGY: Strategy = Strategy::OrderedScan;
100
101    #[inline]
102    fn check(&self, row: &Row, c: &C) -> bool {
103        self.matches(row, c)
104    }
105}
106
107impl<Row, C, Fa, Fb, T> PlannedCondition<Row, C> for LessThanOrEqualJoiner<Fa, Fb, T>
108where
109    T: Ord,
110    Fa: Fn(&Row) -> T + Send + Sync,
111    Fb: Fn(&C) -> T + Send + Sync,
112{
113    const STRATEGY: Strategy = Strategy::OrderedScan;
114
115    #[inline]
116    fn check(&self, row: &Row, c: &C) -> bool {
117        self.matches(row, c)
118    }
119}
120
121impl<Row, C, Fa, Fb, T> PlannedCondition<Row, C> for GreaterThanJoiner<Fa, Fb, T>
122where
123    T: Ord,
124    Fa: Fn(&Row) -> T + Send + Sync,
125    Fb: Fn(&C) -> T + Send + Sync,
126{
127    const STRATEGY: Strategy = Strategy::OrderedScan;
128
129    #[inline]
130    fn check(&self, row: &Row, c: &C) -> bool {
131        self.matches(row, c)
132    }
133}
134
135impl<Row, C, Fa, Fb, T> PlannedCondition<Row, C> for GreaterThanOrEqualJoiner<Fa, Fb, T>
136where
137    T: Ord,
138    Fa: Fn(&Row) -> T + Send + Sync,
139    Fb: Fn(&C) -> T + Send + Sync,
140{
141    const STRATEGY: Strategy = Strategy::OrderedScan;
142
143    #[inline]
144    fn check(&self, row: &Row, c: &C) -> bool {
145        self.matches(row, c)
146    }
147}
148
149impl<Row, C, Fsa, Fea, Fsb, Feb, T> PlannedCondition<Row, C>
150    for OverlappingJoiner<Fsa, Fea, Fsb, Feb, T>
151where
152    T: Ord,
153    Fsa: Fn(&Row) -> T + Send + Sync,
154    Fea: Fn(&Row) -> T + Send + Sync,
155    Fsb: Fn(&C) -> T + Send + Sync,
156    Feb: Fn(&C) -> T + Send + Sync,
157{
158    const STRATEGY: Strategy = Strategy::IntervalNarrow;
159
160    #[inline]
161    fn check(&self, row: &Row, c: &C) -> bool {
162        self.matches(row, c)
163    }
164}
165
166impl<Row, C, F> PlannedCondition<Row, C> for FilteringJoiner<F>
167where
168    F: Fn(&Row, &C) -> bool + Send + Sync,
169{
170    const STRATEGY: Strategy = Strategy::Scan;
171
172    #[inline]
173    fn check(&self, row: &Row, c: &C) -> bool {
174        self.matches(row, c)
175    }
176}
177
178impl<Row, C, J1, J2> PlannedCondition<Row, C> for AndJoiner<J1, J2>
179where
180    J1: PlannedCondition<Row, C> + Joiner<Row, C>,
181    J2: PlannedCondition<Row, C> + Joiner<Row, C>,
182{
183    const STRATEGY: Strategy = J1::STRATEGY.combine(J2::STRATEGY);
184
185    #[inline]
186    fn check(&self, row: &Row, c: &C) -> bool {
187        self.matches(row, c)
188    }
189}