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}