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 pub(super) scope: Scope,
30 pub(super) extra: MyMap<Source, MyAlias>,
32 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 sea_query::Expr::Column(sea_query::ColumnRef::Column(sea_query::ColumnName(
71 Some(sea_query::TableName(None, table)),
72 col,
73 ))) => {
74 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 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 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#[derive(Clone)]
131pub struct MyTableRef {
132 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
233pub 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 #[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 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}