Skip to main content

rust_ef/query/
cte.rs

1//! Common Table Expression (CTE) and SQL set operation specifications.
2//!
3//! `CteSpec` supports both raw-mode (pre-compiled SQL) and typed-mode
4//! (compile-from-`BoolExpr`) CTEs, including recursive CTEs that emit
5//! `WITH RECURSIVE ... UNION ALL SELECT ... JOIN name ON ...`.
6//!
7//! `SetOperator` / `SetOpSpec` represent UNION / INTERSECT / EXCEPT
8//! operands appended after the main SELECT.
9
10use crate::provider::DbValue;
11
12use super::ast::BoolExpr;
13
14/// Specification for a Common Table Expression (CTE).
15///
16/// A CTE is defined by a name and either a pre-compiled SQL string (raw mode)
17/// or a typed WHERE expression compiled at SQL generation time (typed mode).
18/// The main query references the CTE by name (typically in its FROM clause).
19/// Parameters are prepended to the main query's parameter list in CTE
20/// declaration order.
21///
22/// ## Modes
23///
24/// - **Raw mode** (via `with_cte_internal`): `sql` is non-empty, `table` and
25///   `where_expr` are empty. The SQL is emitted verbatim. Placeholders use
26///   the `?` style and are **not** converted to provider-specific syntax —
27///   suitable for SQLite/MySQL but may produce incorrect `$N` on PostgreSQL.
28///
29/// - **Typed mode** (via `with_cte_typed`, used by `linq!(with ...)`): `table`
30///   is non-empty, `where_expr` is `Some(...)`, `sql` is empty. The CTE body
31///   `SELECT * FROM <table> WHERE <expr>` is compiled at `to_sql_with` time
32///   using the provider's placeholder syntax, ensuring correct `$N` numbering
33///   on all providers.
34///
35/// `#[non_exhaustive]` prevents direct struct construction outside the crate
36/// so future field additions don't break downstream code. Use
37/// `with_cte_internal` (raw mode) or `with_cte_typed` (typed mode) to create
38/// CTE specifications.
39#[derive(Debug, Clone)]
40#[non_exhaustive]
41pub struct CteSpec {
42    /// The CTE name (used as the derived table alias in `WITH name AS (...)`).
43    pub name: String,
44    /// Raw mode: the pre-compiled SQL of the CTE body. Empty in typed mode.
45    pub sql: String,
46    /// Typed mode: source table name (`SELECT * FROM <table> WHERE ...`).
47    /// Empty in raw mode.
48    pub table: String,
49    /// Typed mode: WHERE expression compiled at `to_sql_with` time with the
50    /// provider's placeholder syntax. `None` in raw mode.
51    pub where_expr: Option<BoolExpr>,
52    /// Parameter values bound to the CTE's placeholders, in order.
53    /// In typed mode, these are extracted from `where_expr` via
54    /// `collect_bool_expr_values` at construction time.
55    pub params: Vec<DbValue>,
56    /// Optional explicit column list (`WITH name (c1, c2) AS (...)`).
57    /// Empty means no explicit column list.
58    pub columns: Vec<String>,
59    /// Recursive CTE flag. When true, generates
60    /// `WITH RECURSIVE name AS (anchor UNION ALL SELECT t.* FROM table t JOIN name ON t.fk = name.pk)`.
61    pub is_recursive: bool,
62    /// Recursive link columns: `(fk_column, pk_column)`. Only meaningful when
63    /// `is_recursive` is true. The recursive member joins the CTE name to the
64    /// source table via `t.fk = name.pk`.
65    pub recursive_link: Option<(String, String)>,
66}
67
68/// SQL set operators for combining result sets (UNION / INTERSECT / EXCEPT).
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum SetOperator {
71    Union,
72    UnionAll,
73    Intersect,
74    Except,
75}
76
77/// A set operation operand: a pre-compiled SQL string and its bound params.
78///
79/// Per D5, operands should not contain ORDER BY / LIMIT (caller responsibility).
80#[derive(Debug, Clone)]
81pub struct SetOpSpec {
82    pub operator: SetOperator,
83    pub operand_sql: String,
84    pub operand_params: Vec<DbValue>,
85}