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}