rust_query/
rows.rs

1use std::{marker::PhantomData, rc::Rc};
2
3use sea_query::{ExprTrait, IntoIden};
4
5use crate::{
6    CustomJoin, Expr, Table,
7    alias::{Field, JoinableTable, TmpTable},
8    ast::MySelect,
9    db::Join,
10    joinable::IntoJoinable,
11    private::Joinable,
12    value::{DynTypedExpr, IntoExpr, MyTableRef, MyTyp},
13};
14
15/// [Rows] keeps track of all rows in the current query.
16///
17/// This is the base type for other query types like [crate::args::Aggregate] and [crate::args::Query].
18/// It contains basic query functionality like joining tables and filters.
19///
20/// [Rows] mutability is only about which rows are included.
21/// Adding new columns does not require mutating [Rows].
22pub struct Rows<'inner, S> {
23    // we might store 'inner
24    pub(crate) phantom: PhantomData<fn(&'inner ()) -> &'inner ()>,
25    pub(crate) _p: PhantomData<S>,
26    pub(crate) ast: Rc<MySelect>,
27}
28
29impl<'inner, S> Rows<'inner, S> {
30    /// Join a table, this is like a super simple [Iterator::flat_map] but for queries.
31    ///
32    /// After this operation [Rows] has rows for the combinations of each original row with each row of the table.
33    /// (Also called the "Carthesian product")
34    /// The expression that is returned refers to the joined table.
35    ///
36    /// The parameter must be a table name from the schema like `v0::User`.
37    /// This table can be filtered by `#[index]`: `rows.join(v0::User.score(100))`.
38    ///
39    /// See also [Self::filter_some] if you want to join a table that is filtered by `#[unique]`.
40    pub fn join<T: Table>(
41        &mut self,
42        j: impl IntoJoinable<'inner, S, Typ = T>,
43    ) -> Expr<'inner, S, T> {
44        let joinable = j.into_joinable();
45        let out = self.join_inner(joinable.table);
46        for (name, val) in joinable.conds {
47            let out = out.inner.clone();
48            self.filter(Expr::adhoc(move |b| {
49                sea_query::Expr::col((out.build_table(b), Field::Str(name))).eq((val.func)(b))
50            }));
51        }
52        out
53    }
54
55    #[doc(hidden)]
56    pub fn join_private<T: Table<Schema = S>>(&mut self) -> Expr<'inner, S, T> {
57        self.join(Joinable::table())
58    }
59
60    pub(crate) fn join_custom<T: CustomJoin<Schema = S>>(&mut self, t: T) -> Expr<'inner, S, T> {
61        self.join_inner(t.name())
62    }
63
64    pub(crate) fn join_tmp<T: Table<Schema = S>>(&mut self, tmp: TmpTable) -> Expr<'inner, S, T> {
65        let tmp_string = tmp.into_iden();
66        self.join_inner(JoinableTable::Normal(tmp_string))
67    }
68
69    fn join_inner<T: Table>(&mut self, name: JoinableTable) -> Expr<'inner, S, T> {
70        let table_idx = self.ast.tables.len();
71        Rc::make_mut(&mut self.ast).tables.push(name.clone());
72        Expr::new(Join::new(MyTableRef {
73            scope_rc: self.ast.scope_rc.clone(),
74            idx: table_idx,
75            table_name: name,
76        }))
77    }
78
79    // Join a vector of values.
80    // pub fn vec<V: IntoExpr<'inner>>(&mut self, vec: Vec<V>) -> Join<'inner, V::Typ> {
81    //     todo!()
82    // }
83
84    /// Filter rows based on an expression.
85    pub fn filter(&mut self, prop: impl IntoExpr<'inner, S, Typ = bool>) {
86        let prop = DynTypedExpr::erase(prop);
87        Rc::make_mut(&mut self.ast).filters.push(prop);
88    }
89
90    /// Filter out rows where this expression is [None].
91    ///
92    /// Returns a new expression with the unwrapped type.
93    pub fn filter_some<Typ: MyTyp>(
94        &mut self,
95        val: impl IntoExpr<'inner, S, Typ = Option<Typ>>,
96    ) -> Expr<'inner, S, Typ> {
97        let val = val.into_expr();
98        Rc::make_mut(&mut self.ast)
99            .filters
100            .push(DynTypedExpr::erase(val.is_some()));
101
102        // we already removed all rows with null, so this is ok.
103        Expr::adhoc_promise(move |b| val.inner.build_expr(b), false)
104    }
105}