Skip to main content

reinhardt_query/expr/
expr.rs

1//! Expr - The expression builder.
2//!
3//! This module provides [`Expr`], a builder for creating SQL expressions.
4//! It provides a fluent API for building complex expressions.
5
6use super::simple_expr::{CaseStatement, Keyword, SimpleExpr};
7use crate::types::{ColumnRef, DynIden, IntoColumnRef, IntoIden, WindowStatement};
8use crate::value::{IntoValue, Value};
9
10/// Expression builder for creating SQL expressions.
11///
12/// `Expr` provides static methods to create expressions and instance methods
13/// to chain operations on them.
14///
15/// # Example
16///
17/// ```rust,ignore
18/// use reinhardt_query::Expr;
19///
20/// // Simple column reference
21/// let col = Expr::col("name");
22///
23/// // Value expression
24/// let val = Expr::val(42);
25///
26/// // Build complex expressions
27/// let expr = Expr::col("age").gte(18).and(Expr::col("active").eq(true));
28/// ```
29#[derive(Debug, Clone)]
30pub struct Expr(SimpleExpr);
31
32impl Expr {
33	/// Create an expression from a column reference.
34	///
35	/// # Example
36	///
37	/// ```rust
38	/// use reinhardt_query::expr::Expr;
39	///
40	/// let expr = Expr::col("name");
41	/// ```
42	pub fn col<C>(col: C) -> Self
43	where
44		C: IntoColumnRef,
45	{
46		Self(SimpleExpr::Column(col.into_column_ref()))
47	}
48
49	/// Create a table-qualified column expression.
50	///
51	/// # Example
52	///
53	/// ```rust
54	/// use reinhardt_query::expr::Expr;
55	///
56	/// let expr = Expr::tbl("users", "name");
57	/// ```
58	pub fn tbl<T, C>(table: T, col: C) -> Self
59	where
60		T: IntoIden,
61		C: IntoIden,
62	{
63		Self(SimpleExpr::TableColumn(table.into_iden(), col.into_iden()))
64	}
65
66	/// Create a value expression.
67	///
68	/// # Example
69	///
70	/// ```rust
71	/// use reinhardt_query::expr::Expr;
72	///
73	/// let expr = Expr::val(42);
74	/// let expr2 = Expr::val("hello");
75	/// ```
76	pub fn val<V>(val: V) -> Self
77	where
78		V: IntoValue,
79	{
80		Self(SimpleExpr::Value(val.into_value()))
81	}
82
83	/// Create a custom SQL expression.
84	///
85	/// # Security Warning
86	///
87	/// **DO NOT** pass user input directly to this method. This method embeds the
88	/// SQL string directly into the query without parameterization, which can lead
89	/// to SQL injection vulnerabilities.
90	///
91	/// Use [`Expr::cust_with_values()`] for dynamic values instead.
92	///
93	/// # Examples
94	///
95	/// ```rust
96	/// use reinhardt_query::expr::Expr;
97	///
98	/// // ✅ SAFE: Static SQL expression
99	/// let expr = Expr::cust("NOW()");
100	///
101	/// // ❌ UNSAFE: User input
102	/// // let expr = Expr::cust(&user_input);
103	///
104	/// // ✅ SAFE: Parameterized custom expression
105	/// // let expr = Expr::cust_with_values("? + ?", [user_input, other_value]);
106	/// ```
107	pub fn cust<S>(sql: S) -> Self
108	where
109		S: Into<String>,
110	{
111		Self(SimpleExpr::Custom(sql.into()))
112	}
113
114	/// Create a custom SQL expression with value placeholders.
115	///
116	/// Use `?` as placeholders for the values.
117	///
118	/// # Example
119	///
120	/// ```rust
121	/// use reinhardt_query::expr::Expr;
122	///
123	/// let expr = Expr::cust_with_values("? + ?", [1, 2]);
124	/// ```
125	pub fn cust_with_values<S, I, V>(sql: S, values: I) -> Self
126	where
127		S: Into<String>,
128		I: IntoIterator<Item = V>,
129		V: Into<SimpleExpr>,
130	{
131		Self(SimpleExpr::CustomWithExpr(
132			sql.into(),
133			values.into_iter().map(|v| v.into()).collect(),
134		))
135	}
136
137	/// Create a tuple expression.
138	///
139	/// # Example
140	///
141	/// ```rust
142	/// use reinhardt_query::expr::Expr;
143	///
144	/// let expr = Expr::tuple([Expr::val(1), Expr::val(2), Expr::val(3)]);
145	/// ```
146	pub fn tuple<I>(exprs: I) -> Self
147	where
148		I: IntoIterator<Item = Self>,
149	{
150		Self(SimpleExpr::Tuple(
151			exprs.into_iter().map(|e| e.into_simple_expr()).collect(),
152		))
153	}
154
155	/// Create an asterisk expression (`*`).
156	///
157	/// # Example
158	///
159	/// ```rust
160	/// use reinhardt_query::expr::Expr;
161	///
162	/// let expr = Expr::asterisk();
163	/// ```
164	pub fn asterisk() -> Self {
165		Self(SimpleExpr::Asterisk)
166	}
167
168	/// Create a subquery expression.
169	///
170	/// This creates a standalone subquery that can be used in FROM or SELECT clauses.
171	///
172	/// # Example
173	///
174	/// ```rust,ignore
175	/// use reinhardt_query::prelude::*;
176	///
177	/// let subquery = Query::select().column("id").from("users");
178	/// let expr = Expr::subquery(subquery);
179	/// ```
180	pub fn subquery(select: crate::query::SelectStatement) -> Self {
181		Self(SimpleExpr::SubQuery(None, Box::new(select)))
182	}
183
184	/// Create an EXISTS subquery expression.
185	///
186	/// # Example
187	///
188	/// ```rust,ignore
189	/// use reinhardt_query::prelude::*;
190	///
191	/// let subquery = Query::select().column("id").from("orders").and_where(
192	///     Expr::col(("orders", "user_id")).eq(Expr::col(("users", "id")))
193	/// );
194	/// let exists = Expr::exists(subquery);
195	/// ```
196	pub fn exists(select: crate::query::SelectStatement) -> Self {
197		Self(SimpleExpr::SubQuery(
198			Some(super::simple_expr::SubQueryOper::Exists),
199			Box::new(select),
200		))
201	}
202
203	/// Create a NOT EXISTS subquery expression.
204	///
205	/// # Example
206	///
207	/// ```rust,ignore
208	/// use reinhardt_query::prelude::*;
209	///
210	/// let subquery = Query::select().column("id").from("banned_users").and_where(
211	///     Expr::col(("banned_users", "id")).eq(Expr::col(("users", "id")))
212	/// );
213	/// let not_exists = Expr::not_exists(subquery);
214	/// ```
215	pub fn not_exists(select: crate::query::SelectStatement) -> Self {
216		Self(SimpleExpr::SubQuery(
217			Some(super::simple_expr::SubQueryOper::NotExists),
218			Box::new(select),
219		))
220	}
221
222	/// Create an IN subquery expression.
223	///
224	/// # Example
225	///
226	/// ```rust,ignore
227	/// use reinhardt_query::prelude::*;
228	///
229	/// let subquery = Query::select().column("user_id").from("premium_users");
230	/// let in_expr = Expr::col("id").in_subquery(subquery);
231	/// ```
232	pub fn in_subquery(self, select: crate::query::SelectStatement) -> Self {
233		Self(SimpleExpr::Binary(
234			Box::new(self.0),
235			crate::types::BinOper::In,
236			Box::new(SimpleExpr::SubQuery(None, Box::new(select))),
237		))
238	}
239
240	/// Create a NOT IN subquery expression.
241	///
242	/// # Example
243	///
244	/// ```rust,ignore
245	/// use reinhardt_query::prelude::*;
246	///
247	/// let subquery = Query::select().column("user_id").from("banned_users");
248	/// let not_in_expr = Expr::col("id").not_in_subquery(subquery);
249	/// ```
250	pub fn not_in_subquery(self, select: crate::query::SelectStatement) -> Self {
251		Self(SimpleExpr::Binary(
252			Box::new(self.0),
253			crate::types::BinOper::NotIn,
254			Box::new(SimpleExpr::SubQuery(None, Box::new(select))),
255		))
256	}
257
258	/// Create a CASE expression.
259	///
260	/// # Example
261	///
262	/// ```rust
263	/// use reinhardt_query::expr::{Expr, ExprTrait};
264	///
265	/// let case = Expr::case()
266	///     .when(Expr::col("status").eq("active"), "Active")
267	///     .when(Expr::col("status").eq("pending"), "Pending")
268	///     .else_result("Unknown");
269	/// ```
270	pub fn case() -> CaseExprBuilder {
271		CaseExprBuilder {
272			case: CaseStatement::new(),
273		}
274	}
275
276	/// Create a value expression (alias for [`Expr::val`]).
277	pub fn value<V>(val: V) -> Self
278	where
279		V: IntoValue,
280	{
281		Self::val(val)
282	}
283
284	/// Create an expression from a DynIden column.
285	pub fn expr_col(col: DynIden) -> Self {
286		Self(SimpleExpr::Column(ColumnRef::Column(col)))
287	}
288
289	// Constant expressions
290
291	/// Create a NULL constant expression.
292	pub fn null() -> Self {
293		Self(SimpleExpr::Constant(Keyword::Null))
294	}
295
296	/// Create a TRUE constant expression.
297	pub fn constant_true() -> Self {
298		Self(SimpleExpr::Constant(Keyword::True))
299	}
300
301	/// Create a FALSE constant expression.
302	pub fn constant_false() -> Self {
303		Self(SimpleExpr::Constant(Keyword::False))
304	}
305
306	/// Create a DEFAULT constant expression.
307	// Intentional factory method for SQL DEFAULT keyword, not std::default::Default
308	#[allow(clippy::should_implement_trait)]
309	pub fn default() -> Self {
310		Self(SimpleExpr::Constant(Keyword::Default))
311	}
312
313	/// Create a CURRENT_TIMESTAMP expression.
314	pub fn current_timestamp() -> Self {
315		Self(SimpleExpr::Constant(Keyword::CurrentTimestamp))
316	}
317
318	/// Create a CURRENT_DATE expression.
319	pub fn current_date() -> Self {
320		Self(SimpleExpr::Constant(Keyword::CurrentDate))
321	}
322
323	/// Create a CURRENT_TIME expression.
324	pub fn current_time() -> Self {
325		Self(SimpleExpr::Constant(Keyword::CurrentTime))
326	}
327
328	// Window function methods
329
330	/// Apply a window specification to this expression.
331	///
332	/// This creates a window function call with an inline window specification.
333	///
334	/// # Examples
335	///
336	/// ```rust,ignore
337	/// use reinhardt_query::prelude::*;
338	/// use reinhardt_query::types::window::WindowStatement;
339	///
340	/// let window = WindowStatement {
341	///     partition_by: vec![Expr::col("department_id").into_simple_expr()],
342	///     order_by: vec![],
343	///     frame: None,
344	/// };
345	///
346	/// let expr = Expr::row_number().over(window);
347	/// ```
348	#[must_use]
349	pub fn over(self, window: WindowStatement) -> SimpleExpr {
350		SimpleExpr::Window {
351			func: Box::new(self.0),
352			window,
353		}
354	}
355
356	/// Apply a named window to this expression.
357	///
358	/// This creates a window function call that references a named window
359	/// defined in the WINDOW clause.
360	///
361	/// # Examples
362	///
363	/// ```rust,ignore
364	/// use reinhardt_query::prelude::*;
365	///
366	/// let expr = Expr::row_number().over_named("w");
367	/// ```
368	#[must_use]
369	pub fn over_named<T: IntoIden>(self, name: T) -> SimpleExpr {
370		SimpleExpr::WindowNamed {
371			func: Box::new(self.0),
372			name: name.into_iden(),
373		}
374	}
375
376	/// Create a ROW_NUMBER() window function.
377	///
378	/// Returns a sequential number for each row within a partition.
379	///
380	/// # Examples
381	///
382	/// ```rust,ignore
383	/// use reinhardt_query::prelude::*;
384	/// use reinhardt_query::types::window::WindowStatement;
385	///
386	/// let window = WindowStatement {
387	///     partition_by: vec![],
388	///     order_by: vec![],
389	///     frame: None,
390	/// };
391	///
392	/// let expr = Expr::row_number().over(window);
393	/// ```
394	pub fn row_number() -> Self {
395		Self(SimpleExpr::FunctionCall(
396			"ROW_NUMBER".into_iden(),
397			Vec::new(),
398		))
399	}
400
401	/// Create a RANK() window function.
402	///
403	/// Returns the rank of each row within a partition, with gaps in ranking
404	/// for tied values.
405	///
406	/// # Examples
407	///
408	/// ```rust,ignore
409	/// use reinhardt_query::prelude::*;
410	/// use reinhardt_query::types::window::WindowStatement;
411	///
412	/// let window = WindowStatement {
413	///     partition_by: vec![],
414	///     order_by: vec![],
415	///     frame: None,
416	/// };
417	///
418	/// let expr = Expr::rank().over(window);
419	/// ```
420	pub fn rank() -> Self {
421		Self(SimpleExpr::FunctionCall("RANK".into_iden(), Vec::new()))
422	}
423
424	/// Create a DENSE_RANK() window function.
425	///
426	/// Returns the rank of each row within a partition, without gaps in ranking
427	/// for tied values.
428	///
429	/// # Examples
430	///
431	/// ```rust,ignore
432	/// use reinhardt_query::prelude::*;
433	/// use reinhardt_query::types::window::WindowStatement;
434	///
435	/// let window = WindowStatement {
436	///     partition_by: vec![],
437	///     order_by: vec![],
438	///     frame: None,
439	/// };
440	///
441	/// let expr = Expr::dense_rank().over(window);
442	/// ```
443	pub fn dense_rank() -> Self {
444		Self(SimpleExpr::FunctionCall(
445			"DENSE_RANK".into_iden(),
446			Vec::new(),
447		))
448	}
449
450	/// Create an NTILE(n) window function.
451	///
452	/// Divides the rows in a partition into `buckets` number of groups.
453	///
454	/// # Examples
455	///
456	/// ```rust,ignore
457	/// use reinhardt_query::prelude::*;
458	/// use reinhardt_query::types::window::WindowStatement;
459	///
460	/// let window = WindowStatement {
461	///     partition_by: vec![],
462	///     order_by: vec![],
463	///     frame: None,
464	/// };
465	///
466	/// let expr = Expr::ntile(4).over(window); // Divide into quartiles
467	/// ```
468	pub fn ntile(buckets: i64) -> Self {
469		Self(SimpleExpr::FunctionCall(
470			"NTILE".into_iden(),
471			vec![SimpleExpr::Value(Value::BigInt(Some(buckets)))],
472		))
473	}
474
475	/// Create a LEAD() window function.
476	///
477	/// Returns the value of the expression evaluated at the row that is `offset`
478	/// rows after the current row within the partition.
479	///
480	/// # Arguments
481	///
482	/// * `expr` - The expression to evaluate
483	/// * `offset` - Number of rows after the current row (default is 1 if `None`)
484	/// * `default` - Default value if the lead row doesn't exist (default is NULL if `None`)
485	///
486	/// # Examples
487	///
488	/// ```rust,ignore
489	/// use reinhardt_query::prelude::*;
490	/// use reinhardt_query::types::window::WindowStatement;
491	///
492	/// let window = WindowStatement {
493	///     partition_by: vec![],
494	///     order_by: vec![],
495	///     frame: None,
496	/// };
497	///
498	/// // Get next salary value
499	/// let expr = Expr::lead(Expr::col("salary").into_simple_expr(), Some(1), None).over(window);
500	/// ```
501	pub fn lead(expr: SimpleExpr, offset: Option<i64>, default: Option<Value>) -> Self {
502		let mut args = vec![expr];
503		if let Some(off) = offset {
504			args.push(SimpleExpr::Value(Value::BigInt(Some(off))));
505			if let Some(def) = default {
506				args.push(SimpleExpr::Value(def));
507			}
508		}
509		Self(SimpleExpr::FunctionCall("LEAD".into_iden(), args))
510	}
511
512	/// Create a LAG() window function.
513	///
514	/// Returns the value of the expression evaluated at the row that is `offset`
515	/// rows before the current row within the partition.
516	///
517	/// # Arguments
518	///
519	/// * `expr` - The expression to evaluate
520	/// * `offset` - Number of rows before the current row (default is 1 if `None`)
521	/// * `default` - Default value if the lag row doesn't exist (default is NULL if `None`)
522	///
523	/// # Examples
524	///
525	/// ```rust,ignore
526	/// use reinhardt_query::prelude::*;
527	/// use reinhardt_query::types::window::WindowStatement;
528	///
529	/// let window = WindowStatement {
530	///     partition_by: vec![],
531	///     order_by: vec![],
532	///     frame: None,
533	/// };
534	///
535	/// // Get previous salary value
536	/// let expr = Expr::lag(Expr::col("salary").into_simple_expr(), Some(1), None).over(window);
537	/// ```
538	pub fn lag(expr: SimpleExpr, offset: Option<i64>, default: Option<Value>) -> Self {
539		let mut args = vec![expr];
540		if let Some(off) = offset {
541			args.push(SimpleExpr::Value(Value::BigInt(Some(off))));
542			if let Some(def) = default {
543				args.push(SimpleExpr::Value(def));
544			}
545		}
546		Self(SimpleExpr::FunctionCall("LAG".into_iden(), args))
547	}
548
549	/// Create a FIRST_VALUE() window function.
550	///
551	/// Returns the first value in a window frame.
552	///
553	/// # Examples
554	///
555	/// ```rust,ignore
556	/// use reinhardt_query::prelude::*;
557	/// use reinhardt_query::types::window::WindowStatement;
558	///
559	/// let window = WindowStatement {
560	///     partition_by: vec![],
561	///     order_by: vec![],
562	///     frame: None,
563	/// };
564	///
565	/// let expr = Expr::first_value(Expr::col("salary").into_simple_expr()).over(window);
566	/// ```
567	pub fn first_value(expr: SimpleExpr) -> Self {
568		Self(SimpleExpr::FunctionCall(
569			"FIRST_VALUE".into_iden(),
570			vec![expr],
571		))
572	}
573
574	/// Create a LAST_VALUE() window function.
575	///
576	/// Returns the last value in a window frame.
577	///
578	/// # Examples
579	///
580	/// ```rust,ignore
581	/// use reinhardt_query::prelude::*;
582	/// use reinhardt_query::types::window::WindowStatement;
583	///
584	/// let window = WindowStatement {
585	///     partition_by: vec![],
586	///     order_by: vec![],
587	///     frame: None,
588	/// };
589	///
590	/// let expr = Expr::last_value(Expr::col("salary").into_simple_expr()).over(window);
591	/// ```
592	pub fn last_value(expr: SimpleExpr) -> Self {
593		Self(SimpleExpr::FunctionCall(
594			"LAST_VALUE".into_iden(),
595			vec![expr],
596		))
597	}
598
599	/// Create an NTH_VALUE() window function.
600	///
601	/// Returns the value of the expression at the nth row of the window frame.
602	///
603	/// # Arguments
604	///
605	/// * `expr` - The expression to evaluate
606	/// * `n` - The row number (1-based) within the frame
607	///
608	/// # Examples
609	///
610	/// ```rust,ignore
611	/// use reinhardt_query::prelude::*;
612	/// use reinhardt_query::types::window::WindowStatement;
613	///
614	/// let window = WindowStatement {
615	///     partition_by: vec![],
616	///     order_by: vec![],
617	///     frame: None,
618	/// };
619	///
620	/// // Get the 3rd salary value in the frame
621	/// let expr = Expr::nth_value(Expr::col("salary").into_simple_expr(), 3).over(window);
622	/// ```
623	pub fn nth_value(expr: SimpleExpr, n: i64) -> Self {
624		Self(SimpleExpr::FunctionCall(
625			"NTH_VALUE".into_iden(),
626			vec![expr, SimpleExpr::Value(Value::BigInt(Some(n)))],
627		))
628	}
629
630	// Conversion methods
631
632	/// Convert this Expr into a SimpleExpr.
633	#[must_use]
634	pub fn into_simple_expr(self) -> SimpleExpr {
635		self.0
636	}
637
638	/// Get a reference to the underlying SimpleExpr.
639	#[must_use]
640	pub fn as_simple_expr(&self) -> &SimpleExpr {
641		&self.0
642	}
643
644	/// Create a binary operation expression.
645	///
646	/// # Example
647	///
648	/// ```rust,ignore
649	/// use reinhardt_query::prelude::*;
650	///
651	/// let expr = Expr::col("age").binary(BinOper::GreaterThan, Expr::val(18).into_simple_expr());
652	/// ```
653	pub fn binary<R>(self, op: crate::types::BinOper, right: R) -> SimpleExpr
654	where
655		R: Into<SimpleExpr>,
656	{
657		SimpleExpr::Binary(Box::new(self.0), op, Box::new(right.into()))
658	}
659
660	/// Create an equality expression between two columns.
661	///
662	/// This is equivalent to `self.eq(Expr::col(col))`.
663	///
664	/// # Example
665	///
666	/// ```rust,ignore
667	/// use reinhardt_query::prelude::*;
668	///
669	/// let expr = Expr::col(("orders", "user_id")).equals(("users", "id"));
670	/// ```
671	pub fn equals<C>(self, col: C) -> SimpleExpr
672	where
673		C: IntoColumnRef,
674	{
675		SimpleExpr::Binary(
676			Box::new(self.0),
677			crate::types::BinOper::Equal,
678			Box::new(SimpleExpr::Column(col.into_column_ref())),
679		)
680	}
681
682	/// Create an aliased expression (AS).
683	///
684	/// # Example
685	///
686	/// ```rust
687	/// use reinhardt_query::expr::Expr;
688	///
689	/// let expr = Expr::col("name").expr_as("alias_name");
690	/// ```
691	#[must_use]
692	pub fn expr_as<T: IntoIden>(self, alias: T) -> SimpleExpr {
693		SimpleExpr::AsEnum(alias.into_iden(), Box::new(self.0))
694	}
695}
696
697// Allow Expr to be converted into SimpleExpr
698impl From<Expr> for SimpleExpr {
699	fn from(e: Expr) -> Self {
700		e.0
701	}
702}
703
704// Allow creating Expr from SimpleExpr
705impl From<SimpleExpr> for Expr {
706	fn from(e: SimpleExpr) -> Self {
707		Self(e)
708	}
709}
710
711impl From<&str> for Expr {
712	fn from(s: &str) -> Self {
713		Expr::val(s)
714	}
715}
716
717impl crate::value::IntoValue for Expr {
718	fn into_value(self) -> crate::value::Value {
719		match self.0 {
720			SimpleExpr::Value(v) => v,
721			_ => panic!("Cannot convert non-value Expr to Value"),
722		}
723	}
724}
725
726/// Builder for CASE expressions.
727#[derive(Debug, Clone)]
728pub struct CaseExprBuilder {
729	case: CaseStatement,
730}
731
732impl CaseExprBuilder {
733	/// Add a WHEN clause.
734	#[must_use]
735	pub fn when<C, R>(mut self, condition: C, result: R) -> Self
736	where
737		C: Into<SimpleExpr>,
738		R: Into<SimpleExpr>,
739	{
740		self.case = self.case.when(condition, result);
741		self
742	}
743
744	/// Set the ELSE clause and return the built Expr.
745	#[must_use]
746	pub fn else_result<E>(mut self, result: E) -> Expr
747	where
748		E: Into<SimpleExpr>,
749	{
750		self.case = self.case.else_result(result);
751		Expr(SimpleExpr::Case(Box::new(self.case)))
752	}
753
754	/// Build the CASE expression without an ELSE clause.
755	#[must_use]
756	pub fn build(self) -> Expr {
757		Expr(SimpleExpr::Case(Box::new(self.case)))
758	}
759}
760
761#[cfg(test)]
762mod tests {
763	use super::*;
764	use crate::value::Value;
765	use rstest::rstest;
766
767	#[rstest]
768	fn test_expr_col() {
769		let expr = Expr::col("name");
770		assert!(matches!(expr.0, SimpleExpr::Column(_)));
771	}
772
773	#[rstest]
774	fn test_expr_tbl() {
775		let expr = Expr::tbl("users", "name");
776		assert!(matches!(expr.0, SimpleExpr::TableColumn(_, _)));
777	}
778
779	#[rstest]
780	fn test_expr_val() {
781		let expr = Expr::val(42);
782		assert!(matches!(expr.0, SimpleExpr::Value(Value::Int(Some(42)))));
783	}
784
785	#[rstest]
786	fn test_expr_cust() {
787		let expr = Expr::cust("NOW()");
788		if let SimpleExpr::Custom(s) = expr.0 {
789			assert_eq!(s, "NOW()");
790		} else {
791			panic!("Expected Custom variant");
792		}
793	}
794
795	#[rstest]
796	fn test_expr_cust_with_values() {
797		let expr = Expr::cust_with_values("? + ?", [1i32, 2i32]);
798		assert!(matches!(expr.0, SimpleExpr::CustomWithExpr(_, _)));
799	}
800
801	#[rstest]
802	fn test_expr_tuple() {
803		let expr = Expr::tuple([Expr::val(1), Expr::val(2), Expr::val(3)]);
804		if let SimpleExpr::Tuple(v) = expr.0 {
805			assert_eq!(v.len(), 3);
806		} else {
807			panic!("Expected Tuple variant");
808		}
809	}
810
811	#[rstest]
812	fn test_expr_asterisk() {
813		let expr = Expr::asterisk();
814		assert!(matches!(expr.0, SimpleExpr::Asterisk));
815	}
816
817	#[rstest]
818	fn test_expr_null() {
819		let expr = Expr::null();
820		assert!(matches!(expr.0, SimpleExpr::Constant(Keyword::Null)));
821	}
822
823	#[rstest]
824	fn test_expr_current_timestamp() {
825		let expr = Expr::current_timestamp();
826		assert!(matches!(
827			expr.0,
828			SimpleExpr::Constant(Keyword::CurrentTimestamp)
829		));
830	}
831
832	#[rstest]
833	fn test_case_expr_builder() {
834		let expr = Expr::case()
835			.when(true, 1i32)
836			.when(false, 0i32)
837			.else_result(-1i32);
838
839		assert!(matches!(expr.0, SimpleExpr::Case(_)));
840	}
841
842	#[rstest]
843	fn test_case_expr_without_else() {
844		let expr = Expr::case().when(true, 1i32).build();
845
846		assert!(matches!(expr.0, SimpleExpr::Case(_)));
847	}
848
849	#[rstest]
850	fn test_expr_into_simple_expr() {
851		let expr = Expr::val(42);
852		let simple = expr.into_simple_expr();
853		assert!(matches!(simple, SimpleExpr::Value(Value::Int(Some(42)))));
854	}
855}