drizzle_core/expr/
util.rs

1//! Utility SQL functions (alias, cast, distinct, typeof, concat).
2
3use crate::sql::{SQL, Token};
4use crate::traits::{SQLParam, ToSQL};
5use crate::types::{DataType, Textual};
6
7use super::{Expr, NonNull, Null, NullOr, Nullability, SQLExpr, Scalar};
8
9// =============================================================================
10// ALIAS
11// =============================================================================
12
13/// Create an aliased expression.
14///
15/// # Example
16///
17/// ```ignore
18/// use drizzle_core::expr::alias;
19///
20/// // SELECT users.first_name || users.last_name AS full_name
21/// let full_name = alias(string_concat(users.first_name, users.last_name), "full_name");
22/// ```
23pub fn alias<'a, V, E>(expr: E, name: &'a str) -> SQL<'a, V>
24where
25    V: SQLParam + 'a,
26    E: ToSQL<'a, V>,
27{
28    expr.to_sql().alias(name)
29}
30
31// =============================================================================
32// TYPEOF
33// =============================================================================
34
35/// Get the SQL type of an expression.
36///
37/// Returns the data type name as text.
38///
39/// # Example
40///
41/// ```ignore
42/// use drizzle_core::expr::typeof_;
43///
44/// // SELECT TYPEOF(users.age) -- returns "integer"
45/// let age_type = typeof_(users.age);
46/// ```
47pub fn typeof_<'a, V, E>(expr: E) -> SQLExpr<'a, V, crate::types::Text, NonNull, Scalar>
48where
49    V: SQLParam + 'a,
50    E: ToSQL<'a, V>,
51{
52    SQLExpr::new(SQL::func("TYPEOF", expr.to_sql()))
53}
54
55/// Alias for typeof_ (uses Rust raw identifier syntax).
56pub fn r#typeof<'a, V, E>(expr: E) -> SQLExpr<'a, V, crate::types::Text, NonNull, Scalar>
57where
58    V: SQLParam + 'a,
59    E: ToSQL<'a, V>,
60{
61    typeof_(expr)
62}
63
64// =============================================================================
65// CAST
66// =============================================================================
67
68/// Cast an expression to a different type.
69///
70/// The target type marker specifies the result type for the type system,
71/// while the SQL type string specifies the actual SQL type name (dialect-specific).
72/// Preserves the input expression's nullability and aggregate marker.
73///
74/// # Example
75///
76/// ```ignore
77/// use drizzle_core::expr::cast;
78/// use drizzle_core::types::Text;
79///
80/// // SELECT CAST(users.age AS TEXT)
81/// let age_text = cast::<_, _, _, Text>(users.age, "TEXT");
82///
83/// // PostgreSQL-specific
84/// let age_text = cast::<_, _, _, Text>(users.age, "VARCHAR(255)");
85/// ```
86pub fn cast<'a, V, E, Target>(
87    expr: E,
88    target_type: &'a str,
89) -> SQLExpr<'a, V, Target, E::Nullable, E::Aggregate>
90where
91    V: SQLParam + 'a,
92    E: Expr<'a, V>,
93    Target: DataType,
94{
95    SQLExpr::new(SQL::func(
96        "CAST",
97        expr.to_sql().push(Token::AS).append(SQL::raw(target_type)),
98    ))
99}
100
101// =============================================================================
102// STRING CONCATENATION
103// =============================================================================
104
105/// Concatenate two string expressions using || operator.
106///
107/// Requires both operands to be `Textual` (Text or VarChar).
108/// Nullability follows SQL concatenation rules: nullable input -> nullable output.
109///
110/// # Type Safety
111///
112/// ```ignore
113/// // ✅ OK: Both are Text
114/// string_concat(users.first_name, users.last_name);
115///
116/// // ✅ OK: Text with string literal
117/// string_concat(users.first_name, " ");
118///
119/// // ❌ Compile error: Int is not Textual
120/// string_concat(users.id, users.name);
121/// ```
122///
123/// # Example
124///
125/// ```ignore
126/// use drizzle_core::expr::string_concat;
127///
128/// // SELECT users.first_name || ' ' || users.last_name
129/// let full_name = string_concat(string_concat(users.first_name, " "), users.last_name);
130/// ```
131pub fn string_concat<'a, V, L, R>(
132    left: L,
133    right: R,
134) -> SQLExpr<'a, V, crate::types::Text, <L::Nullable as NullOr<R::Nullable>>::Output, Scalar>
135where
136    V: SQLParam + 'a,
137    L: Expr<'a, V>,
138    R: Expr<'a, V>,
139    L::SQLType: Textual,
140    R::SQLType: Textual,
141    L::Nullable: NullOr<R::Nullable>,
142    R::Nullable: Nullability,
143{
144    super::concat(left, right)
145}
146
147// =============================================================================
148// RAW SQL Expression
149// =============================================================================
150
151/// Create a raw SQL expression with a specified type.
152///
153/// Use this for dialect-specific features or when the type system
154/// can't infer the correct type.
155///
156/// # Safety
157///
158/// This bypasses type checking. Use sparingly and only when necessary.
159///
160/// # Example
161///
162/// ```ignore
163/// use drizzle_core::expr::raw;
164/// use drizzle_core::types::Int;
165///
166/// let expr = raw::<_, Int>("RANDOM()");
167/// ```
168pub fn raw<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar>
169where
170    V: SQLParam + 'a,
171    T: DataType,
172{
173    SQLExpr::new(SQL::raw(sql))
174}
175
176/// Create a raw SQL expression with explicit nullability.
177pub fn raw_non_null<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, NonNull, Scalar>
178where
179    V: SQLParam + 'a,
180    T: DataType,
181{
182    SQLExpr::new(SQL::raw(sql))
183}