Skip to main content

drizzle_core/expr/
mod.rs

1//! Type-safe SQL expressions: comparisons, logic, aggregates and functions.
2//!
3//! Every expression carries three facts in its type:
4//!
5//! - its SQL type, such as the dialect's integer or text type;
6//! - whether it can be NULL ([`NonNull`] or [`Null`]);
7//! - whether it is a plain value or an aggregate ([`Scalar`] or [`Agg`]).
8//!
9//! The functions in this module read those facts from their arguments and
10//! compute them for their result. Comparing a number with text, summing a text
11//! column, or calling a PostgreSQL-only function on SQLite fails to compile.
12//! Expressions also record which tables they read ([`ExprSources`]); a query
13//! checks that against its `FROM`/`JOIN` scope.
14//!
15//! Rust values (`i32`, `&str`, `bool`, ...) can be used wherever an expression
16//! is expected. They are sent as bound parameters (`?` on SQLite and MySQL,
17//! `$1`, `$2`, ... on PostgreSQL).
18//!
19//! The examples in this module use a `users` table with the columns `id`,
20//! `age` (integer), `name` (text), `email` (nullable text), `score` (nullable
21//! real), `active` (boolean) and `created_at` (timestamp).
22//!
23//! # Examples
24//!
25//! ```rust
26//! # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
27//! # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
28//! # #[derive(Clone, Debug)] struct Value(String);
29//! # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
30//! # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
31//! # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
32//! # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
33//! # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
34//! # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
35//! # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
36//! # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
37//! let adults = and(gt(users.age, 18), like(users.name, "A%"));
38//! assert_eq!(adults.sql(), r#"("users"."age" > ? AND "users"."name" LIKE ?)"#);
39//!
40//! // Arithmetic uses the Rust operators.
41//! let next_year = users.age.clone() + 1;
42//! assert_eq!(next_year.sql(), r#""users"."age" + ?"#);
43//! ```
44//!
45//! Comparing an integer column with text does not compile:
46//!
47//! ```rust,compile_fail
48//! # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
49//! # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
50//! # #[derive(Clone, Debug)] struct Value(String);
51//! # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
52//! # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
53//! # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
54//! # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
55//! # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
56//! # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
57//! # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
58//! # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
59//! let wrong = eq(users.age, "hello");
60//! ```
61
62mod agg;
63mod case;
64mod cmp;
65mod column_ops;
66mod cond;
67mod datetime;
68mod logical;
69mod math;
70mod null;
71mod ops;
72mod primitives;
73mod seq;
74mod set;
75mod string;
76mod subquery;
77mod typed;
78mod util;
79mod window;
80
81/// Zero-sized marker for type parameters a struct only carries at the type
82/// level. `fn() -> T` keeps the struct `Send`, `Sync` and covariant in `T`
83/// whatever `T` is.
84pub(crate) type TypeMarker<T> = core::marker::PhantomData<fn() -> T>;
85
86pub use agg::*;
87pub use case::*;
88pub use cmp::*;
89#[doc(hidden)]
90pub use column_ops::*;
91pub use cond::*;
92pub use datetime::*;
93pub use logical::*;
94pub use math::*;
95pub use null::*;
96pub use seq::*;
97pub use set::*;
98pub use string::*;
99pub use subquery::*;
100// ops has only trait impls - no items to re-export
101pub use typed::*;
102pub use util::*;
103pub use window::*;
104
105use crate::traits::{SQLParam, ToSQL};
106use crate::types::DataType;
107
108// =============================================================================
109// Sealed Trait Pattern
110// =============================================================================
111
112mod private {
113    pub trait Sealed {}
114}
115
116// =============================================================================
117// Nullability Markers
118// =============================================================================
119
120/// Type-level marker saying whether an expression can be NULL.
121///
122/// Implemented only by [`NonNull`] and [`Null`]. The associated types compute
123/// the nullability of combined expressions.
124#[diagnostic::on_unimplemented(
125    message = "`{Self}` is not a valid nullability marker",
126    label = "expected `NonNull` or `Null`"
127)]
128pub trait Nullability: private::Sealed + Copy + Default + 'static {
129    /// How a decoded value of Rust type `T` is wrapped: `T` for [`NonNull`],
130    /// [`MaybeNull<T>`](crate::row::MaybeNull) for [`Null`].
131    type Decoded<T>;
132
133    /// NULL propagation: nullable when either side is (`a + b`, `f(a, b)`).
134    ///
135    /// | Self | Rhs | Or |
136    /// |------|-----|----|
137    /// | NonNull | NonNull | NonNull |
138    /// | NonNull | Null | Null |
139    /// | Null | _ | Null |
140    type Or<Rhs: Nullability>: Nullability;
141
142    /// NULL absorption: nullable only when both sides are (`COALESCE(a, b)`).
143    ///
144    /// | Self | Rhs | And |
145    /// |------|-----|-----|
146    /// | NonNull | _ | NonNull |
147    /// | Null | NonNull | NonNull |
148    /// | Null | Null | Null |
149    type And<Rhs: Nullability>: Nullability;
150}
151
152/// Nullability marker: the expression is never NULL.
153#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
154pub struct NonNull;
155
156/// Nullability marker: the expression can be NULL.
157#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
158pub struct Null;
159
160impl private::Sealed for NonNull {}
161impl private::Sealed for Null {}
162impl Nullability for NonNull {
163    type Decoded<T> = T;
164    type Or<Rhs: Nullability> = Rhs;
165    type And<Rhs: Nullability> = Self;
166}
167impl Nullability for Null {
168    type Decoded<T> = crate::row::MaybeNull<T>;
169    type Or<Rhs: Nullability> = Self;
170    type And<Rhs: Nullability> = Rhs;
171}
172
173/// Says whether a value of nullability `Source` may be assigned to a column
174/// with nullability `Self`.
175///
176/// Non-null values can be assigned to any column. Nullable values can only be
177/// assigned to nullable columns.
178#[doc(hidden)]
179#[diagnostic::on_unimplemented(
180    message = "a nullable expression cannot be assigned to a non-null column",
181    label = "this assignment could produce NULL",
182    note = "handle the NULL case in the expression or make the target column nullable"
183)]
184pub trait AcceptsNullability<Source: Nullability>: Nullability {}
185
186impl AcceptsNullability<NonNull> for NonNull {}
187impl AcceptsNullability<NonNull> for Null {}
188impl AcceptsNullability<Null> for Null {}
189
190// =============================================================================
191// Aggregate Kind Markers
192// =============================================================================
193
194/// Type-level marker saying whether an expression is an aggregate.
195///
196/// Implemented only by [`Scalar`] and [`Agg`]. Query builders use it to check
197/// that a SELECT list does not mix aggregates and plain columns without a
198/// `GROUP BY`.
199#[diagnostic::on_unimplemented(
200    message = "`{Self}` is not a valid aggregate marker",
201    label = "expected `Scalar` or `Agg`"
202)]
203pub trait AggregateKind: private::Sealed + Copy + Default + 'static {
204    /// Aggregate propagation: an expression over any aggregate is itself
205    /// aggregate (`SUM(x) + 5`).
206    ///
207    /// | Self | Rhs | Or |
208    /// |------|-----|----|
209    /// | Scalar | Scalar | Scalar |
210    /// | Scalar | Agg | Agg |
211    /// | Agg | _ | Agg |
212    type Or<Rhs: AggregateKind>: AggregateKind;
213
214    /// The SELECT-list status of a single expression of this kind
215    /// ([`AllScalar`] or [`AllAgg`]).
216    type Status;
217}
218
219/// Aggregate marker: a plain per-row expression (not an aggregate).
220#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
221pub struct Scalar;
222
223/// Aggregate marker: an aggregate expression such as `COUNT(...)` or `SUM(...)`.
224#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
225pub struct Agg;
226
227impl private::Sealed for Scalar {}
228impl private::Sealed for Agg {}
229impl AggregateKind for Scalar {
230    type Or<Rhs: AggregateKind> = Rhs;
231    type Status = AllScalar;
232}
233impl AggregateKind for Agg {
234    type Or<Rhs: AggregateKind> = Self;
235    type Status = AllAgg;
236}
237
238// =============================================================================
239// Aggregate Status (for SELECT list validation)
240// =============================================================================
241
242/// SELECT-list status: every selected expression is scalar.
243#[derive(Debug, Clone, Copy, Default)]
244pub struct AllScalar;
245
246/// SELECT-list status: every selected expression is an aggregate.
247#[derive(Debug, Clone, Copy, Default)]
248pub struct AllAgg;
249
250/// SELECT-list status: the list mixes scalar and aggregate expressions.
251#[derive(Debug, Clone, Copy, Default)]
252pub struct MixedAgg;
253
254/// Combines the SELECT-list statuses of two expressions.
255///
256/// | Left | Right | Output |
257/// |------|-------|--------|
258/// | AllScalar | AllScalar | AllScalar |
259/// | AllAgg | AllAgg | AllAgg |
260/// | anything else | _ | MixedAgg |
261pub trait CombineAggStatus<Rhs> {
262    /// The combined status.
263    type Output;
264}
265
266impl CombineAggStatus<Self> for AllScalar {
267    type Output = Self;
268}
269impl CombineAggStatus<AllAgg> for AllScalar {
270    type Output = MixedAgg;
271}
272impl CombineAggStatus<MixedAgg> for AllScalar {
273    type Output = MixedAgg;
274}
275impl CombineAggStatus<AllScalar> for AllAgg {
276    type Output = MixedAgg;
277}
278impl CombineAggStatus<Self> for AllAgg {
279    type Output = Self;
280}
281impl CombineAggStatus<MixedAgg> for AllAgg {
282    type Output = MixedAgg;
283}
284impl CombineAggStatus<AllScalar> for MixedAgg {
285    type Output = Self;
286}
287impl CombineAggStatus<AllAgg> for MixedAgg {
288    type Output = Self;
289}
290impl CombineAggStatus<Self> for MixedAgg {
291    type Output = Self;
292}
293
294/// The SELECT-list status of a type that can appear in a SELECT list.
295///
296/// Implemented for columns (always [`AllScalar`]), [`SQLExpr`], and the
297/// expression wrappers in this module.
298pub trait HasAggStatus {
299    /// [`AllScalar`], [`AllAgg`] or [`MixedAgg`].
300    type Status;
301}
302
303impl<T: HasAggStatus + ?Sized> HasAggStatus for &T {
304    type Status = T::Status;
305}
306
307// =============================================================================
308// Core Expression Trait
309// =============================================================================
310
311/// The tables an expression reads, as a type-level tree.
312///
313/// Columns record their table, operators combine their operands' trees, and
314/// literals, placeholders and raw SQL read nothing (`()`). Queries check the
315/// tree against their `FROM`/`JOIN` scope when they are run with `.all()`,
316/// `.get()` or `.rows()`, so using a column of a table that was never joined
317/// is a compile error. See [`crate::scope`] for the node types.
318pub trait ExprSources {
319    /// Type-level tree with one [`Src`](crate::scope::Src) leaf per column read.
320    type Sources;
321}
322
323impl<T: ExprSources + ?Sized> ExprSources for &T {
324    type Sources = T::Sources;
325}
326
327/// Expression lists (`Cons<E, ...>`) read every element's sources.
328impl ExprSources for crate::Nil {
329    type Sources = ();
330}
331
332impl<Head: ExprSources, Tail: ExprSources> ExprSources for crate::Cons<Head, Tail> {
333    type Sources = (Head::Sources, Tail::Sources);
334}
335
336/// `()` reads nothing (`COUNT(*)`).
337impl ExprSources for () {
338    type Sources = ();
339}
340
341// Tuples (condition lists, GROUP BY keys, ORDER BY terms) read the sources of
342// every element, nested as NULL-propagating pairs.
343macro_rules! impl_tuple_expr_sources {
344    ($($T:ident),+; $($i:tt),+) => {
345        impl<$($T: ExprSources),+> ExprSources for ($($T,)+) {
346            type Sources = impl_tuple_expr_sources!(@nest $($T),+);
347        }
348    };
349    (@nest $T:ident) => { <$T as ExprSources>::Sources };
350    (@nest $T:ident, $($rest:ident),+) => {
351        (<$T as ExprSources>::Sources, impl_tuple_expr_sources!(@nest $($rest),+))
352    };
353}
354
355with_col_sizes_8!(impl_tuple_expr_sources);
356
357#[cfg(any(
358    feature = "col16",
359    feature = "col32",
360    feature = "col64",
361    feature = "col128",
362    feature = "col200"
363))]
364with_col_sizes_16!(impl_tuple_expr_sources);
365
366/// A typed SQL expression.
367///
368/// Columns, Rust literals, [`SQLExpr`] values and the results of the functions
369/// in this module implement `Expr`. The associated types describe the result:
370///
371/// - `SQLType`: the SQL data type, such as the dialect's integer or text type;
372/// - `Nullable`: [`NonNull`] or [`Null`];
373/// - `Aggregate`: [`Scalar`] or [`Agg`].
374///
375/// `V` is the dialect's value type (for example `SQLiteValue` or
376/// `PostgresValue`), and `'a` is the lifetime of borrowed values inside the
377/// expression. The table macros implement this trait for generated columns;
378/// you rarely implement it yourself.
379///
380/// # Examples
381///
382/// A helper that accepts any integer expression:
383///
384/// ```rust
385/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
386/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
387/// # #[derive(Clone, Debug)] struct Value(String);
388/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
389/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
390/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
391/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
392/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
393/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
394/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
395/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
396/// fn is_adult<'a, E>(age: E) -> impl Expr<'a, Value>
397/// where
398///     E: Expr<'a, Value, SQLType = Int>,
399/// {
400///     gt(age, 18)
401/// }
402///
403/// assert_eq!(is_adult(users.age).into_expr_sql().sql(), r#""users"."age" > ?"#);
404/// ```
405#[diagnostic::on_unimplemented(
406    message = "`{Self}` is not a valid SQL expression",
407    label = "expected a column, literal, or expression — does this type implement Expr?",
408    note = "SQL expressions must have an associated SQLType, Nullable, and Aggregate kind"
409)]
410pub trait Expr<'a, V: SQLParam>: ToSQL<'a, V> + ExprSources {
411    /// The SQL data type this expression evaluates to.
412    type SQLType: DataType;
413
414    /// Whether this expression can be NULL.
415    type Nullable: Nullability;
416
417    /// Whether this is an aggregate (COUNT, SUM) or scalar expression.
418    type Aggregate: AggregateKind;
419
420    /// Renders this value as a scalar expression, borrowing it.
421    ///
422    /// Most expressions use their `ToSQL` implementation. A few Rust container
423    /// types, notably byte buffers, need expression-specific rendering because
424    /// their generic `ToSQL` form is a comma-separated list.
425    fn to_expr_sql(&self) -> crate::SQL<'a, V> {
426        self.to_sql().parens_if_subquery()
427    }
428
429    /// Renders this value as a scalar expression, consuming it.
430    fn into_expr_sql(self) -> crate::SQL<'a, V>
431    where
432        Self: Sized,
433    {
434        self.into_sql().parens_if_subquery()
435    }
436
437    /// Renders this value as one element of a [`ConditionList`].
438    ///
439    /// `None` means the element contributes no condition and is dropped from
440    /// the combined SQL. Only `Option::None` does this; every other expression
441    /// renders through [`Expr::to_expr_sql`].
442    fn to_condition_sql(&self) -> Option<crate::SQL<'a, V>> {
443        Some(self.to_expr_sql())
444    }
445
446    /// Consuming counterpart of [`Expr::to_condition_sql`].
447    fn into_condition_sql(self) -> Option<crate::SQL<'a, V>>
448    where
449        Self: Sized,
450    {
451        Some(self.into_expr_sql())
452    }
453}
454
455// Note: Columns implement Expr via explicit impls generated by macros,
456// not via a blanket impl, to avoid conflicts with `impl Expr for &T`.