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::{
11    cell::{Cell, OnceCell},
12    fmt::Debug,
13    marker::PhantomData,
14    ops::Deref,
15    rc::Rc,
16};
17
18use crate::{
19    IntoExpr, Table,
20    db::TableRow,
21    lower,
22    mutable::Mutable,
23    private::IntoJoinable,
24    scoped_transaction::{MutTemp, TransactionScope},
25};
26pub use db_typ::{DbTyp, StorableTyp};
27
28pub trait NumTyp: OrdTyp + Clone + Copy {
29    const ZERO: &str;
30}
31
32impl NumTyp for i64 {
33    const ZERO: &str = "0";
34}
35impl NumTyp for f64 {
36    const ZERO: &str = "0.0";
37}
38
39pub trait OrdTyp: EqTyp {}
40impl OrdTyp for String {}
41impl OrdTyp for Vec<u8> {}
42impl OrdTyp for i64 {}
43impl OrdTyp for f64 {}
44impl OrdTyp for bool {}
45#[cfg(feature = "jiff-02")]
46impl OrdTyp for jiff::Timestamp {}
47#[cfg(feature = "jiff-02")]
48impl OrdTyp for jiff::civil::Date {}
49
50pub trait BuffTyp: DbTyp {}
51impl BuffTyp for String {}
52impl BuffTyp for Vec<u8> {}
53
54#[diagnostic::on_unimplemented(
55    message = "Columns with type `{Self}` can not be checked for equality",
56    note = "`EqTyp` is also implemented for all table types"
57)]
58pub trait EqTyp: DbTyp {}
59
60impl EqTyp for String {}
61impl EqTyp for Vec<u8> {}
62impl EqTyp for i64 {}
63impl EqTyp for f64 {}
64impl EqTyp for bool {}
65#[cfg(feature = "jiff-02")]
66impl EqTyp for jiff::Timestamp {}
67#[cfg(feature = "jiff-02")]
68impl EqTyp for jiff::civil::Date {}
69#[diagnostic::do_not_recommend]
70impl<T: Table> EqTyp for TableRow<T> {}
71
72/// Should not be used outside this crate.
73pub trait OptTable: DbTyp {
74    type Schema;
75    type Mutable<'t>;
76
77    fn into_mutable<'t>(
78        txn: &'t mut TransactionScope<Self::Schema>,
79        val: Self,
80    ) -> Self::Mutable<'t>;
81}
82
83impl<T: Table> OptTable for TableRow<T> {
84    type Schema = T::Schema;
85    type Mutable<'t> = Mutable<'t, T>;
86
87    fn into_mutable<'t>(
88        txn: &'t mut TransactionScope<Self::Schema>,
89        inp: Self,
90    ) -> Self::Mutable<'t> {
91        txn.tmp = Cell::new(vec![MutTemp::new(inp)]);
92        Mutable::new(&mut *Cell::get_mut(&mut txn.tmp)[0])
93    }
94}
95
96impl<T: Table> OptTable for Option<TableRow<T>> {
97    type Schema = T::Schema;
98    type Mutable<'t> = Option<Mutable<'t, T>>;
99
100    fn into_mutable<'t>(
101        txn: &'t mut TransactionScope<Self::Schema>,
102        val: Self,
103    ) -> Self::Mutable<'t> {
104        val.map(|x| TableRow::<T>::into_mutable(txn, x))
105    }
106}
107
108/// This is an expression that can be used in queries.
109///
110/// - The lifetime parameter `'column` specifies which columns need to be in scope.
111/// - The type parameter `S` specifies the expected schema of the query.
112/// - And finally the type paramter `T` specifies the type of the expression.
113///
114/// [Expr] implements [Deref] to have column fields in case the expression has a table type.
115pub struct Expr<'column, S, T: DbTyp> {
116    pub(crate) _local: PhantomData<*const ()>,
117    pub(crate) inner: Rc<lower::Expr>,
118    pub(crate) _p: PhantomData<&'column ()>,
119    pub(crate) _p2: PhantomData<S>,
120    pub(crate) ext: OnceCell<Box<T::Ext<'static>>>,
121    // is this expressions not null and a foreign key?
122    pub(crate) not_null_key: bool,
123}
124
125#[cfg_attr(false, mutants::skip)]
126impl<S, T: DbTyp> Debug for Expr<'_, S, T> {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        write!(f, "Expr of type {}", std::any::type_name::<T>())
129    }
130}
131
132impl<'column, S, T: DbTyp> Expr<'column, S, T> {
133    /// Extremely easy to use API. Should only be used by the macro to implement migrations.
134    #[doc(hidden)]
135    pub fn _migrate<OldS>(prev: impl IntoExpr<'column, OldS>) -> Self {
136        let prev = prev.into_expr().inner;
137        Self::new(prev)
138    }
139}
140
141pub fn adhoc_expr<S, T: DbTyp>(f: lower::Expr) -> Expr<'static, S, T> {
142    Expr::adhoc(f)
143}
144
145pub fn new_column<'x, S, C: DbTyp, T: Table>(
146    table: impl IntoExpr<'x, S, Typ = TableRow<T>>,
147    name: &'static str,
148) -> Expr<'x, S, C> {
149    let table = table.into_expr();
150    Expr::new_inner(
151        table.inner.col(T::NAME, name, T::ID, table.not_null_key),
152        table.not_null_key && !C::NULLABLE,
153    )
154}
155
156pub fn unique_from_joinable<'inner, T: Table>(
157    j: impl IntoJoinable<'inner, T::Schema, Typ = TableRow<T>>,
158) -> Expr<'inner, T::Schema, Option<TableRow<T>>> {
159    let joinable = j.into_joinable();
160    let unique = Rc::new(lower::Unique {
161        table: joinable.table.name,
162        conds: joinable.conds,
163        guaranteed: false,
164    });
165    Expr::adhoc(lower::Expr::RowIndex(lower::RowLike::Unique(unique), T::ID))
166}
167
168impl<S, T: DbTyp> Expr<'_, S, T> {
169    pub(crate) fn adhoc(e: lower::Expr) -> Self {
170        Self::new(Rc::new(e))
171    }
172
173    pub(crate) fn new(val: Rc<lower::Expr>) -> Self {
174        Self::new_inner(val, false)
175    }
176
177    pub(crate) fn new_inner(val: Rc<lower::Expr>, not_null_key: bool) -> Self {
178        Self {
179            _local: PhantomData,
180            inner: val,
181            _p: PhantomData,
182            _p2: PhantomData,
183            ext: OnceCell::new(),
184            not_null_key,
185        }
186    }
187}
188
189impl<S, T: DbTyp> Clone for Expr<'_, S, T> {
190    fn clone(&self) -> Self {
191        Self {
192            _local: PhantomData,
193            inner: self.inner.clone(),
194            _p: self._p,
195            _p2: self._p2,
196            ext: OnceCell::new(),
197            not_null_key: self.not_null_key,
198        }
199    }
200}
201
202impl<'t, T: Table> Deref for Expr<'t, T::Schema, TableRow<T>> {
203    type Target = T::Ext2<'t>;
204
205    fn deref(&self) -> &Self::Target {
206        T::covariant_ext(self.ext.get_or_init(|| {
207            let expr = Expr {
208                _local: PhantomData,
209                inner: self.inner.clone(),
210                _p: PhantomData::<&'static ()>,
211                _p2: PhantomData,
212                ext: OnceCell::new(),
213                not_null_key: self.not_null_key,
214            };
215            Box::new(T::build_ext2(&expr))
216        }))
217    }
218}