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}