Skip to main content

graphrecords_query/operations/
on_missing.rs

1use crate::{
2    Bare, BareValueDomain, Explain, IndexDomain, Indexed, Multiple, OrderState, QueryResult,
3    Single, ValueDomain,
4    element::{Dropping, ElementEmission, Retention},
5    execution::EvaluationCache,
6    explain::ExplainFormatter,
7    operands::OperandHandle,
8    operations::{
9        Alignment, ArgumentSource, Keyed, Lookup, Prepare, SourceDomain,
10        policy::{Drop, Replace},
11    },
12    optimizer::{Estimate, Estimated, PlanIdentity, PlanInputs, PlanNode, Stats},
13};
14use graphrecords_core::GraphRecord;
15use std::{
16    fmt::{self, Write},
17    hash::Hasher,
18    marker::PhantomData,
19};
20
21pub trait MaybeAbsent<A: Alignment>: ArgumentSource<A> {
22    fn on_missing<P>(self, policy: P) -> WithMissing<A, Self, P>
23    where
24        Self: Sized,
25        P: MissingPolicy<A, Self>,
26    {
27        WithMissing::new(self, policy)
28    }
29}
30
31pub trait MissingPolicy<A: Alignment, S: SourceDomain>:
32    Send + Sync + Clone + 'static + Explain + PlanIdentity + PlanInputs
33{
34    type Retention: Retention;
35
36    type Prepared<'a>: Clone + 'a
37    where
38        Self: 'a;
39
40    fn prepare<'a>(
41        &'a self,
42        graphrecord: &'a GraphRecord,
43        cache: &'a EvaluationCache<'a>,
44    ) -> QueryResult<Self::Prepared<'a>>;
45
46    fn resolve_absent<'a>(
47        prepared: &Self::Prepared<'a>,
48        address: &A::Address<'a>,
49        label: &'static str,
50    ) -> <Self::Retention as ElementEmission>::Step<
51        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
52    >
53    where
54        S: 'a;
55}
56
57impl<A: Alignment, S: SourceDomain> MissingPolicy<A, S> for Drop {
58    type Prepared<'a> = ();
59    type Retention = Dropping;
60
61    fn prepare<'a>(
62        &'a self,
63        _graphrecord: &'a GraphRecord,
64        _cache: &'a EvaluationCache<'a>,
65    ) -> QueryResult<Self::Prepared<'a>> {
66        Ok(())
67    }
68
69    fn resolve_absent<'a>(
70        _prepared: &Self::Prepared<'a>,
71        _address: &A::Address<'a>,
72        _label: &'static str,
73    ) -> <Self::Retention as ElementEmission>::Step<
74        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
75    >
76    where
77        S: 'a,
78    {
79        None
80    }
81}
82
83impl<A, S, R> MissingPolicy<A, S> for Replace<R>
84where
85    A: Alignment,
86    S: SourceDomain,
87    R: ArgumentSource<A, S::ValueDomain> + Clone,
88{
89    type Prepared<'a> = R::Prepared<'a>;
90    type Retention = R::Retention;
91
92    fn prepare<'a>(
93        &'a self,
94        graphrecord: &'a GraphRecord,
95        cache: &'a EvaluationCache<'a>,
96    ) -> QueryResult<Self::Prepared<'a>> {
97        self.replacement().prepare(graphrecord, cache)
98    }
99
100    fn resolve_absent<'a>(
101        prepared: &Self::Prepared<'a>,
102        address: &A::Address<'a>,
103        label: &'static str,
104    ) -> <Self::Retention as ElementEmission>::Step<
105        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
106    >
107    where
108        S: 'a,
109    {
110        R::resolve(prepared, address, label)
111    }
112}
113
114impl<I: IndexDomain, V: ValueDomain, O: OrderState> MaybeAbsent<Keyed<I>>
115    for OperandHandle<Indexed<I, V>, Multiple<O>>
116{
117}
118impl<A: Alignment, V: BareValueDomain> MaybeAbsent<A> for OperandHandle<Bare<V>, Single> {}
119
120pub struct WithMissing<A: Alignment, S: MaybeAbsent<A>, P> {
121    inner: S,
122    policy: P,
123    alignment: PhantomData<fn() -> A>,
124}
125
126impl<A: Alignment, S: MaybeAbsent<A>, P> WithMissing<A, S, P> {
127    #[must_use]
128    pub fn new(inner: S, policy: P) -> Self {
129        Self {
130            inner,
131            policy,
132            alignment: PhantomData,
133        }
134    }
135}
136
137impl<A: Alignment, S: MaybeAbsent<A> + Clone, P: Clone> Clone for WithMissing<A, S, P> {
138    fn clone(&self) -> Self {
139        Self {
140            inner: self.inner.clone(),
141            policy: self.policy.clone(),
142            alignment: PhantomData,
143        }
144    }
145}
146
147impl<A: Alignment, S: MaybeAbsent<A>, P: Explain> Explain for WithMissing<A, S, P> {
148    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
149        self.inner.describe(formatter)?;
150        formatter.write_str(" on_missing(")?;
151        self.policy.describe(formatter)?;
152        formatter.write_str(")")
153    }
154}
155
156impl<A: Alignment, S: MaybeAbsent<A>, P: PlanIdentity> PlanIdentity for WithMissing<A, S, P> {
157    fn identity_eq(&self, other: &Self) -> bool {
158        self.inner.identity_eq(&other.inner) && self.policy.identity_eq(&other.policy)
159    }
160
161    fn identity_hash<H: Hasher>(&self, state: &mut H) {
162        self.inner.identity_hash(state);
163        self.policy.identity_hash(state);
164    }
165}
166
167impl<A: Alignment, S: MaybeAbsent<A>, P: PlanInputs> PlanInputs for WithMissing<A, S, P> {
168    fn inputs(&self) -> Vec<&dyn PlanNode> {
169        let mut inputs = self.inner.inputs();
170        inputs.extend(self.policy.inputs());
171
172        inputs
173    }
174}
175
176impl<A: Alignment, S: MaybeAbsent<A>, P: MissingPolicy<A, S>> Prepare for WithMissing<A, S, P> {
177    type Prepared<'a>
178        = (S::Prepared<'a>, P::Prepared<'a>)
179    where
180        Self: 'a;
181
182    fn prepare<'a>(
183        &'a self,
184        graphrecord: &'a GraphRecord,
185        cache: &'a EvaluationCache<'a>,
186    ) -> QueryResult<Self::Prepared<'a>> {
187        Ok((
188            self.inner.prepare(graphrecord, cache)?,
189            self.policy.prepare(graphrecord, cache)?,
190        ))
191    }
192}
193
194impl<A: Alignment, S: MaybeAbsent<A>, P> Estimated for WithMissing<A, S, P> {
195    fn estimate(&self, stats: &Stats) -> Estimate {
196        self.inner.estimate(stats)
197    }
198}
199
200impl<A: Alignment, S: MaybeAbsent<A>, P> SourceDomain for WithMissing<A, S, P> {
201    type ValueDomain = S::ValueDomain;
202}
203
204impl<A: Alignment, S: MaybeAbsent<A>, P: MissingPolicy<A, S>> ArgumentSource<A>
205    for WithMissing<A, S, P>
206{
207    type Retention = P::Retention;
208
209    fn lookup<'a, 'prepared>(
210        prepared: &'prepared Self::Prepared<'a>,
211        address: &A::Address<'a>,
212    ) -> Lookup<'prepared, QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>>
213    where
214        Self: 'a,
215    {
216        S::lookup(&prepared.0, address)
217    }
218
219    fn resolve<'a>(
220        prepared: &Self::Prepared<'a>,
221        address: &A::Address<'a>,
222        label: &'static str,
223    ) -> <Self::Retention as ElementEmission>::Step<
224        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
225    >
226    where
227        Self: 'a,
228    {
229        match S::lookup(&prepared.0, address) {
230            Lookup::Present(wrapped) => P::Retention::keep(wrapped.clone()),
231            Lookup::Absent(_) => P::resolve_absent(&prepared.1, address, label),
232        }
233    }
234}