Skip to main content

rust_query/
value.rs

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