Skip to main content

rust_query/
value.rs

1pub mod aggregate;
2mod db_typ;
3pub mod from_expr;
4pub mod into_expr;
5#[cfg(feature = "jiff-02")]
6mod jiff_operations;
7mod operations;
8pub mod optional;
9
10use std::{cell::OnceCell, fmt::Debug, marker::PhantomData, ops::Deref, rc::Rc};
11
12use sea_query::{Alias, JoinType, SelectStatement};
13
14use crate::{
15    IntoExpr, IntoSelect, Select, Table,
16    alias::{Field, JoinableTable, MyAlias, Scope},
17    ast::{MySelect, Source},
18    db::TableRow,
19    mutable::Mutable,
20    mymap::MyMap,
21    private::IntoJoinable,
22};
23pub use db_typ::{DbTyp, StorableTyp};
24
25#[derive(Default)]
26pub struct ValueBuilder {
27    pub(crate) from: Rc<MySelect>,
28    // only used for tables
29    pub(super) scope: Scope,
30    // implicit joins
31    pub(super) extra: MyMap<Source, MyAlias>,
32    // calculating these results
33    pub(super) forwarded: MyMap<MyTableRef, MyAlias>,
34}
35
36impl ValueBuilder {
37    pub(crate) fn get_aggr(
38        &mut self,
39        aggr: Rc<SelectStatement>,
40        conds: Vec<MyTableRef>,
41    ) -> MyAlias {
42        let source = Source {
43            kind: crate::ast::SourceKind::Aggregate(aggr),
44            conds: conds
45                .into_iter()
46                .enumerate()
47                .map(|(idx, join)| {
48                    let alias = Alias::new(join.table_name.main_column());
49                    (
50                        Field::U64(MyAlias::new(idx)),
51                        sea_query::Expr::col((self.get_table(join), alias)),
52                    )
53                })
54                .collect(),
55        };
56        let new_alias = || self.scope.new_alias();
57        *self.extra.get_or_init(source, new_alias)
58    }
59
60    pub(crate) fn get_join<T: Table>(
61        &mut self,
62        expr: sea_query::Expr,
63        possible_null: bool,
64        new_col: &'static str,
65    ) -> sea_query::Expr {
66        match &expr {
67            // we could use our own type instead of `sea_query::Expr` to make it much easier
68            // to know if we joined the table explicitly, but that would add much complexity
69            // everywhere else
70            sea_query::Expr::Column(sea_query::ColumnRef::Column(sea_query::ColumnName(
71                Some(sea_query::TableName(None, table)),
72                col,
73            ))) => {
74                // check if this table has been joined explicitly
75                if let Some(alias) = MyAlias::try_from(table)
76                    && let Some(from) = self.from.tables.get(alias.idx)
77                    && from.main_column() == col.inner().as_ref()
78                {
79                    // No need to join the table again
80                    return sea_query::Expr::col((alias, new_col));
81                }
82            }
83            _ => (),
84        };
85
86        let join_type = if possible_null {
87            JoinType::LeftJoin
88        } else {
89            JoinType::Join
90        };
91        let source = Source {
92            kind: crate::ast::SourceKind::Implicit(T::NAME.to_owned(), join_type),
93            conds: vec![(Field::Str(T::ID), expr)],
94        };
95        let new_alias = || self.scope.new_alias();
96
97        // TODO: possible optimization to unify the join_type?
98        // e.g. join + left join = join
99        let alias = *self.extra.get_or_init(source, new_alias);
100
101        sea_query::Expr::col((alias, new_col))
102    }
103
104    pub fn get_unique<T: Table>(
105        &mut self,
106        conds: Box<[(&'static str, sea_query::Expr)]>,
107    ) -> sea_query::Expr {
108        let source = Source {
109            kind: crate::ast::SourceKind::Implicit(T::NAME.to_owned(), JoinType::LeftJoin),
110            conds: conds.into_iter().map(|x| (Field::Str(x.0), x.1)).collect(),
111        };
112
113        let new_alias = || self.scope.new_alias();
114        let table = self.extra.get_or_init(source, new_alias);
115        sea_query::Expr::col((*table, Alias::new(T::ID)))
116    }
117
118    pub fn get_table(&mut self, table: MyTableRef) -> MyAlias {
119        if Rc::ptr_eq(&self.from.scope_rc, &table.scope_rc) {
120            MyAlias::new(table.idx)
121        } else {
122            *self.forwarded.get_or_init(table, || self.scope.new_alias())
123        }
124    }
125}
126
127/// This references a particular user specified join,
128/// so not any of the forwarded joins.
129/// We use this to know if the current scope has the original join or needs to forward it.
130#[derive(Clone)]
131pub struct MyTableRef {
132    // one Rc exists for each scope, so we can check if we have the right
133    // scope by comparing the Rc ptr.
134    pub(crate) scope_rc: Rc<()>,
135    pub(crate) idx: usize,
136    pub(crate) table_name: JoinableTable,
137}
138
139impl PartialEq for MyTableRef {
140    fn eq(&self, other: &Self) -> bool {
141        Rc::ptr_eq(&self.scope_rc, &other.scope_rc) && self.idx == other.idx
142    }
143}
144
145pub trait NumTyp: OrdTyp + Clone + Copy {
146    const ZERO: sea_query::Value;
147}
148
149impl NumTyp for i64 {
150    const ZERO: sea_query::Value = sea_query::Value::BigInt(Some(0));
151}
152impl NumTyp for f64 {
153    const ZERO: sea_query::Value = sea_query::Value::Double(Some(0.));
154}
155
156pub trait OrdTyp: EqTyp {}
157impl OrdTyp for String {}
158impl OrdTyp for Vec<u8> {}
159impl OrdTyp for i64 {}
160impl OrdTyp for f64 {}
161impl OrdTyp for bool {}
162#[cfg(feature = "jiff-02")]
163impl OrdTyp for jiff::Timestamp {}
164#[cfg(feature = "jiff-02")]
165impl OrdTyp for jiff::civil::Date {}
166
167pub trait BuffTyp: DbTyp {}
168impl BuffTyp for String {}
169impl BuffTyp for Vec<u8> {}
170
171#[diagnostic::on_unimplemented(
172    message = "Columns with type `{Self}` can not be checked for equality",
173    note = "`EqTyp` is also implemented for all table types"
174)]
175pub trait EqTyp: DbTyp {}
176
177impl EqTyp for String {}
178impl EqTyp for Vec<u8> {}
179impl EqTyp for i64 {}
180impl EqTyp for f64 {}
181impl EqTyp for bool {}
182#[cfg(feature = "jiff-02")]
183impl EqTyp for jiff::Timestamp {}
184#[cfg(feature = "jiff-02")]
185impl EqTyp for jiff::civil::Date {}
186#[diagnostic::do_not_recommend]
187impl<T: Table> EqTyp for TableRow<T> {}
188
189pub trait OptTable: DbTyp {
190    type Schema;
191    type Select;
192    type Mutable<'t>;
193    fn select_opt_mutable(
194        val: Expr<'_, Self::Schema, Self>,
195    ) -> Select<'_, Self::Schema, Self::Select>;
196
197    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t>;
198}
199
200impl<T: Table> OptTable for TableRow<T> {
201    type Schema = T::Schema;
202    type Select = (T::Select, TableRow<T>);
203    type Mutable<'t> = Mutable<'t, T>;
204    fn select_opt_mutable(
205        val: Expr<'_, Self::Schema, Self>,
206    ) -> Select<'_, Self::Schema, Self::Select> {
207        (T::into_select(val.clone()), val).into_select()
208    }
209
210    fn into_mutable<'t>((inner, row_id): Self::Select) -> Self::Mutable<'t> {
211        Mutable::new(T::select_mutable(inner), row_id)
212    }
213}
214
215impl<T: Table> OptTable for Option<TableRow<T>> {
216    type Schema = T::Schema;
217    type Select = Option<(T::Select, TableRow<T>)>;
218    type Mutable<'t> = Option<Mutable<'t, T>>;
219    fn select_opt_mutable(
220        val: Expr<'_, Self::Schema, Self>,
221    ) -> Select<'_, Self::Schema, Self::Select> {
222        crate::optional(|row| {
223            let val = row.and(val);
224            row.then_select((T::into_select(val.clone()), val))
225        })
226    }
227
228    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t> {
229        val.map(TableRow::<T>::into_mutable)
230    }
231}
232
233/// This is an expression that can be used in queries.
234///
235/// - The lifetime parameter `'column` specifies which columns need to be in scope.
236/// - The type parameter `S` specifies the expected schema of the query.
237/// - And finally the type paramter `T` specifies the type of the expression.
238///
239/// [Expr] implements [Deref] to have column fields in case the expression has a table type.
240pub struct Expr<'column, S, T: DbTyp> {
241    pub(crate) _local: PhantomData<*const ()>,
242    pub(crate) inner: Rc<AdHoc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr, T>>,
243    pub(crate) _p: PhantomData<&'column ()>,
244    pub(crate) _p2: PhantomData<S>,
245    pub(crate) ext: OnceCell<Box<T::Ext<'static>>>,
246}
247
248#[cfg_attr(test, mutants::skip)]
249impl<S, T: DbTyp> Debug for Expr<'_, S, T> {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        write!(f, "Expr of type {}", std::any::type_name::<T>())
252    }
253}
254
255impl<'column, S, T: DbTyp> Expr<'column, S, T> {
256    /// Extremely easy to use API. Should only be used by the macro to implement migrations.
257    #[doc(hidden)]
258    pub fn _migrate<OldS>(prev: impl IntoExpr<'column, OldS>) -> Self {
259        let prev = DynTypedExpr::erase(prev);
260        Self::adhoc(move |b| (prev.func)(b))
261    }
262}
263
264pub fn adhoc_expr<S, T: DbTyp>(
265    f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr,
266) -> Expr<'static, S, T> {
267    Expr::adhoc(f)
268}
269
270pub fn new_column<'x, S, C: DbTyp, T: Table>(
271    table: impl IntoExpr<'x, S, Typ = TableRow<T>>,
272    name: &'static str,
273) -> Expr<'x, S, C> {
274    let table = table.into_expr().inner;
275    let possible_null = table.maybe_optional;
276    Expr::adhoc_promise(
277        move |b| {
278            let main_column = table.build_expr(b);
279            b.get_join::<T>(main_column, table.maybe_optional, name)
280        },
281        possible_null,
282    )
283}
284
285pub fn unique_from_joinable<'inner, T: Table>(
286    j: impl IntoJoinable<'inner, T::Schema, Typ = TableRow<T>>,
287) -> Expr<'inner, T::Schema, Option<TableRow<T>>> {
288    let list = j.into_joinable().conds;
289    ::rust_query::private::adhoc_expr(move |_b| {
290        let list = list
291            .iter()
292            .map(|(name, col)| (*name, (col.func)(_b)))
293            .collect();
294        _b.get_unique::<T>(list)
295    })
296}
297
298pub struct AdHoc<F: ?Sized, T: ?Sized> {
299    maybe_optional: bool,
300    _p: PhantomData<T>,
301    func: F,
302}
303
304impl<F: ?Sized + Fn(&mut ValueBuilder) -> sea_query::Expr, T> AdHoc<F, T> {
305    pub fn build_expr(&self, b: &mut ValueBuilder) -> sea_query::Expr {
306        (self.func)(b)
307    }
308}
309
310impl<S, T: DbTyp> Expr<'_, S, T> {
311    pub(crate) fn adhoc(f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr) -> Self {
312        Self::adhoc_promise(f, true)
313    }
314
315    /// Only set `maybe_optional` to `false` if you are absolutely sure that the
316    /// value is not null. The [crate::optional] combinator makes this more difficult.
317    /// There is no reason to use this for values that can not be foreign keys.
318    /// This is used to optimize implicit joins from LEFT JOIN to just JOIN.
319    pub(crate) fn adhoc_promise(
320        f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr,
321        maybe_optional: bool,
322    ) -> Self {
323        Self::new(Rc::new(AdHoc {
324            func: f,
325            maybe_optional,
326            _p: PhantomData,
327        }))
328    }
329
330    pub(crate) fn new(val: Rc<AdHoc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr, T>>) -> Self {
331        Self {
332            _local: PhantomData,
333            inner: val,
334            _p: PhantomData,
335            _p2: PhantomData,
336            ext: OnceCell::new(),
337        }
338    }
339}
340
341impl<S, T: DbTyp> Clone for Expr<'_, S, T> {
342    fn clone(&self) -> Self {
343        Self {
344            _local: PhantomData,
345            inner: self.inner.clone(),
346            _p: self._p,
347            _p2: self._p2,
348            ext: OnceCell::new(),
349        }
350    }
351}
352
353#[derive(Clone)]
354pub struct DynTypedExpr {
355    pub func: Rc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr>,
356}
357
358impl DynTypedExpr {
359    pub fn new(f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr) -> Self {
360        Self { func: Rc::new(f) }
361    }
362    pub fn erase<'x, S>(expr: impl IntoExpr<'x, S>) -> Self {
363        let typed = expr.into_expr().inner;
364        Self::new(move |b| typed.build_expr(b))
365    }
366}
367
368impl<'t, T: Table> Deref for Expr<'t, T::Schema, TableRow<T>> {
369    type Target = T::Ext2<'t>;
370
371    fn deref(&self) -> &Self::Target {
372        T::covariant_ext(self.ext.get_or_init(|| {
373            let expr = Expr {
374                _local: PhantomData,
375                inner: self.inner.clone(),
376                _p: PhantomData::<&'static ()>,
377                _p2: PhantomData,
378                ext: OnceCell::new(),
379            };
380            Box::new(T::build_ext2(&expr))
381        }))
382    }
383}