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