drizzle_sqlite/builder/
cte.rs

1use crate::values::SQLiteValue;
2use drizzle_core::{SQL, ToSQL, Token};
3use std::marker::PhantomData;
4use std::ops::Deref;
5
6/// Trait for types that can provide a CTE definition for WITH clauses.
7pub trait CTEDefinition<'a> {
8    /// Returns the SQL for the CTE definition (e.g., "cte_name AS (SELECT ...)")
9    fn cte_definition(&self) -> SQL<'a, SQLiteValue<'a>>;
10}
11
12/// A CTE (Common Table Expression) view that wraps an aliased table with its defining query.
13///
14/// This struct enables type-safe CTE usage by combining:
15/// - An aliased table instance for typed field access
16/// - The CTE definition (query) for the WITH clause
17///
18/// # Example
19///
20/// ```rust,ignore
21/// let active_users = builder
22///     .select((user.id, user.name))
23///     .from(user)
24///     .as_cte("active_users");
25///
26/// // active_users.name works via Deref to the aliased table
27/// builder
28///     .with(&active_users)
29///     .select(active_users.name)
30///     .from(&active_users)
31/// ```
32#[derive(Clone, Debug)]
33pub struct CTEView<'a, Table, Query> {
34    /// The aliased table for typed field access
35    pub table: Table,
36    /// The CTE name
37    name: &'static str,
38    /// The defining query
39    query: Query,
40    /// Lifetime marker
41    _phantom: PhantomData<SQLiteValue<'a>>,
42}
43
44impl<'a, Table, Query> CTEView<'a, Table, Query>
45where
46    Query: ToSQL<'a, SQLiteValue<'a>>,
47{
48    /// Creates a new CTEView with the given aliased table, name, and query.
49    pub fn new(table: Table, name: &'static str, query: Query) -> Self {
50        Self {
51            table,
52            name,
53            query,
54            _phantom: PhantomData,
55        }
56    }
57
58    /// Returns the CTE name.
59    pub fn cte_name(&self) -> &'static str {
60        self.name
61    }
62
63    /// Returns a reference to the underlying query.
64    pub fn query(&self) -> &Query {
65        &self.query
66    }
67}
68
69/// CTEDefinition implementation for CTEView.
70impl<'a, Table, Query> CTEDefinition<'a> for CTEView<'a, Table, Query>
71where
72    Query: ToSQL<'a, SQLiteValue<'a>>,
73{
74    fn cte_definition(&self) -> SQL<'a, SQLiteValue<'a>> {
75        SQL::raw(self.name)
76            .push(Token::AS)
77            .append(self.query.to_sql().parens())
78    }
79}
80
81/// CTEDefinition for references.
82impl<'a, Table, Query> CTEDefinition<'a> for &CTEView<'a, Table, Query>
83where
84    Query: ToSQL<'a, SQLiteValue<'a>>,
85{
86    fn cte_definition(&self) -> SQL<'a, SQLiteValue<'a>> {
87        SQL::raw(self.name)
88            .push(Token::AS)
89            .append(self.query.to_sql().parens())
90    }
91}
92
93/// Deref to the aliased table for field access.
94impl<'a, Table, Query> Deref for CTEView<'a, Table, Query> {
95    type Target = Table;
96
97    fn deref(&self) -> &Self::Target {
98        &self.table
99    }
100}
101
102/// ToSQL implementation renders just the CTE name for use in FROM clauses.
103/// Unlike aliased tables (which render as "original" AS "alias"), CTEs
104/// should just render as their name since they're already defined in the WITH clause.
105impl<'a, Table, Query> ToSQL<'a, SQLiteValue<'a>> for CTEView<'a, Table, Query>
106where
107    Query: ToSQL<'a, SQLiteValue<'a>>,
108{
109    fn to_sql(&self) -> SQL<'a, SQLiteValue<'a>> {
110        // Just output the CTE name - it's already defined in the WITH clause
111        SQL::ident(self.name)
112    }
113}