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`.